private void TestBinaryIntrinsicSymbol( BinaryOperatorKind op, TypeSymbol leftType, TypeSymbol rightType, CSharpCompilation compilation, SemanticModel semanticModel, ExpressionSyntax node1, ExpressionSyntax node2, ExpressionSyntax node3, ExpressionSyntax node4, ExpressionSyntax node5, ExpressionSyntax node6, ExpressionSyntax node7, ExpressionSyntax node8 ) { SymbolInfo info1 = semanticModel.GetSymbolInfo(node1); HashSet<DiagnosticInfo> useSiteDiagnostics = null; if (info1.Symbol == null) { if (info1.CandidateSymbols.Length == 0) { if (leftType.IsDynamic() || rightType.IsDynamic()) { Assert.True(CandidateReason.LateBound == info1.CandidateReason || CandidateReason.None == info1.CandidateReason); } else { Assert.Equal(CandidateReason.None, info1.CandidateReason); } } else { Assert.Equal(CandidateReason.OverloadResolutionFailure, info1.CandidateReason); foreach (MethodSymbol s in info1.CandidateSymbols) { Assert.Equal(MethodKind.UserDefinedOperator, s.MethodKind); } } } else { Assert.Equal(leftType.IsDynamic() || rightType.IsDynamic() ? CandidateReason.LateBound : CandidateReason.None, info1.CandidateReason); Assert.Equal(0, info1.CandidateSymbols.Length); } var symbol1 = (MethodSymbol)info1.Symbol; var symbol2 = semanticModel.GetSymbolInfo(node2).Symbol; var symbol3 = semanticModel.GetSymbolInfo(node3).Symbol; var symbol4 = semanticModel.GetSymbolInfo(node4).Symbol; var symbol5 = (MethodSymbol)semanticModel.GetSymbolInfo(node5).Symbol; var symbol6 = semanticModel.GetSymbolInfo(node6).Symbol; var symbol7 = semanticModel.GetSymbolInfo(node7).Symbol; var symbol8 = semanticModel.GetSymbolInfo(node8).Symbol; Assert.Equal(symbol1, symbol5); Assert.Equal(symbol2, symbol6); Assert.Equal(symbol3, symbol7); Assert.Equal(symbol4, symbol8); if ((object)symbol1 != null && symbol1.IsImplicitlyDeclared) { Assert.NotSame(symbol1, symbol5); Assert.Equal(symbol1.GetHashCode(), symbol5.GetHashCode()); for (int i = 0; i < 2; i++) { Assert.Equal(symbol1.Parameters[i], symbol5.Parameters[i]); Assert.Equal(symbol1.Parameters[i].GetHashCode(), symbol5.Parameters[i].GetHashCode()); } Assert.NotEqual(symbol1.Parameters[0], symbol5.Parameters[1]); } switch (op) { case BinaryOperatorKind.LogicalAnd: case BinaryOperatorKind.LogicalOr: Assert.Null(symbol1); Assert.Null(symbol2); Assert.Null(symbol3); Assert.Null(symbol4); return; } BinaryOperatorKind result = OverloadResolution.BinopEasyOut.OpKind(op, leftType, rightType); BinaryOperatorSignature signature; bool isDynamic = (leftType.IsDynamic() || rightType.IsDynamic()); if (result == BinaryOperatorKind.Error) { if (leftType.IsDynamic() && !rightType.IsPointerType() && !rightType.IsRestrictedType()) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Dynamic, leftType, rightType, leftType); } else if (rightType.IsDynamic() && !leftType.IsPointerType() && !leftType.IsRestrictedType()) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Dynamic, leftType, rightType, rightType); } else if ((op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual) && leftType.IsReferenceType && rightType.IsReferenceType && (leftType == rightType || compilation.Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics).IsReference)) { if (leftType.IsDelegateType() && rightType.IsDelegateType()) { Assert.Equal(leftType, rightType); signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Delegate, leftType, // TODO: this feels like a spec violation leftType, // TODO: this feels like a spec violation compilation.GetSpecialType(SpecialType.System_Boolean)); } else if (leftType.SpecialType == SpecialType.System_Delegate && rightType.SpecialType == SpecialType.System_Delegate) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Delegate, compilation.GetSpecialType(SpecialType.System_Delegate), compilation.GetSpecialType(SpecialType.System_Delegate), compilation.GetSpecialType(SpecialType.System_Boolean)); } else { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Object, compilation.ObjectType, compilation.ObjectType, compilation.GetSpecialType(SpecialType.System_Boolean)); } } else if (op == BinaryOperatorKind.Addition && ((leftType.IsStringType() && !rightType.IsPointerType()) || (!leftType.IsPointerType() && rightType.IsStringType()))) { Assert.False(leftType.IsStringType() && rightType.IsStringType()); if (leftType.IsStringType()) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.String, leftType, compilation.ObjectType, leftType); } else { Assert.True(rightType.IsStringType()); signature = new BinaryOperatorSignature(op | BinaryOperatorKind.String, compilation.ObjectType, rightType, rightType); } } else if (op == BinaryOperatorKind.Addition && (((leftType.IsIntegralType() || leftType.IsCharType()) && rightType.IsPointerType()) || (leftType.IsPointerType() && (rightType.IsIntegralType() || rightType.IsCharType())))) { if (leftType.IsPointerType()) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Pointer, leftType, symbol1.Parameters[1].Type, leftType); Assert.True(symbol1.Parameters[1].Type.IsIntegralType()); } else { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Pointer, symbol1.Parameters[0].Type, rightType, rightType); Assert.True(symbol1.Parameters[0].Type.IsIntegralType()); } } else if (op == BinaryOperatorKind.Subtraction && (leftType.IsPointerType() && (rightType.IsIntegralType() || rightType.IsCharType()))) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.String, leftType, symbol1.Parameters[1].Type, leftType); Assert.True(symbol1.Parameters[1].Type.IsIntegralType()); } else if (op == BinaryOperatorKind.Subtraction && leftType.IsPointerType() && leftType == rightType) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Pointer, leftType, rightType, compilation.GetSpecialType(SpecialType.System_Int64)); } else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) && leftType.IsEnumType() && (rightType.IsIntegralType() || rightType.IsCharType()) && (result = OverloadResolution.BinopEasyOut.OpKind(op, leftType.EnumUnderlyingType(), rightType)) != BinaryOperatorKind.Error && (signature = compilation.builtInOperators.GetSignature(result)).RightType == leftType.EnumUnderlyingType()) { signature = new BinaryOperatorSignature(signature.Kind | BinaryOperatorKind.EnumAndUnderlying, leftType, signature.RightType, leftType); } else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) && rightType.IsEnumType() && (leftType.IsIntegralType() || leftType.IsCharType()) && (result = OverloadResolution.BinopEasyOut.OpKind(op, leftType, rightType.EnumUnderlyingType())) != BinaryOperatorKind.Error && (signature = compilation.builtInOperators.GetSignature(result)).LeftType == rightType.EnumUnderlyingType()) { signature = new BinaryOperatorSignature(signature.Kind | BinaryOperatorKind.EnumAndUnderlying, signature.LeftType, rightType, rightType); } else if (op == BinaryOperatorKind.Subtraction && leftType.IsEnumType() && leftType == rightType) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Enum, leftType, rightType, leftType.EnumUnderlyingType()); } else if ((op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual || op == BinaryOperatorKind.LessThan || op == BinaryOperatorKind.LessThanOrEqual || op == BinaryOperatorKind.GreaterThan || op == BinaryOperatorKind.GreaterThanOrEqual) && leftType.IsEnumType() && leftType == rightType) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Enum, leftType, rightType, compilation.GetSpecialType(SpecialType.System_Boolean)); } else if ((op == BinaryOperatorKind.Xor || op == BinaryOperatorKind.And || op == BinaryOperatorKind.Or) && leftType.IsEnumType() && leftType == rightType) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Enum, leftType, rightType, leftType); } else if ((op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) && leftType.IsDelegateType() && leftType == rightType) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Delegate, leftType, leftType, leftType); } else if ((op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual || op == BinaryOperatorKind.LessThan || op == BinaryOperatorKind.LessThanOrEqual || op == BinaryOperatorKind.GreaterThan || op == BinaryOperatorKind.GreaterThanOrEqual) && leftType.IsPointerType() && rightType.IsPointerType()) { signature = new BinaryOperatorSignature(op | BinaryOperatorKind.Pointer, compilation.CreatePointerTypeSymbol(compilation.GetSpecialType(SpecialType.System_Void)), compilation.CreatePointerTypeSymbol(compilation.GetSpecialType(SpecialType.System_Void)), compilation.GetSpecialType(SpecialType.System_Boolean)); } else { if ((object)symbol1 != null) { Assert.False(symbol1.IsImplicitlyDeclared); Assert.Equal(MethodKind.UserDefinedOperator, symbol1.MethodKind); if (leftType.IsValueType && !leftType.IsPointerType()) { if (rightType.IsValueType && !rightType.IsPointerType()) { Assert.Same(symbol1, symbol2); Assert.Same(symbol1, symbol3); Assert.Same(symbol1, symbol4); return; } else { Assert.Null(symbol2); Assert.Same(symbol1, symbol3); Assert.Null(symbol4); return; } } else if (rightType.IsValueType && !rightType.IsPointerType()) { Assert.Null(symbol2); Assert.Null(symbol3); Assert.Same(symbol1, symbol4); return; } else { Assert.Null(symbol2); Assert.Null(symbol3); Assert.Null(symbol4); return; } } Assert.Null(symbol1); Assert.Null(symbol2); if (!rightType.IsDynamic()) { Assert.Null(symbol3); } if (!leftType.IsDynamic()) { Assert.Null(symbol4); } return; } } else if ((op == BinaryOperatorKind.Equal || op == BinaryOperatorKind.NotEqual) && leftType != rightType && (!leftType.IsValueType || !rightType.IsValueType || leftType.SpecialType == SpecialType.System_Boolean || rightType.SpecialType == SpecialType.System_Boolean || (leftType.SpecialType == SpecialType.System_Decimal && (rightType.SpecialType == SpecialType.System_Double || rightType.SpecialType == SpecialType.System_Single)) || (rightType.SpecialType == SpecialType.System_Decimal && (leftType.SpecialType == SpecialType.System_Double || leftType.SpecialType == SpecialType.System_Single))) && (!leftType.IsReferenceType || !rightType.IsReferenceType || !compilation.Conversions.ClassifyConversion(leftType, rightType, ref useSiteDiagnostics).IsReference)) { Assert.Null(symbol1); Assert.Null(symbol2); Assert.Null(symbol3); Assert.Null(symbol4); return; } else { signature = compilation.builtInOperators.GetSignature(result); } Assert.NotNull(symbol1); string containerName = signature.LeftType.ToTestDisplayString(); string leftName = containerName; string rightName = signature.RightType.ToTestDisplayString(); string returnName = signature.ReturnType.ToTestDisplayString(); if (isDynamic) { containerName = compilation.DynamicType.ToTestDisplayString(); } else if (op == BinaryOperatorKind.Addition || op == BinaryOperatorKind.Subtraction) { if (signature.LeftType.IsObjectType() && signature.RightType.IsStringType()) { containerName = rightName; } else if ((leftType.IsEnumType() || leftType.IsPointerType()) && (rightType.IsIntegralType() || rightType.IsCharType())) { containerName = leftType.ToTestDisplayString(); leftName = containerName; returnName = containerName; } else if ((rightType.IsEnumType() || rightType.IsPointerType()) && (leftType.IsIntegralType() || leftType.IsCharType())) { containerName = rightType.ToTestDisplayString(); rightName = containerName; returnName = containerName; } } Assert.Equal(isDynamic, signature.ReturnType.IsDynamic()); string expectedSymbol = String.Format("{4} {0}.{2}({1} left, {3} right)", containerName, leftName, OperatorFacts.BinaryOperatorNameFromOperatorKind(op), rightName, returnName); string actualSymbol = symbol1.ToTestDisplayString(); Assert.Equal(expectedSymbol, actualSymbol); Assert.Equal(MethodKind.BuiltinOperator, symbol1.MethodKind); Assert.True(symbol1.IsImplicitlyDeclared); bool isChecked; switch (op) { case BinaryOperatorKind.Multiplication: case BinaryOperatorKind.Addition: case BinaryOperatorKind.Subtraction: case BinaryOperatorKind.Division: isChecked = isDynamic || symbol1.ContainingSymbol.Kind == SymbolKind.PointerType || symbol1.ContainingType.EnumUnderlyingType().SpecialType.IsIntegralType(); break; default: isChecked = isDynamic; break; } Assert.Equal(isChecked, symbol1.IsCheckedBuiltin); Assert.False(symbol1.IsGenericMethod); Assert.False(symbol1.IsExtensionMethod); Assert.False(symbol1.IsExtern); Assert.False(symbol1.CanBeReferencedByName); Assert.Null(symbol1.DeclaringCompilation); Assert.Equal(symbol1.Name, symbol1.MetadataName); Assert.True(symbol1.ContainingSymbol == symbol1.Parameters[0].Type || symbol1.ContainingSymbol == symbol1.Parameters[1].Type); int match = 0; if (symbol1.ContainingSymbol == symbol1.ReturnType) { match++; } if (symbol1.ContainingSymbol == symbol1.Parameters[0].Type) { match++; } if (symbol1.ContainingSymbol == symbol1.Parameters[1].Type) { match++; } Assert.True(match >= 2); Assert.Equal(0, symbol1.Locations.Length); Assert.Null(symbol1.GetDocumentationCommentId()); Assert.Equal("", symbol1.GetDocumentationCommentXml()); Assert.True(symbol1.HasSpecialName); Assert.True(symbol1.IsStatic); Assert.Equal(Accessibility.Public, symbol1.DeclaredAccessibility); Assert.False(symbol1.HidesBaseMethodsByName); Assert.False(symbol1.IsOverride); Assert.False(symbol1.IsVirtual); Assert.False(symbol1.IsAbstract); Assert.False(symbol1.IsSealed); Assert.Equal(2, symbol1.ParameterCount); Assert.Equal(0, symbol1.Parameters[0].Ordinal); Assert.Equal(1, symbol1.Parameters[1].Ordinal); var otherSymbol = (MethodSymbol)semanticModel.GetSymbolInfo(node1).Symbol; Assert.Equal(symbol1, otherSymbol); if (leftType.IsValueType && !leftType.IsPointerType()) { if (rightType.IsValueType && !rightType.IsPointerType()) { Assert.Equal(symbol1, symbol2); Assert.Equal(symbol1, symbol3); Assert.Equal(symbol1, symbol4); return; } else { Assert.Null(symbol2); if (rightType.IsDynamic()) { Assert.NotEqual(symbol1, symbol3); } else { Assert.Equal(rightType.IsPointerType() ? null : symbol1, symbol3); } Assert.Null(symbol4); return; } } else if (rightType.IsValueType && !rightType.IsPointerType()) { Assert.Null(symbol2); Assert.Null(symbol3); if (leftType.IsDynamic()) { Assert.NotEqual(symbol1, symbol4); } else { Assert.Equal(leftType.IsPointerType() ? null : symbol1, symbol4); } return; } Assert.Null(symbol2); if (rightType.IsDynamic()) { Assert.NotEqual(symbol1, symbol3); } else { Assert.Null(symbol3); } if (leftType.IsDynamic()) { Assert.NotEqual(symbol1, symbol4); } else { Assert.Null(symbol4); } }
/// <summary> /// Calculates "cost" of conversion. /// </summary> static int ConvCost(CommonConversion conv, TypeSymbol from, TypeSymbol to) { if (conv.Exists) { if (conv.IsIdentity) { return(0); } // calculate magically the conversion cost // TODO: unify with ConversionCost int cost; if (conv.IsReference) { cost = 1; } else if (conv.IsNumeric) { cost = 1; var clfrom = ClassifyNumericType(from); var clto = ClassifyNumericType(to); if (clfrom.size == 1) // bool { cost += 128; // bool -> {0|1} // only if there is nothing better!!! } // cost += Math.Abs(clto.size / 16 - clfrom.size / 16) + (clfrom.floating != clto.floating ? 1 : 0); } else if (conv.IsUserDefined) // preferring user defined conversions { // do not treat all the implicit conversios the same // PhpString <-> string if ((to.IsStringType() || from.IsStringType()) && (to.Is_PhpString() || from.Is_PhpString())) { cost = 2; } else { cost = 4; } } else { cost = 8; } // if (conv.IsImplicit == false) { cost *= 2; } // return(cost); } else { throw new ArgumentException(); } }
protected override Conversion ClassifyVOImplicitBuiltInConversionFromExpression(BoundExpression sourceExpression, TypeSymbol source, TypeSymbol destination, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // Parameters checks have been done in the calling code var srcType = source.SpecialType; var dstType = destination.SpecialType; bool voCast = false; bool voConvert = false; bool typeCast = false; bool vo7 = Compilation.Options.HasOption(CompilerOption.ImplicitCastsAndConversions, sourceExpression.Syntax); if (sourceExpression.Syntax != null) { var xNode = sourceExpression.Syntax.XNode; while (xNode != null) { voCast = xNode is XP.VoCastExpressionContext; if (voCast) { break; } voConvert = xNode is XP.VoConversionExpressionContext; if (voConvert) { break; } typeCast = xNode is XP.TypeCastContext; if (typeCast) { break; } xNode = xNode.Parent as IXParseTree; if (xNode is XP.StatementContext) { break; } } } if (vo7 && (typeCast || voCast || voConvert)) { // Allow cast -> BOOLEAN if (dstType == SpecialType.System_Boolean && srcType.IsIntegralType()) { if (sourceExpression is BoundExpression be && be.Type.SpecialType == SpecialType.System_Boolean) { return(Conversion.Identity); } } } // TYPE(_CAST, expr) allows almost everything // source must be PTR, Integral, IntPtr, UIntPtr // this is handled in CanVOCast if (voCast && source.CanVOCast() && destination.CanVOCast()) { // No _CAST on USUAL if (source == Compilation.UsualType()) { return(Conversion.NoConversion); } // Allow LOGIC(_CAST if (dstType == SpecialType.System_Boolean) { return(Conversion.Identity); } // Allow cast -> INTEGRAL // except from NullableTypes and Reference Types if (dstType.IsIntegralType() && !source.IsNullableType() && !source.IsReferenceType) { if (srcType.IsNumericType()) { // always implicit numeric conversion return(Conversion.ImplicitNumeric); } if (source.SpecialType == SpecialType.System_Boolean) { return(Conversion.Identity); } // Allow PTR -> Integral when size matches if (source.IsVoidPointer()) { if (dstType.SizeInBytes() == 4 && Compilation.Options.Platform == Platform.X86) { return(Conversion.Identity); } if (dstType.SizeInBytes() == 8 && Compilation.Options.Platform == Platform.X64) { return(Conversion.Identity); } } } // Allow cast -> PTR when // source is integral and source size matches the Integral size // source is Ptr, IntPtr, UintPtr // source is PSZ if (destination is PointerTypeSymbol) { if (source.IsIntegralType()) { if (Compilation.Options.Platform == Platform.X86 && srcType.SizeInBytes() == 4) { return(Conversion.Identity); } if (Compilation.Options.Platform == Platform.X64 && srcType.SizeInBytes() == 8) { return(Conversion.Identity); } return(Conversion.IntegerToPointer); } if (source.IsPointerType() || source.IsVoidPointer() || source.SpecialType == SpecialType.System_IntPtr || source.SpecialType == SpecialType.System_UIntPtr) { return(Conversion.Identity); } if (source == Compilation.PszType()) { return(Conversion.Identity); } } // Allow cast -> PSZ if (destination == Compilation.PszType()) { return(Conversion.Identity); } } if (voConvert) { // we need to convert BYTE(<p>) to dereferencing the <p> // This is done else where in Binder.BindVulcanPointerDereference() // Integer conversions if (srcType.IsNumericType() && dstType.IsNumericType() && srcType.IsIntegralType() == dstType.IsIntegralType()) { // always implicit numeric conversion return(Conversion.ImplicitNumeric); } } if (source == Compilation.UsualType()) { // Usual -> Decimal. Get the object out of the Usual and let the rest be done by Roslyn if (destination == Compilation.UsualType()) { return(Conversion.NoConversion); } if (dstType == SpecialType.System_Decimal) { return(Conversion.Boxing); } // Usual -> OBJECT. Get the object out of the Usual // Our special call will call in LocalWriter.UnBoxVOType will // convert the Unbox operation to a call to __Usual.ToObject() // This method will return the Contents of the usual as an object // and not the usual itself as an object else if (dstType == SpecialType.System_Object) { // All Objects are boxed in a usual return(Conversion.Boxing); } else if (destination.IsReferenceType && !IsClipperArgsType(destination) && !destination.IsStringType() && !destination.IsIFormatProvider()) { // all user reference types are boxed. But not the Usual[] args and not string return(Conversion.Boxing); } else if (destination.IsPointerType()) { // Not really boxed, but we handle this in LocalRewriter.UnBoxXSharpType return(Conversion.Boxing); } } if (Compilation.Options.LateBindingOrFox(sourceExpression.Syntax) || vo7) // lb or vo7 { if (srcType == SpecialType.System_Object) { if (destination.IsReferenceType && !IsClipperArgsType(destination)) { // Convert Object -> Reference allowed with /lb and with /vo7 // except when converting to array of usuals return(Conversion.ImplicitReference); } if (destination.IsPointerType() || destination.SpecialType == SpecialType.System_IntPtr) { return(Conversion.Identity); } } if (dstType == SpecialType.System_Object) { if (source.IsReferenceType) { return(Conversion.ImplicitReference); } if (source.IsPointerType() || source.SpecialType == SpecialType.System_IntPtr) { return(Conversion.Identity); } } } if (vo7) { // Convert Any Ptr -> Any Ptr if (source.IsPointerType() && destination.IsPointerType()) { return(Conversion.Identity); } } // Convert Integral type -> Ptr Type if (source.IsIntegralType() && destination.IsPointerType()) { if (Compilation.Options.Platform == Platform.X86 && srcType.SizeInBytes() <= 4) { return(Conversion.Identity); } if (Compilation.Options.Platform == Platform.X64 && srcType.SizeInBytes() <= 8) { return(Conversion.Identity); } return(Conversion.IntegerToPointer); } // When unsafe we always allow to cast void * to typed * // Is this OK ? // See ticket C425 if (source.IsVoidPointer() && destination.IsPointerType() && Compilation.Options.AllowUnsafe) { return(Conversion.Identity); } if (srcType.IsIntegralType() && dstType.IsIntegralType()) { if (srcType.SizeInBytes() < dstType.SizeInBytes() || sourceExpression is BoundConditionalOperator) // IIF expressions with literals are always seen as Int, even when the values are asmall { return(Conversion.ImplicitNumeric); } } if (destination == Compilation.PszType() || destination.IsVoidPointer()) { if (source.SpecialType == SpecialType.System_String) { return(Conversion.ImplicitReference); } } // when nothing else, then use the Core rules return(ClassifyCoreImplicitConversionFromExpression(sourceExpression, source, destination, ref useSiteDiagnostics)); }