public SynthesizedIntrinsicOperatorSymbol(TypeSymbol leftType, string name, TypeSymbol rightType, TypeSymbol returnType, bool isCheckedBuiltin) { if (leftType.Equals(rightType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds)) { _containingType = leftType; } else if (rightType.Equals(returnType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds)) { _containingType = rightType; } else { Debug.Assert(leftType.Equals(returnType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds)); _containingType = leftType; } _name = name; _returnType = returnType; Debug.Assert((leftType.IsDynamic() || rightType.IsDynamic()) == returnType.IsDynamic()); Debug.Assert(_containingType.IsDynamic() == returnType.IsDynamic()); _parameters = (new ParameterSymbol[] {new SynthesizedOperatorParameterSymbol(this, leftType, 0, "left"), new SynthesizedOperatorParameterSymbol(this, rightType, 1, "right")}).AsImmutableOrNull(); _isCheckedBuiltin = isCheckedBuiltin; }
public SynthesizedIntrinsicOperatorSymbol(TypeSymbol leftType, string name, TypeSymbol rightType, TypeSymbol returnType, bool isCheckedBuiltin) { if (leftType.Equals(rightType, ignoreCustomModifiers: true)) { this.containingType = leftType; } else if (rightType.Equals(returnType, ignoreCustomModifiers: true)) { this.containingType = rightType; } else { Debug.Assert(leftType.Equals(returnType, ignoreCustomModifiers: true)); this.containingType = leftType; } this.name = name; this.returnType = returnType; Debug.Assert((leftType.IsDynamic() || rightType.IsDynamic()) == returnType.IsDynamic()); Debug.Assert(containingType.IsDynamic() == returnType.IsDynamic()); this.parameters = (new ParameterSymbol[] {new SynthesizedOperatorParameterSymbol(this, leftType, 0, "left"), new SynthesizedOperatorParameterSymbol(this, rightType, 1, "right")}).AsImmutableOrNull(); this.isCheckedBuiltin = isCheckedBuiltin; }
private static Symbol GetIntrinsicOperatorSymbol(BinaryOperatorKind op, bool isDynamic, TypeSymbol leftType, TypeSymbol rightType, TypeSymbol returnType, bool isChecked) { if (!isDynamic) { leftType = leftType.StrippedType(); rightType = rightType.StrippedType(); returnType = returnType.StrippedType(); } else { Debug.Assert(returnType.IsDynamic()); if ((object)leftType == null) { Debug.Assert(rightType.IsDynamic()); leftType = rightType; } else if ((object)rightType == null) { Debug.Assert(leftType.IsDynamic()); rightType = leftType; } } return new SynthesizedIntrinsicOperatorSymbol(leftType, OperatorFacts.BinaryOperatorNameFromOperatorKind(op), rightType, returnType, isChecked); }
private TypeSymbol TransformType(TypeSymbol type) { Debug.Assert(_index >= 0); if (!HasFlag || PeekFlag() && (type.SpecialType != SpecialType.System_Object && !type.IsDynamic())) { // Bail, since flags are invalid. return null; } switch (type.Kind) { case SymbolKind.ErrorType: case SymbolKind.NamedType: if (type.SpecialType == SpecialType.System_Object) { // Replace the given System.Object type with dynamic type if the corresponding dynamicTransformFlag is set to true. return ConsumeFlag() ? DynamicTypeSymbol.Instance : type; } return TransformNamedType((NamedTypeSymbol)type); case SymbolKind.ArrayType: return TransformArrayType((ArrayTypeSymbol)type); case SymbolKind.PointerType: return TransformPointerType((PointerTypeSymbol)type); case SymbolKind.DynamicType: Debug.Assert(!_haveCustomModifierFlags, "This shouldn't happen during decoding."); return ConsumeFlag() ? type : _containingAssembly.GetSpecialType(SpecialType.System_Object); default: ConsumeFlag(); return HandleCustomModifiers(type.CustomModifierCount()) ? type : null; } }
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); } }
private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // Anything is better than an error sym. if (type1.IsErrorType()) { return type2; } if ((object)type2 == null || type2.IsErrorType()) { return type1; } var t1tot2 = _conversions.ClassifyImplicitConversion(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = _conversions.ClassifyImplicitConversion(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.IsDynamic()) { return type1; } if (type2.IsDynamic()) { return type2; } return null; } if (t1tot2) { return type2; } if (t2tot1) { return type1; } return null; }
/// <summary> /// Return the side-effect expression corresponding to an evaluation. /// </summary> protected BoundExpression LowerEvaluation(BoundDagEvaluation evaluation) { BoundExpression input = _tempAllocator.GetTemp(evaluation.Input); switch (evaluation) { case BoundDagFieldEvaluation f: { FieldSymbol field = f.Field; var outputTemp = new BoundDagTemp(f.Syntax, field.Type, f, index: 0); BoundExpression output = _tempAllocator.GetTemp(outputTemp); BoundExpression access = _localRewriter.MakeFieldAccess(f.Syntax, input, field, null, LookupResultKind.Viable, field.Type); access.WasCompilerGenerated = true; return(_factory.AssignmentExpression(output, access)); } case BoundDagPropertyEvaluation p: { PropertySymbol property = p.Property; var outputTemp = new BoundDagTemp(p.Syntax, property.Type, p, index: 0); BoundExpression output = _tempAllocator.GetTemp(outputTemp); return(_factory.AssignmentExpression(output, _factory.Property(input, property))); } case BoundDagDeconstructEvaluation d: { MethodSymbol method = d.DeconstructMethod; var refKindBuilder = ArrayBuilder <RefKind> .GetInstance(); var argBuilder = ArrayBuilder <BoundExpression> .GetInstance(); BoundExpression receiver; void addArg(RefKind refKind, BoundExpression expression) { refKindBuilder.Add(refKind); argBuilder.Add(expression); } Debug.Assert(method.Name == WellKnownMemberNames.DeconstructMethodName); int extensionExtra; if (method.IsStatic) { Debug.Assert(method.IsExtensionMethod); receiver = _factory.Type(method.ContainingType); addArg(method.ParameterRefKinds[0], input); extensionExtra = 1; } else { receiver = input; extensionExtra = 0; } for (int i = extensionExtra; i < method.ParameterCount; i++) { ParameterSymbol parameter = method.Parameters[i]; Debug.Assert(parameter.RefKind == RefKind.Out); var outputTemp = new BoundDagTemp(d.Syntax, parameter.Type, d, i - extensionExtra); addArg(RefKind.Out, _tempAllocator.GetTemp(outputTemp)); } return(_factory.Call(receiver, method, refKindBuilder.ToImmutableAndFree(), argBuilder.ToImmutableAndFree())); } case BoundDagTypeEvaluation t: { TypeSymbol inputType = input.Type; if (inputType.IsDynamic() || inputType.ContainsTypeParameter()) { inputType = _factory.SpecialType(SpecialType.System_Object); } TypeSymbol type = t.Type; var outputTemp = new BoundDagTemp(t.Syntax, type, t, index: 0); BoundExpression output = _tempAllocator.GetTemp(outputTemp); HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = _factory.Compilation.Conversions.ClassifyBuiltInConversion(inputType, output.Type, ref useSiteDiagnostics); _localRewriter._diagnostics.Add(t.Syntax, useSiteDiagnostics); BoundExpression evaluated; if (conversion.Exists) { if (conversion.Kind == ConversionKind.ExplicitNullable && inputType.GetNullableUnderlyingType().Equals(output.Type, TypeCompareKind.AllIgnoreOptions) && _localRewriter.TryGetNullableMethod(t.Syntax, inputType, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault)) { // As a special case, since the null test has already been done we can use Nullable<T>.GetValueOrDefault evaluated = _factory.Call(input, getValueOrDefault); } else { evaluated = _factory.Convert(type, input, conversion); } } else { evaluated = _factory.As(input, type); } return(_factory.AssignmentExpression(output, evaluated)); } case BoundDagIndexEvaluation e: { // This is an evaluation of an indexed property with a constant int value. // The input type must be ITuple, and the property must be a property of ITuple. Debug.Assert(e.Property.ContainingSymbol.Equals(input.Type)); Debug.Assert(e.Property.GetMethod.ParameterCount == 1); Debug.Assert(e.Property.GetMethod.Parameters[0].Type.SpecialType == SpecialType.System_Int32); TypeSymbol type = e.Property.GetMethod.ReturnType; var outputTemp = new BoundDagTemp(e.Syntax, type, e, index: 0); BoundExpression output = _tempAllocator.GetTemp(outputTemp); return(_factory.AssignmentExpression(output, _factory.Call(input, e.Property.GetMethod, _factory.Literal(e.Index)))); } default: throw ExceptionUtilities.UnexpectedValue(evaluation); } }
/// <summary> /// Lower "using [await] (expression) statement" to a try-finally block. /// </summary> private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock) { Debug.Assert(node.ExpressionOpt != null); Debug.Assert(node.DeclarationsOpt == null); // See comments in BuildUsingTryFinally for the details of the lowering to try-finally. // // SPEC: A using statement of the form "using (expression) statement; " has the // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ] // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression, // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement. // // DELIBERATE SPEC VIOLATION: // // The spec quote above implies that the expression must have a type; in fact we allow // the expression to be null. // // If expr is the constant null then we can elide the whole thing and simply generate the statement. BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt); if (rewrittenExpression.ConstantValue == ConstantValue.Null) { Debug.Assert(node.Locals.IsEmpty); // TODO: This might not be a valid assumption in presence of semicolon operator. return(tryBlock); } // Otherwise, we lower "using(expression) statement;" as follows: // // * If the expression is of type dynamic then we lower as though the user had written // // using(IDisposable temp = (IDisposable)expression) statement; // // Note that we have to do the conversion early, not in the finally block, because // if the conversion fails at runtime with an exception then the exception must happen // before the statement runs. // // * Otherwise we lower as though the user had written // // using(ResourceType temp = expression) statement; // TypeSymbol expressionType = rewrittenExpression.Type; SyntaxNode expressionSyntax = rewrittenExpression.Syntax; UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax; BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; if ((object)expressionType == null || expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; // or // IAsyncDisposable temp = (IAsyncDisposable) expr; TypeSymbol iDisposableType = node.AwaitOpt is null? _compilation.GetSpecialType(SpecialType.System_IDisposable) : _compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable); BoundExpression tempInit = MakeConversionNode( expressionSyntax, rewrittenExpression, Conversion.GetTrivialConversion(node.IDisposableConversion.Kind), iDisposableType, @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); boundTemp = _factory.StoreToTemp(tempInit, out tempAssignment, kind: SynthesizedLocalKind.Using); } else { // ResourceType temp = expr; boundTemp = _factory.StoreToTemp(rewrittenExpression, out tempAssignment, syntaxOpt: usingSyntax, kind: SynthesizedLocalKind.Using); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.Instrument) { expressionStatement = _instrumenter.InstrumentUsingTargetCapture(node, expressionStatement); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp, usingSyntax.AwaitKeyword, node.AwaitOpt); // { ResourceType temp = expr; try { ... } finally { ... } } return(new BoundBlock( syntax: usingSyntax, locals: node.Locals.Add(boundTemp.LocalSymbol), statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally))); }
/// <summary> /// Check that the pattern type is valid for the operand. Return true if an error was reported. /// </summary> private bool CheckValidPatternType( CSharpSyntaxNode typeSyntax, BoundExpression operand, TypeSymbol operandType, TypeSymbol patternType, bool patternTypeWasInSource, bool isVar, DiagnosticBag diagnostics) { Debug.Assert((object)operandType != null); Debug.Assert((object)patternType != null); // Because we do not support recursive patterns, we always have an operand Debug.Assert((object)operand != null); // Once we support recursive patterns that will be relaxed. if (operandType.IsErrorType() || patternType.IsErrorType()) { return(false); } else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource) { // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type. Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType()); return(true); } else if (patternType.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType); return(true); } else if (!isVar) { if (patternType.IsDynamic()) { Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax); return(true); } HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = operand != null ? this.Conversions.ClassifyConversionFromExpression(operand, patternType, ref useSiteDiagnostics, forCast : true) : this.Conversions.ClassifyConversionFromType(operandType, patternType, ref useSiteDiagnostics, forCast: true); diagnostics.Add(typeSyntax, useSiteDiagnostics); switch (conversion.Kind) { case ConversionKind.ExplicitDynamic: case ConversionKind.ImplicitDynamic: // Since the input was `dynamic`, which is equivalent to `object`, there must also // exist some unboxing, identity, or reference conversion as well, making the conversion legal. case ConversionKind.Boxing: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Identity: case ConversionKind.ImplicitReference: case ConversionKind.Unboxing: case ConversionKind.ImplicitNullable: // these are the conversions allowed by a pattern match break; case ConversionKind.DefaultOrNullLiteral: throw ExceptionUtilities.UnexpectedValue(conversion.Kind); //case ConversionKind.ExplicitNumeric: // we do not perform numeric conversions of the operand //case ConversionKind.ImplicitConstant: //case ConversionKind.ImplicitNumeric: default: if (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter()) { LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion(); if (requiredVersion > Compilation.LanguageVersion) { Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax, operandType, patternType, Compilation.LanguageVersion.ToDisplayString(), new CSharpRequiredLanguageVersion(requiredVersion)); return(true); } // permit pattern-matching when one of the types is an open type in C# 7.1. break; } else { Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType); return(true); } } } return(false); }
/// <summary> /// Check that the pattern type is valid for the operand. Return true if an error was reported. /// </summary> private bool CheckValidPatternType( CSharpSyntaxNode typeSyntax, TypeSymbol operandType, TypeSymbol patternType, bool patternTypeWasInSource, bool isVar, DiagnosticBag diagnostics) { Debug.Assert((object)operandType != null); Debug.Assert((object)patternType != null); if (operandType.IsErrorType() || patternType.IsErrorType()) { return(false); } else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource) { // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type. Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType()); return(true); } else if (patternType.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType); return(true); } else if (!isVar) { if (patternType.IsDynamic()) { Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax); return(true); } HashSet <DiagnosticInfo> useSiteDiagnostics = null; var matchPossible = ExpressionOfTypeMatchesPatternType(Conversions, operandType, patternType, ref useSiteDiagnostics, out Conversion conversion, operandConstantValue: null, operandCouldBeNull: true); diagnostics.Add(typeSyntax, useSiteDiagnostics); if (matchPossible != false) { if (!conversion.Exists && (operandType.ContainsTypeParameter() || patternType.ContainsTypeParameter())) { // permit pattern-matching when one of the types is an open type in C# 7.1. LanguageVersion requiredVersion = MessageID.IDS_FeatureGenericPatternMatching.RequiredVersion(); if (requiredVersion > Compilation.LanguageVersion) { Error(diagnostics, ErrorCode.ERR_PatternWrongGenericTypeInVersion, typeSyntax, operandType, patternType, Compilation.LanguageVersion.ToDisplayString(), new CSharpRequiredLanguageVersion(requiredVersion)); return(true); } } } else { Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType); return(true); } } return(false); }
/// <summary> /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names). /// </summary> private static TypeSymbol Better( TypeSymbol type1, TypeSymbol type2, ConversionsBase conversions, out bool hadNullabilityMismatch, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { hadNullabilityMismatch = false; // Anything is better than an error sym. if (type1.IsErrorType()) { return(type2); } if ((object)type2 == null || type2.IsErrorType()) { return(type1); } var conversionsWithoutNullability = conversions.WithNullability(false); var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.IsDynamic()) { return(type1); } if (type2.IsDynamic()) { return(type2); } if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)) { return(MethodTypeInferrer.Merge( TypeSymbolWithAnnotations.Create(type1), TypeSymbolWithAnnotations.Create(type2), VarianceKind.Out, conversions, out hadNullabilityMismatch).TypeSymbol); } return(null); } if (t1tot2) { return(type2); } if (t2tot1) { return(type1); } return(null); }
private BoundExpression MakeUnaryOperator( BoundUnaryOperator oldNode, UnaryOperatorKind kind, CSharpSyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { if (kind.IsDynamic()) { Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic()); Debug.Assert((object)method == null); // Logical operators on boxed Boolean constants: var constant = UnboxConstant(loweredOperand); if (constant == ConstantValue.True || constant == ConstantValue.False) { if (kind == UnaryOperatorKind.DynamicTrue) { return(_factory.Literal(constant.BooleanValue)); } else if (kind == UnaryOperatorKind.DynamicLogicalNegation) { return(MakeConversionNode(_factory.Literal(!constant.BooleanValue), type, @checked: false)); } } return(_dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression()); } else if (kind.IsLifted()) { if (!_inExpressionLambda) { return(LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type)); } } else if (kind.IsUserDefined()) { Debug.Assert((object)method != null); Debug.Assert(type == method.ReturnType); if (!_inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse) { // @t-mawind // As usual, concept accesses need to be rewritten down to their // default() form. // Is this correct? It's mostly a copy over from the binary case, // but the unary case is different enough to make me nervous. if (method is SynthesizedWitnessMethodSymbol) { return(BoundCall.Synthesized(syntax, SynthesizeWitnessInvocationReceiver(syntax, ((SynthesizedWitnessMethodSymbol)method).Parent), method, loweredOperand)); } return(BoundCall.Synthesized(syntax, null, method, loweredOperand)); } } else if (kind.Operator() == UnaryOperatorKind.UnaryPlus) { // We do not call the operator even for decimal; we simply optimize it away entirely. return(loweredOperand); } if (kind == UnaryOperatorKind.EnumBitwiseComplement) { var underlyingType = loweredOperand.Type.GetEnumUnderlyingType(); var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType); var upconvertType = upconvertSpecialType == underlyingType.SpecialType ? underlyingType : _compilation.GetSpecialType(upconvertSpecialType); var newOperand = MakeConversionNode(loweredOperand, upconvertType, false); UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType); var newNode = (oldNode != null) ? oldNode.Update( newKind, newOperand, oldNode.ConstantValueOpt, method, newOperand.ResultKind, upconvertType) : new BoundUnaryOperator( syntax, newKind, newOperand, null, method, LookupResultKind.Viable, upconvertType); return(MakeConversionNode(newNode.Syntax, newNode, Conversion.ExplicitEnumeration, type, @checked: false)); } if (kind == UnaryOperatorKind.DecimalUnaryMinus) { method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation); if (!_inExpressionLambda) { return(BoundCall.Synthesized(syntax, null, method, loweredOperand)); } } return((oldNode != null) ? oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) : new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type)); }
internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNode syntax, SyntaxToken usingKeyword, SyntaxToken awaitKeyword, Binder originalBinder, UsingStatementBinder usingBinderOpt, DiagnosticBag diagnostics) { bool isUsingDeclaration = syntax.Kind() == SyntaxKind.LocalDeclarationStatement; bool isExpression = !isUsingDeclaration && syntax.Kind() != SyntaxKind.VariableDeclaration; bool hasAwait = awaitKeyword != default; Debug.Assert(isUsingDeclaration || usingBinderOpt != null); TypeSymbol disposableInterface = getDisposableInterface(hasAwait); Debug.Assert((object)disposableInterface != null); bool hasErrors = ReportUseSiteDiagnostics(disposableInterface, diagnostics, hasAwait ? awaitKeyword : usingKeyword); Conversion iDisposableConversion; ImmutableArray <BoundLocalDeclaration> declarationsOpt = default; BoundMultipleLocalDeclarations multipleDeclarationsOpt = null; BoundExpression expressionOpt = null; TypeSymbol declarationTypeOpt = null; MethodSymbol disposeMethodOpt; TypeSymbol awaitableTypeOpt; if (isExpression) { expressionOpt = usingBinderOpt.BindTargetExpression(diagnostics, originalBinder); hasErrors |= !populateDisposableConversionOrDisposeMethod(fromExpression: true, out iDisposableConversion, out disposeMethodOpt, out awaitableTypeOpt); } else { VariableDeclarationSyntax declarationSyntax = isUsingDeclaration ? ((LocalDeclarationStatementSyntax)syntax).Declaration : (VariableDeclarationSyntax)syntax; originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarationsOpt); Debug.Assert(!declarationsOpt.IsEmpty); multipleDeclarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarationsOpt); declarationTypeOpt = declarationsOpt[0].DeclaredTypeOpt.Type; if (declarationTypeOpt.IsDynamic()) { iDisposableConversion = Conversion.ImplicitDynamic; disposeMethodOpt = null; awaitableTypeOpt = null; } else { hasErrors |= !populateDisposableConversionOrDisposeMethod(fromExpression: false, out iDisposableConversion, out disposeMethodOpt, out awaitableTypeOpt); } } BoundAwaitableInfo awaitOpt = null; if (hasAwait) { // even if we don't have a proper value to await, we'll still report bad usages of `await` originalBinder.ReportBadAwaitDiagnostics(syntax, awaitKeyword.GetLocation(), diagnostics, ref hasErrors); if (awaitableTypeOpt is null) { awaitOpt = new BoundAwaitableInfo(syntax, awaitableInstancePlaceholder: null, isDynamic: true, getAwaiter: null, isCompleted: null, getResult: null) { WasCompilerGenerated = true }; } else { hasErrors |= ReportUseSiteDiagnostics(awaitableTypeOpt, diagnostics, awaitKeyword); var placeholder = new BoundAwaitableValuePlaceholder(syntax, valEscape: originalBinder.LocalScopeDepth, awaitableTypeOpt).MakeCompilerGenerated(); awaitOpt = originalBinder.BindAwaitInfo(placeholder, syntax, diagnostics, ref hasErrors); } } // This is not awesome, but its factored. // In the future it might be better to have a separate shared type that we add the info to, and have the callers create the appropriate bound nodes from it if (isUsingDeclaration) { return(new BoundUsingLocalDeclarations(syntax, disposeMethodOpt, iDisposableConversion, awaitOpt, declarationsOpt, hasErrors)); } else { BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(usingBinderOpt._syntax.Statement, diagnostics); return(new BoundUsingStatement( usingBinderOpt._syntax, usingBinderOpt.Locals, multipleDeclarationsOpt, expressionOpt, iDisposableConversion, boundBody, awaitOpt, disposeMethodOpt, hasErrors)); } bool populateDisposableConversionOrDisposeMethod(bool fromExpression, out Conversion iDisposableConversion, out MethodSymbol disposeMethodOpt, out TypeSymbol awaitableTypeOpt) { HashSet <DiagnosticInfo> useSiteDiagnostics = null; iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteDiagnostics); disposeMethodOpt = null; awaitableTypeOpt = null; diagnostics.Add(syntax, useSiteDiagnostics); if (iDisposableConversion.IsImplicit) { if (hasAwait) { awaitableTypeOpt = originalBinder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask); } return(true); } TypeSymbol type = fromExpression ? expressionOpt.Type : declarationTypeOpt; // If this is a ref struct, or we're in a valid asynchronous using, try binding via pattern. // We won't need to try and bind a second time if it fails, as async dispose can't be pattern based (ref structs are not allowed in async methods) if (type is object && (type.IsRefLikeType || hasAwait)) { BoundExpression receiver = fromExpression ? expressionOpt : new BoundLocal(syntax, declarationsOpt[0].LocalSymbol, null, type) { WasCompilerGenerated = true }; DiagnosticBag patternDiagnostics = originalBinder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureUsingDeclarations) ? diagnostics : new DiagnosticBag(); disposeMethodOpt = originalBinder.TryFindDisposePatternMethod(receiver, syntax, hasAwait, patternDiagnostics); if (disposeMethodOpt is object) { MessageID.IDS_FeatureUsingDeclarations.CheckFeatureAvailability(diagnostics, originalBinder.Compilation, syntax.Location); if (hasAwait) { awaitableTypeOpt = disposeMethodOpt.ReturnType; } return(true); } } if (type is null || !type.IsErrorType()) { // Retry with a different assumption about whether the `using` is async TypeSymbol alternateInterface = getDisposableInterface(!hasAwait); HashSet <DiagnosticInfo> ignored = null; Conversion alternateConversion = classifyConversion(fromExpression, alternateInterface, ref ignored); bool wrongAsync = alternateConversion.IsImplicit; ErrorCode errorCode = wrongAsync ? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync) : (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp); Error(diagnostics, errorCode, syntax, declarationTypeOpt ?? expressionOpt.Display); } return(false); } Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref HashSet <DiagnosticInfo> diag) { return(fromExpression ? originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref diag) : originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref diag)); } TypeSymbol getDisposableInterface(bool isAsync) { return(isAsync ? originalBinder.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable) : originalBinder.Compilation.GetSpecialType(SpecialType.System_IDisposable)); } }
private BoundExpression GetDefaultParameterValue(CSharpSyntaxNode syntax, ParameterSymbol parameter) { TypeSymbol parameterType = parameter.Type; ConstantValue defaultConstantValue = parameter.ExplicitDefaultConstantValue; BoundExpression defaultValue; SourceLocation callerSourceLocation; if (parameter.IsCallerLineNumber && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { int line = callerSourceLocation.SourceTree.GetDisplayLineNumber(callerSourceLocation.SourceSpan); BoundExpression lineLiteral = MakeLiteral(syntax, ConstantValue.Create(line), compilation.GetSpecialType(SpecialType.System_Int32)); if (parameterType.IsNullableType()) { defaultValue = MakeConversion(lineLiteral, parameterType.GetNullableUnderlyingType(), false); // wrap it in a nullable ctor. defaultValue = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor), defaultValue); } else { defaultValue = MakeConversion(lineLiteral, parameterType, false); } } else if (parameter.IsCallerFilePath && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { string path = callerSourceLocation.SourceTree.GetDisplayPath(callerSourceLocation.SourceSpan, compilation.Options.SourceReferenceResolver); BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(path), compilation.GetSpecialType(SpecialType.System_String)); defaultValue = MakeConversion(memberNameLiteral, parameterType, false); } else if (parameter.IsCallerMemberName && ((callerSourceLocation = GetCallerLocation(syntax)) != null)) { string memberName = this.factory.TopLevelMethod.GetMemberCallerName(); BoundExpression memberNameLiteral = MakeLiteral(syntax, ConstantValue.Create(memberName), compilation.GetSpecialType(SpecialType.System_String)); defaultValue = MakeConversion(memberNameLiteral, parameterType, false); } else if (defaultConstantValue == ConstantValue.NotAvailable) { // There is no constant value given for the parameter in source/metadata. if (parameterType.IsDynamic() || parameterType.SpecialType == SpecialType.System_Object) { // We have something like M([Optional] object x). We have special handling for such situations. defaultValue = GetDefaultParameterSpecial(syntax, parameter); } else { // The argument to M([Optional] int x) becomes default(int) defaultValue = new BoundDefaultOperator(syntax, parameterType); } } else if (defaultConstantValue.IsNull && parameterType.IsValueType) { // We have something like M(int? x = null) or M(S x = default(S)), // so replace the argument with default(int?). defaultValue = new BoundDefaultOperator(syntax, parameterType); } else if (parameterType.IsNullableType()) { // We have something like M(double? x = 1.23), so replace the argument // with new double?(1.23). TypeSymbol constantType = compilation.GetSpecialType(defaultConstantValue.SpecialType); defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType); // The parameter's underlying type might not match the constant type. For example, we might have // a default value of 5 (an integer) but a parameter type of decimal?. defaultValue = MakeConversion(defaultValue, parameterType.GetNullableUnderlyingType(), @checked: false, acceptFailingConversion: true); // Finally, wrap it in a nullable ctor. defaultValue = new BoundObjectCreationExpression( syntax, GetNullableMethod(syntax, parameterType, SpecialMember.System_Nullable_T__ctor), defaultValue); } else if (defaultConstantValue.IsNull || defaultConstantValue.IsBad) { defaultValue = MakeLiteral(syntax, defaultConstantValue, parameterType); } else { // We have something like M(double = 1.23), so replace the argument with 1.23. TypeSymbol constantType = compilation.GetSpecialType(defaultConstantValue.SpecialType); defaultValue = MakeLiteral(syntax, defaultConstantValue, constantType); // The parameter type might not match the constant type. defaultValue = MakeConversion(defaultValue, parameterType, @checked: false, acceptFailingConversion: true); } return(defaultValue); }
/// <summary> /// Lower "using (expression) statement" to a try-finally block. /// </summary> private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock) { Debug.Assert(node.ExpressionOpt != null); Debug.Assert(node.DeclarationsOpt == null); // See comments in BuildUsingTryFinally for the details of the lowering to try-finally. // // SPEC: A using statement of the form "using (expression) statement; " has the // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ] // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression, // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement. // // DELIBERATE SPEC VIOLATION: // // The spec quote above implies that the expression must have a type; in fact we allow // the expression to be null. // // If expr is the constant null then we can elide the whole thing and simply generate the statement. BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt); if (rewrittenExpression.ConstantValue == ConstantValue.Null) { return(tryBlock); } // Otherwise, we lower "using(expression) statement;" as follows: // // * If the expression is of type dynamic then we lower as though the user had written // // using(IDisposable temp = (IDisposable)expression) statement; // // Note that we have to do the conversion early, not in the finally block, because // if the conversion fails at runtime with an exception then the exception must happen // before the statement runs. // // * Otherwise we lower as though the user had written // // using(ResourceType temp = expression) statement; // TypeSymbol expressionType = rewrittenExpression.Type; CSharpSyntaxNode expressionSyntax = rewrittenExpression.Syntax; UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax; BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; if ((object)expressionType == null || expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; BoundExpression tempInit = MakeConversion( expressionSyntax, rewrittenExpression, node.IDisposableConversion.Kind, this.compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment); } else { // ResourceType temp = expr; boundTemp = this.factory.StoreToTemp(rewrittenExpression, tempKind: TempKind.Using, store: out tempAssignment); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.generateDebugInfo) { // NOTE: unlike in the assignment case, the sequence point is on the using keyword, not the expression. expressionStatement = new BoundSequencePointWithSpan(usingSyntax, expressionStatement, usingSyntax.UsingKeyword.Span); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); // { ResourceType temp = expr; try { ... } finally { ... } } return(new BoundBlock( syntax: usingSyntax, localsOpt: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally))); }
/// <summary> /// If the extension method is applicable based on the "this" argument type, return /// the method constructed with the inferred type arguments. If the method is not an /// unconstructed generic method, type inference is skipped. If the method is not /// applicable, or if constraints when inferring type parameters from the "this" type /// are not satisfied, the return value is null. /// </summary> public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol method, TypeSymbol thisType, Compilation compilation, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(method.IsExtensionMethod); Debug.Assert((object)thisType != null); if (!method.IsGenericMethod || method != method.ConstructedFrom) { return method; } // We never resolve extension methods on a dynamic receiver. if (thisType.IsDynamic()) { return null; } var containingAssembly = method.ContainingAssembly; var errorNamespace = containingAssembly.GlobalNamespace; var conversions = new TypeConversions(containingAssembly.CorLibrary); // There is absolutely no plausible syntax/tree that we could use for these // synthesized literals. We could be speculatively binding a call to a PE method. var syntaxTree = CSharpSyntaxTree.Dummy; var syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); // Create an argument value for the "this" argument of specific type, // and pass the same bad argument value for all other arguments. var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType) { WasCompilerGenerated = true }; var otherArgumentType = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false); var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType) { WasCompilerGenerated = true }; var paramCount = method.ParameterCount; var arguments = new BoundExpression[paramCount]; var argumentTypes = new TypeSymbol[paramCount]; for (int i = 0; i < paramCount; i++) { var argument = (i == 0) ? thisArgumentValue : otherArgumentValue; arguments[i] = argument; argumentTypes[i] = argument.Type; } var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument( conversions, method, argumentTypes.AsImmutableOrNull(), arguments.AsImmutableOrNull(), ref useSiteDiagnostics); if (typeArgs.IsDefault) { return null; } int firstNullInTypeArgs = -1; // For the purpose of constraint checks we use error type symbol in place of type arguments that we couldn't infer from the first argument. // This prevents constraint checking from failing for corresponding type parameters. var typeArgsForConstraintsCheck = typeArgs; for (int i = 0; i < typeArgsForConstraintsCheck.Length; i++) { if ((object)typeArgsForConstraintsCheck[i] == null) { firstNullInTypeArgs = i; var builder = ArrayBuilder<TypeSymbol>.GetInstance(); builder.AddRange(typeArgs, firstNullInTypeArgs); for (; i < typeArgsForConstraintsCheck.Length; i++) { builder.Add(typeArgsForConstraintsCheck[i] ?? ErrorTypeSymbol.UnknownResultType); } typeArgsForConstraintsCheck = builder.ToImmutableAndFree(); break; } } // Check constraints. var diagnosticsBuilder = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance(); var typeParams = method.TypeParameters; var substitution = new TypeMap(typeParams, typeArgsForConstraintsCheck.SelectAsArray(TypeMap.TypeSymbolAsTypeWithModifiers)); ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null; var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); diagnosticsBuilder.Free(); if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0) { if (useSiteDiagnostics == null) { useSiteDiagnostics = new HashSet<DiagnosticInfo>(); } foreach (var diag in useSiteDiagnosticsBuilder) { useSiteDiagnostics.Add(diag.DiagnosticInfo); } } if (!success) { return null; } // For the purpose of construction we use original type parameters in place of type arguments that we couldn't infer from the first argument. var typeArgsForConstruct = typeArgs; if (firstNullInTypeArgs != -1) { var builder = ArrayBuilder<TypeSymbol>.GetInstance(); builder.AddRange(typeArgs, firstNullInTypeArgs); for (int i = firstNullInTypeArgs; i < typeArgsForConstruct.Length; i++) { builder.Add(typeArgsForConstruct[i] ?? typeParams[i]); } typeArgsForConstruct = builder.ToImmutableAndFree(); } return method.Construct(typeArgsForConstruct); }
private bool IsCheckedConversion(TypeSymbol source, TypeSymbol target) { Debug.Assert((object)target != null); if ((object)source == null || !CheckOverflowAtRuntime) { return false; } if (source.IsDynamic()) { return true; } SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType; SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType; // integral to double or float is never checked, but float/double to integral // may be checked. bool sourceIsNumeric = SpecialType.System_Char <= sourceST && sourceST <= SpecialType.System_Double; bool targetIsNumeric = SpecialType.System_Char <= targetST && targetST <= SpecialType.System_UInt64; return sourceIsNumeric && (targetIsNumeric || target.IsPointerType()) || targetIsNumeric && source.IsPointerType(); }
protected BoundExpression LowerEvaluation(BoundDagEvaluation evaluation) { BoundExpression input = _tempAllocator.GetTemp(evaluation.Input); switch (evaluation) { case BoundDagFieldEvaluation f: { FieldSymbol field = f.Field; var outputTemp = new BoundDagTemp(f.Syntax, field.Type, f); BoundExpression output = _tempAllocator.GetTemp(outputTemp); BoundExpression access = _localRewriter.MakeFieldAccess(f.Syntax, input, field, null, LookupResultKind.Viable, field.Type); access.WasCompilerGenerated = true; return(_factory.AssignmentExpression(output, access)); } case BoundDagPropertyEvaluation p: { PropertySymbol property = p.Property; var outputTemp = new BoundDagTemp(p.Syntax, property.Type, p); BoundExpression output = _tempAllocator.GetTemp(outputTemp); return(_factory.AssignmentExpression(output, _localRewriter.MakePropertyAccess(_factory.Syntax, input, property, LookupResultKind.Viable, property.Type, isLeftOfAssignment: false))); } case BoundDagDeconstructEvaluation d: { MethodSymbol method = d.DeconstructMethod; var refKindBuilder = ArrayBuilder <RefKind> .GetInstance(); var argBuilder = ArrayBuilder <BoundExpression> .GetInstance(); BoundExpression receiver; void addArg(RefKind refKind, BoundExpression expression) { refKindBuilder.Add(refKind); argBuilder.Add(expression); } Debug.Assert(method.Name == WellKnownMemberNames.DeconstructMethodName); int extensionExtra; if (method.IsStatic) { Debug.Assert(method.IsExtensionMethod); receiver = _factory.Type(method.ContainingType); addArg(method.ParameterRefKinds[0], input); extensionExtra = 1; } else { receiver = input; extensionExtra = 0; } for (int i = extensionExtra; i < method.ParameterCount; i++) { ParameterSymbol parameter = method.Parameters[i]; Debug.Assert(parameter.RefKind == RefKind.Out); var outputTemp = new BoundDagTemp(d.Syntax, parameter.Type, d, i - extensionExtra); addArg(RefKind.Out, _tempAllocator.GetTemp(outputTemp)); } return(_factory.Call(receiver, method, refKindBuilder.ToImmutableAndFree(), argBuilder.ToImmutableAndFree())); } case BoundDagTypeEvaluation t: { TypeSymbol inputType = input.Type; Debug.Assert(inputType is { }); if (inputType.IsDynamic()) { // Avoid using dynamic conversions for pattern-matching. inputType = _factory.SpecialType(SpecialType.System_Object); input = _factory.Convert(inputType, input); } TypeSymbol type = t.Type; var outputTemp = new BoundDagTemp(t.Syntax, type, t); BoundExpression output = _tempAllocator.GetTemp(outputTemp); CompoundUseSiteInfo <AssemblySymbol> useSiteInfo = _localRewriter.GetNewCompoundUseSiteInfo(); Conversion conversion = _factory.Compilation.Conversions.ClassifyBuiltInConversion(inputType, output.Type, ref useSiteInfo); _localRewriter._diagnostics.Add(t.Syntax, useSiteInfo); BoundExpression evaluated; if (conversion.Exists) { if (conversion.Kind == ConversionKind.ExplicitNullable && inputType.GetNullableUnderlyingType().Equals(output.Type, TypeCompareKind.AllIgnoreOptions) && _localRewriter.TryGetNullableMethod(t.Syntax, inputType, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault)) { // As a special case, since the null test has already been done we can use Nullable<T>.GetValueOrDefault evaluated = _factory.Call(input, getValueOrDefault); } else { evaluated = _factory.Convert(type, input, conversion); } } else { evaluated = _factory.As(input, type); } return(_factory.AssignmentExpression(output, evaluated)); }
/// <summary> /// If the extension method is applicable based on the "this" argument type, return /// the method constructed with the inferred type arguments. If the method is not an /// unconstructed generic method, type inference is skipped. If the method is not /// applicable, or if constraints when inferring type parameters from the "this" type /// are not satisfied, the return value is null. /// </summary> /// <param name="compilation">Compilation used to check constraints. The latest language version is assumed if this is null.</param> private static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, CSharpCompilation compilation, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(method.IsExtensionMethod); Debug.Assert((object)thisType != null); if (!method.IsGenericMethod || method != method.ConstructedFrom) { return(method); } // We never resolve extension methods on a dynamic receiver. if (thisType.IsDynamic()) { return(null); } var containingAssembly = method.ContainingAssembly; var errorNamespace = containingAssembly.GlobalNamespace; var conversions = new TypeConversions(containingAssembly.CorLibrary); // There is absolutely no plausible syntax/tree that we could use for these // synthesized literals. We could be speculatively binding a call to a PE method. var syntaxTree = CSharpSyntaxTree.Dummy; var syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); // Create an argument value for the "this" argument of specific type, // and pass the same bad argument value for all other arguments. var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType) { WasCompilerGenerated = true }; var otherArgumentType = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false); var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType) { WasCompilerGenerated = true }; var paramCount = method.ParameterCount; var arguments = new BoundExpression[paramCount]; for (int i = 0; i < paramCount; i++) { var argument = (i == 0) ? thisArgumentValue : otherArgumentValue; arguments[i] = argument; } var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument( conversions, method, arguments.AsImmutable(), useSiteDiagnostics: ref useSiteDiagnostics); if (typeArgs.IsDefault) { return(null); } // For the purpose of constraint checks we use error type symbol in place of type arguments that we couldn't infer from the first argument. // This prevents constraint checking from failing for corresponding type parameters. int firstNullInTypeArgs = -1; var notInferredTypeParameters = PooledHashSet <TypeParameterSymbol> .GetInstance(); var typeParams = method.TypeParameters; var typeArgsForConstraintsCheck = typeArgs; for (int i = 0; i < typeArgsForConstraintsCheck.Length; i++) { if (!typeArgsForConstraintsCheck[i].HasType) { firstNullInTypeArgs = i; var builder = ArrayBuilder <TypeWithAnnotations> .GetInstance(); builder.AddRange(typeArgsForConstraintsCheck, firstNullInTypeArgs); for (; i < typeArgsForConstraintsCheck.Length; i++) { var typeArg = typeArgsForConstraintsCheck[i]; if (!typeArg.HasType) { notInferredTypeParameters.Add(typeParams[i]); builder.Add(TypeWithAnnotations.Create(ErrorTypeSymbol.UnknownResultType)); } else { builder.Add(typeArg); } } typeArgsForConstraintsCheck = builder.ToImmutableAndFree(); break; } } // Check constraints. var diagnosticsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance(); var substitution = new TypeMap(typeParams, typeArgsForConstraintsCheck); ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null; var success = method.CheckConstraints(conversions, substitution, typeParams, typeArgsForConstraintsCheck, compilation, diagnosticsBuilder, nullabilityDiagnosticsBuilderOpt: null, ref useSiteDiagnosticsBuilder, ignoreTypeConstraintsDependentOnTypeParametersOpt: notInferredTypeParameters.Count > 0 ? notInferredTypeParameters : null); diagnosticsBuilder.Free(); notInferredTypeParameters.Free(); if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0) { if (useSiteDiagnostics == null) { useSiteDiagnostics = new HashSet <DiagnosticInfo>(); } foreach (var diag in useSiteDiagnosticsBuilder) { useSiteDiagnostics.Add(diag.DiagnosticInfo); } } if (!success) { return(null); } // For the purpose of construction we use original type parameters in place of type arguments that we couldn't infer from the first argument. ImmutableArray <TypeWithAnnotations> typeArgsForConstruct = typeArgs; if (typeArgs.Any(t => !t.HasType)) { typeArgsForConstruct = typeArgs.ZipAsArray( method.TypeParameters, (t, tp) => t.HasType ? t : TypeWithAnnotations.Create(tp)); } return(method.Construct(typeArgsForConstruct)); }
/// <summary> /// Check that the pattern type is valid for the operand. Return true if an error was reported. /// </summary> private bool CheckValidPatternType( CSharpSyntaxNode typeSyntax, BoundExpression operand, TypeSymbol operandType, TypeSymbol patternType, bool patternTypeWasInSource, bool isVar, DiagnosticBag diagnostics) { Debug.Assert((object)operandType != null); Debug.Assert((object)patternType != null); // Because we do not support recursive patterns, we always have an operand Debug.Assert((object)operand != null); // Once we support recursive patterns that will be relaxed. if (operandType.IsErrorType() || patternType.IsErrorType()) { return(false); } else if (patternType.IsNullableType() && !isVar && patternTypeWasInSource) { // It is an error to use pattern-matching with a nullable type, because you'll never get null. Use the underlying type. Error(diagnostics, ErrorCode.ERR_PatternNullableType, typeSyntax, patternType, patternType.GetNullableUnderlyingType()); return(true); } else if (patternType.IsStatic) { Error(diagnostics, ErrorCode.ERR_VarDeclIsStaticClass, typeSyntax, patternType); return(true); } else if (!isVar) { if (patternType.IsDynamic()) { Error(diagnostics, ErrorCode.ERR_PatternDynamicType, typeSyntax); return(true); } HashSet <DiagnosticInfo> useSiteDiagnostics = null; Conversion conversion = operand != null ? this.Conversions.ClassifyConversionFromExpression(operand, patternType, ref useSiteDiagnostics, forCast : true) : this.Conversions.ClassifyConversionFromType(operandType, patternType, ref useSiteDiagnostics, forCast: true); diagnostics.Add(typeSyntax, useSiteDiagnostics); switch (conversion.Kind) { case ConversionKind.ExplicitDynamic: case ConversionKind.ImplicitDynamic: // Since the input was `dynamic`, which is equivalent to `object`, there must also // exist some unboxing, identity, or reference conversion as well, making the conversion legal. case ConversionKind.Boxing: case ConversionKind.ExplicitNullable: case ConversionKind.ExplicitReference: case ConversionKind.Identity: case ConversionKind.ImplicitReference: case ConversionKind.Unboxing: case ConversionKind.NullLiteral: case ConversionKind.ImplicitNullable: // these are the conversions allowed by a pattern match break; //case ConversionKind.ExplicitNumeric: // we do not perform numeric conversions of the operand //case ConversionKind.ImplicitConstant: //case ConversionKind.ImplicitNumeric: default: Error(diagnostics, ErrorCode.ERR_PatternWrongType, typeSyntax, operandType, patternType); return(true); } } return(false); }
private BoundExpression MakeUnaryOperator( BoundUnaryOperator oldNode, UnaryOperatorKind kind, CSharpSyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { if (kind.IsDynamic()) { Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic()); Debug.Assert((object)method == null); // Logical operators on boxed Boolean constants: var constant = UnboxConstant(loweredOperand); if (constant == ConstantValue.True || constant == ConstantValue.False) { if (kind == UnaryOperatorKind.DynamicTrue) { return(factory.Literal(constant.BooleanValue)); } else if (kind == UnaryOperatorKind.DynamicLogicalNegation) { return(MakeConversion(factory.Literal(!constant.BooleanValue), type, @checked: false)); } } return(dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression()); } else if (kind.IsLifted()) { if (!inExpressionLambda) { return(LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type)); } } else if (kind.IsUserDefined()) { Debug.Assert((object)method != null); Debug.Assert(type == method.ReturnType); if (!inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse) { return(BoundCall.Synthesized(syntax, null, method, loweredOperand)); } } else if (kind.Operator() == UnaryOperatorKind.UnaryPlus) { // We do not call the operator even for decimal; we simply optimize it away entirely. return(loweredOperand); } if (kind == UnaryOperatorKind.EnumBitwiseComplement) { var underlyingType = loweredOperand.Type.GetEnumUnderlyingType(); var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType); var upconvertType = upconvertSpecialType == underlyingType.SpecialType ? underlyingType : compilation.GetSpecialType(upconvertSpecialType); var newOperand = MakeConversion(loweredOperand, upconvertType, false); UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType); var newNode = (oldNode != null) ? oldNode.Update( newKind, newOperand, oldNode.ConstantValueOpt, method, newOperand.ResultKind, upconvertType) : new BoundUnaryOperator( syntax, newKind, newOperand, null, method, LookupResultKind.Viable, upconvertType); return(MakeConversion(newNode.Syntax, newNode, ConversionKind.ExplicitEnumeration, type, @checked: false)); } if (kind == UnaryOperatorKind.DecimalUnaryMinus) { method = (MethodSymbol)this.compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation); if (!inExpressionLambda) { return(BoundCall.Synthesized(syntax, null, method, loweredOperand)); } } return((oldNode != null) ? oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) : new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type)); }
/// <summary> /// If the extension method is applicable based on the "this" argument type, return /// the method constructed with the inferred type arguments. If the method is not an /// unconstructed generic method, type inference is skipped. If the method is not /// applicable, or if constraints when inferring type parameters from the "this" type /// are not satisfied, the return value is null. /// </summary> private static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(method.IsExtensionMethod); Debug.Assert((object)thisType != null); if (!method.IsGenericMethod || method != method.ConstructedFrom) { return(method); } // We never resolve extension methods on a dynamic receiver. if (thisType.IsDynamic()) { return(null); } var containingAssembly = method.ContainingAssembly; var errorNamespace = containingAssembly.GlobalNamespace; var conversions = new TypeConversions(containingAssembly.CorLibrary); // There is absolutely no plausible syntax/tree that we could use for these // synthesized literals. We could be speculatively binding a call to a PE method. var syntaxTree = CSharpSyntaxTree.Dummy; var syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); // Create an argument value for the "this" argument of specific type, // and pass the same bad argument value for all other arguments. var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType) { WasCompilerGenerated = true }; var otherArgumentType = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false); var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType) { WasCompilerGenerated = true }; var paramCount = method.ParameterCount; var arguments = new BoundExpression[paramCount]; for (int i = 0; i < paramCount; i++) { var argument = (i == 0) ? thisArgumentValue : otherArgumentValue; arguments[i] = argument; } var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument( conversions, method, arguments.AsImmutable(), useSiteDiagnostics: ref useSiteDiagnostics); if (typeArgs.IsDefault) { return(null); } // For the purpose of construction we use original type parameters in place of type arguments that we couldn't infer from the first argument. ImmutableArray <TypeWithAnnotations> typeArgsForConstruct = typeArgs; if (typeArgs.Any(t => !t.HasType)) { typeArgsForConstruct = typeArgs.ZipAsArray( method.TypeParameters, (t, tp) => t.HasType ? t : TypeWithAnnotations.Create(tp)); } return(method.Construct(typeArgsForConstruct)); }
private ForEachEnumeratorInfo.Builder GetDefaultEnumeratorInfo(ForEachEnumeratorInfo.Builder builder, DiagnosticBag diagnostics, TypeSymbol collectionExprType) { // NOTE: for arrays, we won't actually use any of these members - they're just for the API. builder.CollectionType = GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, _syntax); if (collectionExprType.IsDynamic()) { builder.ElementType = _syntax.Type.IsVar ? (TypeSymbol)DynamicTypeSymbol.Instance : GetSpecialType(SpecialType.System_Object, diagnostics, _syntax); } else { builder.ElementType = collectionExprType.SpecialType == SpecialType.System_String? GetSpecialType(SpecialType.System_Char, diagnostics, _syntax) : ((ArrayTypeSymbol)collectionExprType).ElementType; } // CONSIDER: // For arrays and string none of these members will actually be emitted, so it seems strange to prevent compilation if they can't be found. // skip this work in the batch case? builder.GetEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, _syntax); builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, _syntax); builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, _syntax); Debug.Assert((object)builder.GetEnumeratorMethod == null || builder.GetEnumeratorMethod.ReturnType == this.Compilation.GetSpecialType(SpecialType.System_Collections_IEnumerator)); // We don't know the runtime type, so we will have to insert a runtime check for IDisposable (with a conditional call to IDisposable.Dispose). builder.NeedsDisposeMethod = true; return builder; }
/// <summary> /// Returns the better type amongst the two, with some possible modifications (dynamic/object or tuple names). /// </summary> private TypeSymbol Better(TypeSymbol type1, TypeSymbol type2, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // Anything is better than an error sym. if (type1.IsErrorType()) { return type2; } if ((object)type2 == null || type2.IsErrorType()) { return type1; } var t1tot2 = _conversions.ClassifyImplicitConversionFromType(type1, type2, ref useSiteDiagnostics).Exists; var t2tot1 = _conversions.ClassifyImplicitConversionFromType(type2, type1, ref useSiteDiagnostics).Exists; if (t1tot2 && t2tot1) { if (type1.IsDynamic()) { return type1; } if (type2.IsDynamic()) { return type2; } if (type1.Equals(type2, TypeCompareKind.IgnoreDynamicAndTupleNames)) { return MethodTypeInferrer.MergeTupleNames(type1, type2, MethodTypeInferrer.MergeDynamic(type1, type2, type1, _conversions.CorLibrary), _conversions.CorLibrary); } return null; } if (t1tot2) { return type2; } if (t2tot1) { return type1; } return null; }
private void TestUnaryIntrinsicSymbol( UnaryOperatorKind op, TypeSymbol type, CSharpCompilation compilation, SemanticModel semanticModel, ExpressionSyntax node1, ExpressionSyntax node2, ExpressionSyntax node3, ExpressionSyntax node4 ) { SymbolInfo info1 = semanticModel.GetSymbolInfo(node1); Assert.Equal(type.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 = (MethodSymbol)semanticModel.GetSymbolInfo(node3).Symbol; var symbol4 = semanticModel.GetSymbolInfo(node4).Symbol; Assert.Equal(symbol1, symbol3); if ((object)symbol1 != null) { Assert.NotSame(symbol1, symbol3); Assert.Equal(symbol1.GetHashCode(), symbol3.GetHashCode()); Assert.Equal(symbol1.Parameters[0], symbol3.Parameters[0]); Assert.Equal(symbol1.Parameters[0].GetHashCode(), symbol3.Parameters[0].GetHashCode()); } Assert.Equal(symbol2, symbol4); TypeSymbol underlying = type; if (op == UnaryOperatorKind.BitwiseComplement || op == UnaryOperatorKind.PrefixDecrement || op == UnaryOperatorKind.PrefixIncrement || op == UnaryOperatorKind.PostfixDecrement || op == UnaryOperatorKind.PostfixIncrement) { underlying = type.EnumUnderlyingType(); } UnaryOperatorKind result = OverloadResolution.UnopEasyOut.OpKind(op, underlying); UnaryOperatorSignature signature; if (result == UnaryOperatorKind.Error) { if (type.IsDynamic()) { signature = new UnaryOperatorSignature(op | UnaryOperatorKind.Dynamic, type, type); } else if (type.IsPointerType() && (op == UnaryOperatorKind.PrefixDecrement || op == UnaryOperatorKind.PrefixIncrement || op == UnaryOperatorKind.PostfixDecrement || op == UnaryOperatorKind.PostfixIncrement)) { signature = new UnaryOperatorSignature(op | UnaryOperatorKind.Pointer, type, type); } else { Assert.Null(symbol1); Assert.Null(symbol2); Assert.Null(symbol3); Assert.Null(symbol4); return; } } else { signature = compilation.builtInOperators.GetSignature(result); if ((object)underlying != (object)type) { Assert.Equal(underlying, signature.OperandType); Assert.Equal(underlying, signature.ReturnType); signature = new UnaryOperatorSignature(signature.Kind, type, type); } } Assert.NotNull(symbol1); string containerName = signature.OperandType.ToTestDisplayString(); string returnName = signature.ReturnType.ToTestDisplayString(); if (op == UnaryOperatorKind.LogicalNegation && type.IsEnumType()) { containerName = type.ToTestDisplayString(); returnName = containerName; } Assert.Equal(String.Format("{2} {0}.{1}({0} value)", containerName, OperatorFacts.UnaryOperatorNameFromOperatorKind(op), returnName), symbol1.ToTestDisplayString()); Assert.Equal(MethodKind.BuiltinOperator, symbol1.MethodKind); Assert.True(symbol1.IsImplicitlyDeclared); bool expectChecked = false; switch (op) { case UnaryOperatorKind.UnaryMinus: expectChecked = (type.IsDynamic() || symbol1.ContainingType.EnumUnderlyingType().SpecialType.IsIntegralType()); break; case UnaryOperatorKind.PrefixDecrement: case UnaryOperatorKind.PrefixIncrement: case UnaryOperatorKind.PostfixDecrement: case UnaryOperatorKind.PostfixIncrement: expectChecked = (type.IsDynamic() || type.IsPointerType() || symbol1.ContainingType.EnumUnderlyingType().SpecialType.IsIntegralType() || symbol1.ContainingType.SpecialType == SpecialType.System_Char); break; default: expectChecked = type.IsDynamic(); break; } Assert.Equal(expectChecked, 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.Same(symbol1.ContainingSymbol, symbol1.Parameters[0].Type); 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(1, symbol1.ParameterCount); Assert.Equal(0, symbol1.Parameters[0].Ordinal); var otherSymbol = (MethodSymbol)semanticModel.GetSymbolInfo(node1).Symbol; Assert.Equal(symbol1, otherSymbol); if (type.IsValueType && !type.IsPointerType()) { Assert.Equal(symbol1, symbol2); return; } Assert.Null(symbol2); }
internal override BoundStatement BindUsingStatementParts(DiagnosticBag diagnostics, Binder originalBinder) { ExpressionSyntax expressionSyntax = TargetExpressionSyntax; VariableDeclarationSyntax declarationSyntax = _syntax.Declaration; bool hasAwait = _syntax.AwaitKeyword.Kind() != default; Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither. TypeSymbol disposableInterface = getDisposableInterface(hasAwait); Debug.Assert((object)disposableInterface != null); bool hasErrors = ReportUseSiteDiagnostics(disposableInterface, diagnostics, hasAwait ? _syntax.AwaitKeyword : _syntax.UsingKeyword); Conversion iDisposableConversion = Conversion.NoConversion; BoundMultipleLocalDeclarations declarationsOpt = null; BoundExpression expressionOpt = null; AwaitableInfo awaitOpt = null; TypeSymbol declarationTypeOpt = null; if (expressionSyntax != null) { expressionOpt = this.BindTargetExpression(diagnostics, originalBinder); hasErrors |= !initConversion(fromExpression: true); } else { ImmutableArray <BoundLocalDeclaration> declarations; originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarations); Debug.Assert(!declarations.IsEmpty); declarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarations); declarationTypeOpt = declarations[0].DeclaredType.Type; if (declarationTypeOpt.IsDynamic()) { iDisposableConversion = Conversion.ImplicitDynamic; } else { hasErrors |= !initConversion(fromExpression: false); } } if (hasAwait) { TypeSymbol taskType = this.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask); hasErrors |= ReportUseSiteDiagnostics(taskType, diagnostics, _syntax.AwaitKeyword); var resource = (SyntaxNode)expressionSyntax ?? declarationSyntax; BoundExpression placeholder = new BoundAwaitableValuePlaceholder(resource, taskType).MakeCompilerGenerated(); awaitOpt = BindAwaitInfo(placeholder, resource, _syntax.AwaitKeyword.GetLocation(), diagnostics, ref hasErrors); } BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); Debug.Assert(GetDeclaredLocalsForScope(_syntax) == this.Locals); return(new BoundUsingStatement( _syntax, this.Locals, declarationsOpt, expressionOpt, iDisposableConversion, boundBody, awaitOpt, hasErrors)); bool initConversion(bool fromExpression) { HashSet <DiagnosticInfo> useSiteDiagnostics = null; iDisposableConversion = classifyConversion(fromExpression, disposableInterface, ref useSiteDiagnostics); diagnostics.Add(fromExpression ? (CSharpSyntaxNode)expressionSyntax : declarationSyntax, useSiteDiagnostics); if (iDisposableConversion.IsImplicit) { return(true); } TypeSymbol type = fromExpression ? expressionOpt.Type : declarationTypeOpt; if (type is null || !type.IsErrorType()) { // Retry with a different assumption about whether the `using` is async TypeSymbol alternateInterface = getDisposableInterface(!hasAwait); HashSet <DiagnosticInfo> ignored = null; Conversion alternateConversion = classifyConversion(fromExpression, alternateInterface, ref ignored); bool wrongAsync = alternateConversion.IsImplicit; ErrorCode errorCode = wrongAsync ? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync) : (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp); Error(diagnostics, errorCode, (CSharpSyntaxNode)declarationSyntax ?? expressionSyntax, declarationTypeOpt ?? expressionOpt.Display); } return(false); } Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref HashSet <DiagnosticInfo> diag) { return(fromExpression ? originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref diag) : originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref diag)); } TypeSymbol getDisposableInterface(bool isAsync) { return(isAsync ? this.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable) : this.Compilation.GetSpecialType(SpecialType.System_IDisposable)); } }
private bool ImplicitConversionExists(TypeSymbol source, TypeSymbol destination, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { // SPEC VIOLATION: For the purpose of algorithm in Fix method, dynamic type is not considered convertible to any other type, including object. if (source.IsDynamic() && !destination.IsDynamic()) { return false; } return _conversions.ClassifyImplicitConversionFromType(source, destination, ref useSiteDiagnostics).Exists; }
internal static bool TryCreate(SyntheticBoundNodeFactory F, MethodSymbol method, TypeMap typeMap, out AsyncMethodBuilderMemberCollection collection) { if (method.IsVoidReturningAsync()) { return(TryCreate( F: F, builderType: F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncVoidMethodBuilder), resultType: F.SpecialType(SpecialType.System_Void), setException: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__SetException, setResult: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__SetResult, awaitOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitOnCompleted, awaitUnsafeOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__AwaitUnsafeOnCompleted, start: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__Start_T, setStateMachine: WellKnownMember.System_Runtime_CompilerServices_AsyncVoidMethodBuilder__SetStateMachine, task: null, collection: out collection)); } if (method.IsTaskReturningAsync(F.Compilation)) { NamedTypeSymbol builderType = F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder); PropertySymbol task; if (!TryGetWellKnownPropertyAsMember(F, WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__Task, builderType, out task)) { collection = default(AsyncMethodBuilderMemberCollection); return(false); } return(TryCreate( F: F, builderType: F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder), resultType: F.SpecialType(SpecialType.System_Void), setException: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__SetException, setResult: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__SetResult, awaitOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__AwaitOnCompleted, awaitUnsafeOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__AwaitUnsafeOnCompleted, start: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__Start_T, setStateMachine: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder__SetStateMachine, task: task, collection: out collection)); } if (method.IsGenericTaskReturningAsync(F.Compilation)) { TypeSymbol resultType = method.ReturnType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single(); if (resultType.IsDynamic()) { resultType = F.SpecialType(SpecialType.System_Object); } if (typeMap != null) { resultType = typeMap.SubstituteType(resultType); } NamedTypeSymbol builderType = F.WellKnownType(WellKnownType.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T).Construct(resultType); PropertySymbol task; if (!TryGetWellKnownPropertyAsMember(F, WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__Task, builderType, out task)) { collection = default(AsyncMethodBuilderMemberCollection); return(false); } return(TryCreate( F: F, builderType: builderType, resultType: resultType, setException: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__SetException, setResult: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__SetResult, awaitOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__AwaitOnCompleted, awaitUnsafeOnCompleted: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__AwaitUnsafeOnCompleted, start: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__Start_T, setStateMachine: WellKnownMember.System_Runtime_CompilerServices_AsyncTaskMethodBuilder_T__SetStateMachine, task: task, collection: out collection)); } throw ExceptionUtilities.UnexpectedValue(method); }
// Determine if the conversion can actually overflow at runtime. If not, no need to generate a checked instruction. private static bool NeedsChecked(TypeSymbol source, TypeSymbol target) { Debug.Assert((object)target != null); if ((object)source == null) { return false; } if (source.IsDynamic()) { return true; } SpecialType sourceST = source.StrippedType().EnumUnderlyingType().SpecialType; SpecialType targetST = target.StrippedType().EnumUnderlyingType().SpecialType; // integral to double or float is never checked, but float/double to integral // may be checked. bool sourceIsNumeric = SpecialType.System_Char <= sourceST && sourceST <= SpecialType.System_Double; bool targetIsNumeric = SpecialType.System_Char <= targetST && targetST <= SpecialType.System_UInt64; return sourceIsNumeric && target.IsPointerType() || targetIsNumeric && source.IsPointerType() || sourceIsNumeric && targetIsNumeric && needsChecked[sourceST - SpecialType.System_Char, targetST - SpecialType.System_Char]; }
internal override BoundStatement BindUsingStatementParts(DiagnosticBag diagnostics, Binder originalBinder) { ExpressionSyntax expressionSyntax = TargetExpressionSyntax; VariableDeclarationSyntax declarationSyntax = _syntax.Declaration; Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither. bool hasErrors = false; BoundMultipleLocalDeclarations declarationsOpt = null; BoundExpression expressionOpt = null; Conversion iDisposableConversion = Conversion.NoConversion; TypeSymbol iDisposable = this.Compilation.GetSpecialType(SpecialType.System_IDisposable); // no need for diagnostics, so use the Compilation version Debug.Assert((object)iDisposable != null); if (expressionSyntax != null) { expressionOpt = this.BindTargetExpression(diagnostics, originalBinder); HashSet <DiagnosticInfo> useSiteDiagnostics = null; iDisposableConversion = originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, iDisposable, ref useSiteDiagnostics); diagnostics.Add(expressionSyntax, useSiteDiagnostics); if (!iDisposableConversion.IsImplicit) { TypeSymbol expressionType = expressionOpt.Type; if ((object)expressionType == null || !expressionType.IsErrorType()) { Error(diagnostics, ErrorCode.ERR_NoConvToIDisp, expressionSyntax, expressionOpt.Display); } hasErrors = true; } } else { ImmutableArray <BoundLocalDeclaration> declarations; originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarations); Debug.Assert(!declarations.IsEmpty); declarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarations); TypeSymbol declType = declarations[0].DeclaredType.Type; if (declType.IsDynamic()) { iDisposableConversion = Conversion.ImplicitDynamic; } else { HashSet <DiagnosticInfo> useSiteDiagnostics = null; iDisposableConversion = originalBinder.Conversions.ClassifyImplicitConversionFromType(declType, iDisposable, ref useSiteDiagnostics); diagnostics.Add(declarationSyntax, useSiteDiagnostics); if (!iDisposableConversion.IsImplicit) { if (!declType.IsErrorType()) { Error(diagnostics, ErrorCode.ERR_NoConvToIDisp, declarationSyntax, declType); } hasErrors = true; } } } BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); Debug.Assert(GetDeclaredLocalsForScope(_syntax) == this.Locals); return(new BoundUsingStatement( _syntax, this.Locals, declarationsOpt, expressionOpt, iDisposableConversion, boundBody, hasErrors)); }
/// <summary> /// If the extension method is applicable based on the "this" argument type, return /// the method constructed with the inferred type arguments. If the method is not an /// unconstructed generic method, type inference is skipped. If the method is not /// applicable, or if constraints when inferring type parameters from the "this" type /// are not satisfied, the return value is null. /// </summary> public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol method, TypeSymbol thisType, Compilation compilation, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(method.IsExtensionMethod); Debug.Assert((object)thisType != null); if (!method.IsGenericMethod || method != method.ConstructedFrom) { return method; } // We never resolve extension methods on a dynamic receiver. if (thisType.IsDynamic()) { return null; } var containingAssembly = method.ContainingAssembly; var errorNamespace = containingAssembly.GlobalNamespace; var conversions = new TypeConversions(containingAssembly.CorLibrary); // There is absolutely no plausible syntax/tree that we could use for these // synthesized literals. We could be speculatively binding a call to a PE method. var syntaxTree = CSharpSyntaxTree.Dummy; var syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); // Create an argument value for the "this" argument of specific type, // and pass the same bad argument value for all other arguments. var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType) { WasCompilerGenerated = true }; var otherArgumentType = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false); var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType) { WasCompilerGenerated = true }; var paramCount = method.ParameterCount; var arguments = new BoundExpression[paramCount]; var argumentTypes = new TypeSymbol[paramCount]; for (int i = 0; i < paramCount; i++) { var argument = (i == 0) ? thisArgumentValue : otherArgumentValue; arguments[i] = argument; argumentTypes[i] = argument.Type; } var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument( conversions, method, argumentTypes.AsImmutableOrNull(), arguments.AsImmutableOrNull(), ref useSiteDiagnostics); if (typeArgs.IsDefault) { return null; } // Check constraints. var diagnosticsBuilder = ArrayBuilder<TypeParameterDiagnosticInfo>.GetInstance(); var typeParams = method.TypeParameters; var substitution = new TypeMap(typeParams, typeArgs); ArrayBuilder<TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null; var success = method.CheckConstraints(conversions, substitution, method.TypeParameters, typeArgs, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); diagnosticsBuilder.Free(); if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0) { if (useSiteDiagnostics == null) { useSiteDiagnostics = new HashSet<DiagnosticInfo>(); } foreach (var diag in useSiteDiagnosticsBuilder) { useSiteDiagnostics.Add(diag.DiagnosticInfo); } } if (!success) { return null; } return method.Construct(typeArgs); }
private TupleBinaryOperatorInfo BindTupleBinaryOperatorInfo(BinaryExpressionSyntax node, BinaryOperatorKind kind, BoundExpression left, BoundExpression right, BindingDiagnosticBag diagnostics) { TypeSymbol leftType = left.Type; TypeSymbol rightType = right.Type; if ((object)leftType != null && leftType.IsDynamic() || (object)rightType != null && rightType.IsDynamic()) { return(BindTupleDynamicBinaryOperatorSingleInfo(node, kind, left, right, diagnostics)); } if (IsTupleBinaryOperation(left, right)) { return(BindTupleBinaryOperatorNestedInfo(node, kind, left, right, diagnostics)); } BoundExpression comparison = BindSimpleBinaryOperator(node, diagnostics, left, right); switch (comparison) { case BoundLiteral _: // this case handles `null == null` and the like return(new TupleBinaryOperatorInfo.NullNull(kind)); case BoundBinaryOperator binary: PrepareBoolConversionAndTruthOperator(binary.Type, node, kind, diagnostics, out Conversion conversionIntoBoolOperator, out UnaryOperatorSignature boolOperator); CheckConstraintLanguageVersionAndRuntimeSupportForOperator(node, boolOperator.Method, boolOperator.ConstrainedToTypeOpt, diagnostics); return(new TupleBinaryOperatorInfo.Single(binary.Left.Type, binary.Right.Type, binary.OperatorKind, binary.MethodOpt, binary.ConstrainedToTypeOpt, conversionIntoBoolOperator, boolOperator)); default: throw ExceptionUtilities.UnexpectedValue(comparison); } }
private BoundExpression MakeUnaryOperator( BoundUnaryOperator oldNode, UnaryOperatorKind kind, CSharpSyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { if (kind.IsDynamic()) { Debug.Assert(kind == UnaryOperatorKind.DynamicTrue && type.SpecialType == SpecialType.System_Boolean || type.IsDynamic()); Debug.Assert((object)method == null); // Logical operators on boxed Boolean constants: var constant = UnboxConstant(loweredOperand); if (constant == ConstantValue.True || constant == ConstantValue.False) { if (kind == UnaryOperatorKind.DynamicTrue) { return _factory.Literal(constant.BooleanValue); } else if (kind == UnaryOperatorKind.DynamicLogicalNegation) { return MakeConversionNode(_factory.Literal(!constant.BooleanValue), type, @checked: false); } } return _dynamicFactory.MakeDynamicUnaryOperator(kind, loweredOperand, type).ToExpression(); } else if (kind.IsLifted()) { if (!_inExpressionLambda) { return LowerLiftedUnaryOperator(kind, syntax, method, loweredOperand, type); } } else if (kind.IsUserDefined()) { Debug.Assert((object)method != null); Debug.Assert(type == method.ReturnType); if (!_inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse) { return BoundCall.Synthesized(syntax, null, method, loweredOperand); } } else if (kind.Operator() == UnaryOperatorKind.UnaryPlus) { // We do not call the operator even for decimal; we simply optimize it away entirely. return loweredOperand; } if (kind == UnaryOperatorKind.EnumBitwiseComplement) { var underlyingType = loweredOperand.Type.GetEnumUnderlyingType(); var upconvertSpecialType = Binder.GetEnumPromotedType(underlyingType.SpecialType); var upconvertType = upconvertSpecialType == underlyingType.SpecialType ? underlyingType : _compilation.GetSpecialType(upconvertSpecialType); var newOperand = MakeConversionNode(loweredOperand, upconvertType, false); UnaryOperatorKind newKind = kind.Operator().WithType(upconvertSpecialType); var newNode = (oldNode != null) ? oldNode.Update( newKind, newOperand, oldNode.ConstantValueOpt, method, newOperand.ResultKind, upconvertType) : new BoundUnaryOperator( syntax, newKind, newOperand, null, method, LookupResultKind.Viable, upconvertType); return MakeConversionNode(newNode.Syntax, newNode, Conversion.ExplicitEnumeration, type, @checked: false); } if (kind == UnaryOperatorKind.DecimalUnaryMinus) { method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation); if (!_inExpressionLambda) { return BoundCall.Synthesized(syntax, null, method, loweredOperand); } } return (oldNode != null) ? oldNode.Update(kind, loweredOperand, oldNode.ConstantValueOpt, method, oldNode.ResultKind, type) : new BoundUnaryOperator(syntax, kind, loweredOperand, null, method, LookupResultKind.Viable, type); }
/// <summary> /// The spec describes an algorithm for finding the following types: /// 1) Collection type /// 2) Enumerator type /// 3) Element type /// /// The implementation details are a bit difference. If we're iterating over a string or an array, then we don't need to record anything /// but the inferredType (in case the iteration variable is implicitly typed). If we're iterating over anything else, then we want the /// inferred type plus a ForEachEnumeratorInfo.Builder with: /// 1) Collection type /// 2) Element type /// 3) GetEnumerator method of the collection type (return type will be the enumerator type from the spec) /// 4) Current property of the enumerator type /// 5) MoveNext method of the enumerator type /// /// The caller will have to do some extra conversion checks before creating a ForEachEnumeratorInfo for the BoundForEachStatement. /// </summary> /// <param name="builder">Builder to fill in (partially, all but conversions).</param> /// <param name="collectionExpr">The expression over which to iterate.</param> /// <param name="diagnostics">Populated with binding diagnostics.</param> /// <returns>Partially populated (all but conversions) or null if there was an error.</returns> private bool GetEnumeratorInfo(ref ForEachEnumeratorInfo.Builder builder, BoundExpression collectionExpr, DiagnosticBag diagnostics) { TypeSymbol collectionExprType = collectionExpr.Type; if (collectionExpr.ConstantValue != null && collectionExpr.ConstantValue.IsNull) { // Spec seems to refer to null literals, but Dev10 reports anything known to be null. Debug.Assert(collectionExpr.ConstantValue.IsNull); // only constant value with no type diagnostics.Add(ErrorCode.ERR_NullNotValid, syntax.Expression.Location); return(false); } if ((object)collectionExprType == null) // There's no way to enumerate something without a type. { // The null literal was caught above, so anything else with a null type is a method group or anonymous function diagnostics.Add(ErrorCode.ERR_AnonMethGrpInForEach, syntax.Expression.Location, collectionExpr.Display); // CONSIDER: dev10 also reports ERR_ForEachMissingMember (i.e. failed pattern match). return(false); } if (collectionExpr.ResultKind == LookupResultKind.NotAValue) { // Short-circuiting to prevent strange behavior in the case where the collection // expression is a type expression and the type is enumerable. Debug.Assert(collectionExpr.HasAnyErrors); // should already have been reported return(false); } // The spec specifically lists the collection, enumerator, and element types for arrays and dynamic. if (collectionExprType.Kind == SymbolKind.ArrayType || collectionExprType.Kind == SymbolKind.DynamicType) { // NOTE: for arrays, we won't actually use any of these members - they're just for the API. builder.CollectionType = GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, this.syntax); builder.ElementType = collectionExprType.IsDynamic() ? (this.syntax.Type.IsVar ? (TypeSymbol)DynamicTypeSymbol.Instance : GetSpecialType(SpecialType.System_Object, diagnostics, this.syntax)) : ((ArrayTypeSymbol)collectionExprType).ElementType; // CONSIDER: // For arrays none of these members will actually be emitted, so it seems strange to prevent compilation if they can't be found. // skip this work in the batch case? (If so, also special case string, which won't use the pattern methods.) builder.GetEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, this.syntax); builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, this.syntax); builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, this.syntax); Debug.Assert((object)builder.GetEnumeratorMethod == null || builder.GetEnumeratorMethod.ReturnType == this.Compilation.GetSpecialType(SpecialType.System_Collections_IEnumerator)); // We don't know the runtime type, so we will have to insert a runtime check for IDisposable (with a conditional call to IDisposable.Dispose). builder.NeedsDisposeMethod = true; return(true); } bool foundMultipleGenericIEnumerableInterfaces; if (SatisfiesGetEnumeratorPattern(ref builder, collectionExprType, diagnostics)) { Debug.Assert((object)builder.GetEnumeratorMethod != null); builder.CollectionType = collectionExprType; if (SatisfiesForEachPattern(ref builder, diagnostics)) { builder.ElementType = ((PropertySymbol)builder.CurrentPropertyGetter.AssociatedSymbol).Type; // NOTE: if IDisposable is not available at all, no diagnostics will be reported - we will just assume that // the enumerator is not disposable. If it has IDisposable in its interface list, there will be a diagnostic there. // If IDisposable is available but its Dispose method is not, then diagnostics will be reported only if the enumerator // is potentially disposable. var useSiteDiagnosticBag = DiagnosticBag.GetInstance(); TypeSymbol enumeratorType = builder.GetEnumeratorMethod.ReturnType; HashSet <DiagnosticInfo> useSiteDiagnostics = null; if (!enumeratorType.IsSealed || this.Conversions.ClassifyImplicitConversion(enumeratorType, this.Compilation.GetSpecialType(SpecialType.System_IDisposable), ref useSiteDiagnostics).IsImplicit) { builder.NeedsDisposeMethod = true; diagnostics.AddRange(useSiteDiagnosticBag); } useSiteDiagnosticBag.Free(); diagnostics.Add(this.syntax, useSiteDiagnostics); return(true); } MethodSymbol getEnumeratorMethod = builder.GetEnumeratorMethod; diagnostics.Add(ErrorCode.ERR_BadGetEnumerator, this.syntax.Expression.Location, getEnumeratorMethod.ReturnType, getEnumeratorMethod); return(false); } if (IsIEnumerable(collectionExprType)) { // This indicates a problem with the special IEnumerable type - it should have satisfied the GetEnumerator pattern. diagnostics.Add(ErrorCode.ERR_ForEachMissingMember, syntax.Expression.Location, collectionExprType, GetEnumeratorMethodName); return(false); } if (AllInterfacesContainsIEnumerable(ref builder, collectionExprType, diagnostics, out foundMultipleGenericIEnumerableInterfaces)) { CSharpSyntaxNode errorLocationSyntax = this.syntax.Expression; if (foundMultipleGenericIEnumerableInterfaces) { diagnostics.Add(ErrorCode.ERR_MultipleIEnumOfT, errorLocationSyntax.Location, collectionExprType, this.Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T)); return(false); } Debug.Assert((object)builder.CollectionType != null); NamedTypeSymbol collectionType = (NamedTypeSymbol)builder.CollectionType; if (collectionType.IsGenericType) { // If the type is generic, we have to search for the methods Debug.Assert(collectionType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T); builder.ElementType = collectionType.TypeArgumentsNoUseSiteDiagnostics.Single(); MethodSymbol getEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, diagnostics, errorLocationSyntax); if ((object)getEnumeratorMethod != null) { builder.GetEnumeratorMethod = getEnumeratorMethod.AsMember(collectionType); TypeSymbol enumeratorType = builder.GetEnumeratorMethod.ReturnType; Debug.Assert(enumeratorType.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerator_T); MethodSymbol currentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IEnumerator_T__get_Current, diagnostics, errorLocationSyntax); if ((object)currentPropertyGetter != null) { builder.CurrentPropertyGetter = currentPropertyGetter.AsMember((NamedTypeSymbol)enumeratorType); } } builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax); // NOTE: MoveNext is actually inherited from System.Collections.IEnumerator } else { // Non-generic - use special members to avoid re-computing Debug.Assert(collectionType.SpecialType == SpecialType.System_Collections_IEnumerable); builder.ElementType = GetSpecialType(SpecialType.System_Object, diagnostics, errorLocationSyntax); builder.GetEnumeratorMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerable__GetEnumerator, diagnostics, errorLocationSyntax); builder.CurrentPropertyGetter = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__get_Current, diagnostics, errorLocationSyntax); builder.MoveNextMethod = (MethodSymbol)GetSpecialTypeMember(SpecialMember.System_Collections_IEnumerator__MoveNext, diagnostics, errorLocationSyntax); Debug.Assert((object)builder.GetEnumeratorMethod == null || builder.GetEnumeratorMethod.ReturnType == GetSpecialType(SpecialType.System_Collections_IEnumerator, diagnostics, errorLocationSyntax)); } // We don't know the runtime type, so we will have to insert a runtime check for IDisposable (with a conditional call to IDisposable.Dispose). builder.NeedsDisposeMethod = true; return(true); } if (!string.IsNullOrEmpty(collectionExprType.Name) || !collectionExpr.HasErrors) { diagnostics.Add(ErrorCode.ERR_ForEachMissingMember, syntax.Expression.Location, collectionExprType.ToDisplayString(), GetEnumeratorMethodName); } return(false); }
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { var expression = (BoundExpression)Visit(node.Expression); resultPlace = (BoundExpression)Visit(resultPlace); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); LocalSymbol awaiterTemp; if ((object)getResult == null) { awaiterTemp = F.SynthesizedLocal(DynamicTypeSymbol.Instance); } else if (type.IsDynamic()) { var awaiterType = ((NamedTypeSymbol)getAwaiter.ReturnType).OriginalDefinition.Construct(F.SpecialType(SpecialType.System_Object)); awaiterTemp = F.SynthesizedLocal(awaiterType); getResult = getResult.OriginalDefinition.AsMember(awaiterType); isCompletedMethod = isCompletedMethod.OriginalDefinition.AsMember(awaiterType); } else { awaiterTemp = F.SynthesizedLocal(getAwaiter.ReturnType); } var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, WellKnownMemberNames.GetResult, resultsDiscarded: resultPlace == null); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); if (resultPlace != null && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type); return(F.Block( ImmutableArray.Create(awaiterTemp, resultTemp), awaitIfIncomplete, F.Assignment(F.Local(resultTemp), getResultCall), F.ExpressionStatement(nullAwaiter), F.Assignment(resultPlace, F.Local(resultTemp)))); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; return(F.Block( ImmutableArray.Create(awaiterTemp), awaitIfIncomplete, F.ExpressionStatement(getResultCall), F.ExpressionStatement(nullAwaiter))); } }
/// <summary> /// If the extension method is applicable based on the "this" argument type, return /// the method constructed with the inferred type arguments. If the method is not an /// unconstructed generic method, type inference is skipped. If the method is not /// applicable, or if constraints when inferring type parameters from the "this" type /// are not satisfied, the return value is null. /// </summary> public static MethodSymbol InferExtensionMethodTypeArguments(this MethodSymbol method, TypeSymbol thisType, Compilation compilation, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(method.IsExtensionMethod); Debug.Assert((object)thisType != null); if (!method.IsGenericMethod || method != method.ConstructedFrom) { return(method); } // We never resolve extension methods on a dynamic receiver. if (thisType.IsDynamic()) { return(null); } var containingAssembly = method.ContainingAssembly; var errorNamespace = containingAssembly.GlobalNamespace; var conversions = new TypeConversions(containingAssembly.CorLibrary); // There is absolutely no plausible syntax/tree that we could use for these // synthesized literals. We could be speculatively binding a call to a PE method. var syntaxTree = CSharpSyntaxTree.Dummy; var syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); // Create an argument value for the "this" argument of specific type, // and pass the same bad argument value for all other arguments. var thisArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, thisType) { WasCompilerGenerated = true }; var otherArgumentType = new ExtendedErrorTypeSymbol(errorNamespace, name: string.Empty, arity: 0, errorInfo: null, unreported: false); var otherArgumentValue = new BoundLiteral(syntax, ConstantValue.Bad, otherArgumentType) { WasCompilerGenerated = true }; var paramCount = method.ParameterCount; var arguments = new BoundExpression[paramCount]; var argumentTypes = new TypeSymbol[paramCount]; for (int i = 0; i < paramCount; i++) { var argument = (i == 0) ? thisArgumentValue : otherArgumentValue; arguments[i] = argument; argumentTypes[i] = argument.Type; } var typeArgs = MethodTypeInferrer.InferTypeArgumentsFromFirstArgument( conversions, method, argumentTypes.AsImmutableOrNull(), arguments.AsImmutableOrNull(), ref useSiteDiagnostics); if (typeArgs.IsDefault) { return(null); } // Check constraints. var diagnosticsBuilder = ArrayBuilder <TypeParameterDiagnosticInfo> .GetInstance(); var typeParams = method.TypeParameters; var substitution = new TypeMap(typeParams, typeArgs); ArrayBuilder <TypeParameterDiagnosticInfo> useSiteDiagnosticsBuilder = null; var success = method.CheckConstraints(conversions, substitution, method.TypeParameters, typeArgs, compilation, diagnosticsBuilder, ref useSiteDiagnosticsBuilder); diagnosticsBuilder.Free(); if (useSiteDiagnosticsBuilder != null && useSiteDiagnosticsBuilder.Count > 0) { if (useSiteDiagnostics == null) { useSiteDiagnostics = new HashSet <DiagnosticInfo>(); } foreach (var diag in useSiteDiagnosticsBuilder) { useSiteDiagnostics.Add(diag.DiagnosticInfo); } } if (!success) { return(null); } return(method.Construct(typeArgs)); }