private bool GetEnumeratorInfoAndInferCollectionElementType(ref ForEachEnumeratorInfo.Builder builder, ref BoundExpression collectionExpr, DiagnosticBag diagnostics, out TypeSymbol inferredType) { UnwrapCollectionExpressionIfNullable(ref collectionExpr, diagnostics); bool gotInfo = GetEnumeratorInfo(ref builder, collectionExpr, diagnostics); if (!gotInfo) { inferredType = null; } else if (collectionExpr.HasDynamicType()) { // If the enumerator is dynamic, it yields dynamic values inferredType = DynamicTypeSymbol.Instance; } else if (collectionExpr.Type.SpecialType == SpecialType.System_String && builder.CollectionType.SpecialType == SpecialType.System_Collections_IEnumerable) { // Reproduce dev11 behavior: we're always going to lower a foreach loop over a string to a for loop // over the string's Chars indexer. Therefore, we should infer "char", regardless of what the spec // indicates the element type is. This actually matters in practice because the System.String in // the portable library doesn't have a pattern GetEnumerator method or implement IEnumerable<char>. inferredType = GetSpecialType(SpecialType.System_Char, diagnostics, collectionExpr.Syntax); } else { inferredType = builder.ElementType; } return(gotInfo); }
/// <summary> /// Finds and validates the required members of an awaitable expression, as described in spec 7.7.7.1. /// </summary> /// <returns>True if the expression is awaitable; false otherwise.</returns> private bool GetAwaitableExpressionInfo( BoundExpression expression, out MethodSymbol getAwaiter, out PropertySymbol isCompleted, out MethodSymbol getResult, CSharpSyntaxNode node, DiagnosticBag diagnostics) { getAwaiter = null; isCompleted = null; getResult = null; if (!ValidateAwaitedExpression(expression, node, diagnostics)) { return(false); } if (expression.HasDynamicType()) { return(true); } if (!GetGetAwaiterMethod(expression, node, diagnostics, out getAwaiter)) { return(false); } TypeSymbol awaiterType = getAwaiter.ReturnType; return(GetIsCompletedProperty(awaiterType, node, expression.Type, diagnostics, out isCompleted) && AwaiterImplementsINotifyCompletion(awaiterType, node, diagnostics) && GetGetResultMethod(awaiterType, node, expression.Type, diagnostics, out getResult)); }
/// <summary> /// Finds and validates the required members of an awaitable expression, as described in spec 7.7.7.1. Returns /// true if the expression is awaitable. /// </summary> /// <returns>True if the expression contains errors.</returns> private bool GetAwaitableExpressionInfo( BoundExpression expression, ref MethodSymbol getAwaiter, ref PropertySymbol isCompleted, ref MethodSymbol getResult, CSharpSyntaxNode node, DiagnosticBag diagnostics) { if (!ValidateAwaitedExpression(expression, node, diagnostics)) { return(true); } if (expression.HasDynamicType()) { return(false); } if (!GetGetAwaiterMethod(expression, node, diagnostics, out getAwaiter) || !ValidateGetAwaiter(getAwaiter, node, diagnostics, expression)) { return(true); } TypeSymbol awaiterType = getAwaiter.ReturnType; if (!GetIsCompletedProperty(awaiterType, node, diagnostics, out isCompleted) || !ValidateIsCompleted(isCompleted, node, diagnostics, awaiterType, expression.Type)) { return(true); } if (!AwaiterImplementsINotifyCompletion(awaiterType, node, diagnostics)) { return(true); } if (!GetGetResultMethod(awaiterType, node, diagnostics, out getResult) || !ValidateGetResult(getResult, node, diagnostics, awaiterType, expression.Type)) { return(true); } return(false); }
private bool GetAwaitableExpressionInfo( BoundExpression expression, BoundExpression getAwaiterArgument, out bool isDynamic, out BoundExpression?getAwaiter, out PropertySymbol?isCompleted, out MethodSymbol?getResult, out BoundExpression?getAwaiterGetResultCall, SyntaxNode node, DiagnosticBag diagnostics) { Debug.Assert(TypeSymbol.Equals(expression.Type, getAwaiterArgument.Type, TypeCompareKind.ConsiderEverything)); isDynamic = false; getAwaiter = null; isCompleted = null; getResult = null; getAwaiterGetResultCall = null; if (!ValidateAwaitedExpression(expression, node, diagnostics)) { return(false); } if (expression.HasDynamicType()) { isDynamic = true; return(true); } if (!GetGetAwaiterMethod(getAwaiterArgument, node, diagnostics, out getAwaiter)) { return(false); } TypeSymbol awaiterType = getAwaiter.Type !; return(GetIsCompletedProperty(awaiterType, node, expression.Type !, diagnostics, out isCompleted) && AwaiterImplementsINotifyCompletion(awaiterType, node, diagnostics) && GetGetResultMethod(getAwaiter, node, expression.Type !, diagnostics, out getResult, out getAwaiterGetResultCall)); }
private BoundExpression MakeTruthTestForDynamicLogicalOperator(CSharpSyntaxNode syntax, BoundExpression loweredLeft, TypeSymbol boolean, MethodSymbol leftTruthOperator, bool negative) { if (loweredLeft.HasDynamicType()) { Debug.Assert(leftTruthOperator == null); return _dynamicFactory.MakeDynamicUnaryOperator(negative ? UnaryOperatorKind.DynamicFalse : UnaryOperatorKind.DynamicTrue, loweredLeft, boolean).ToExpression(); } // Although the spec doesn't capture it we do the same that Dev11 does: // Use implicit conversion to Boolean if it is defined on the static type of the left operand. // If not the type has to implement IsTrue/IsFalse operator - we checked it during binding. HashSet<DiagnosticInfo> useSiteDiagnostics = null; var conversion = _compilation.Conversions.ClassifyConversionFromExpression(loweredLeft, boolean, ref useSiteDiagnostics); _diagnostics.Add(loweredLeft.Syntax, useSiteDiagnostics); if (conversion.IsImplicit) { Debug.Assert(leftTruthOperator == null); var converted = MakeConversionNode(loweredLeft, boolean, @checked: false); if (negative) { return new BoundUnaryOperator(syntax, UnaryOperatorKind.BoolLogicalNegation, converted, ConstantValue.NotAvailable, MethodSymbol.None, LookupResultKind.Viable, boolean) { WasCompilerGenerated = true }; } else { return converted; } } Debug.Assert(leftTruthOperator != null); return BoundCall.Synthesized(syntax, null, leftTruthOperator, loweredLeft); }
private BoundExpression BindConditionalLogicalOperator(BinaryExpressionSyntax node, BoundExpression left, BoundExpression right, DiagnosticBag diagnostics) { BinaryOperatorKind kind = SyntaxKindToBinaryOperatorKind(node.Kind()); Debug.Assert(kind == BinaryOperatorKind.LogicalAnd || kind == BinaryOperatorKind.LogicalOr); // If either operand is bad, don't try to do binary operator overload resolution; that will just // make cascading errors. if (left.HasAnyErrors || right.HasAnyErrors) { // NOTE: no candidate user-defined operators. return new BoundBinaryOperator(node, kind, left, right, ConstantValue.NotAvailable, methodOpt: null, resultKind: LookupResultKind.Empty, type: GetBinaryOperatorErrorType(kind, diagnostics, node), hasErrors: true); } // Let's take an easy out here. The vast majority of the time the operands will // both be bool. This is the only situation in which the expression can be a // constant expression, so do the folding now if we can. if ((object)left.Type != null && left.Type.SpecialType == SpecialType.System_Boolean && (object)right.Type != null && right.Type.SpecialType == SpecialType.System_Boolean) { var constantValue = FoldBinaryOperator(node, kind | BinaryOperatorKind.Bool, left, right, SpecialType.System_Boolean, diagnostics); // NOTE: no candidate user-defined operators. return new BoundBinaryOperator(node, kind | BinaryOperatorKind.Bool, left, right, constantValue, methodOpt: null, resultKind: LookupResultKind.Viable, type: left.Type, hasErrors: constantValue != null && constantValue.IsBad); } if (left.HasDynamicType() || right.HasDynamicType()) { return BindDynamicBinaryOperator(node, kind, left, right, diagnostics); } LookupResultKind lookupResult; ImmutableArray<MethodSymbol> originalUserDefinedOperators; var best = this.BinaryOperatorOverloadResolution(kind, left, right, node, diagnostics, out lookupResult, out originalUserDefinedOperators); // SPEC: If overload resolution fails to find a single best operator, or if overload // SPEC: resolution selects one of the predefined integer logical operators, a binding- // SPEC: time error occurs. // // SPEC OMISSION: We should probably clarify that the enum logical operators count as // SPEC OMISSION: integer logical operators. Basically the rule here should actually be: // SPEC OMISSION: if overload resolution selects something other than a user-defined // SPEC OMISSION: operator or the built in not-lifted operator on bool, an error occurs. // if (!best.HasValue) { ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, lookupResult); } else { // There are two non-error possibilities. Either both operands are implicitly convertible to // bool, or we've got a valid user-defined operator. BinaryOperatorSignature signature = best.Signature; bool bothBool = signature.LeftType.SpecialType == SpecialType.System_Boolean && signature.RightType.SpecialType == SpecialType.System_Boolean; MethodSymbol trueOperator = null, falseOperator = null; if (!bothBool && !signature.Kind.IsUserDefined()) { ReportBinaryOperatorError(node, diagnostics, node.OperatorToken, left, right, lookupResult); } else if (bothBool || IsValidUserDefinedConditionalLogicalOperator(node, signature, diagnostics, out trueOperator, out falseOperator)) { var resultLeft = CreateConversion(left, best.LeftConversion, signature.LeftType, diagnostics); var resultRight = CreateConversion(right, best.RightConversion, signature.RightType, diagnostics); var resultKind = kind | signature.Kind.OperandTypes(); if (signature.Kind.IsLifted()) { resultKind |= BinaryOperatorKind.Lifted; } if (resultKind.IsUserDefined()) { Debug.Assert(trueOperator != null && falseOperator != null); return new BoundUserDefinedConditionalLogicalOperator( node, resultKind, resultLeft, resultRight, signature.Method, trueOperator, falseOperator, lookupResult, originalUserDefinedOperators, signature.ReturnType); } else { Debug.Assert(bothBool); return new BoundBinaryOperator( node, resultKind, resultLeft, resultRight, ConstantValue.NotAvailable, signature.Method, lookupResult, originalUserDefinedOperators, signature.ReturnType); } } } // We've already reported the error. return new BoundBinaryOperator(node, kind, left, right, ConstantValue.NotAvailable, null, lookupResult, originalUserDefinedOperators, CreateErrorType(), true); }
/// <summary> /// For "receiver.event += expr", produce "receiver.add_event(expr)". /// For "receiver.event -= expr", produce "receiver.remove_event(expr)". /// </summary> /// <remarks> /// Performs some validation of the accessor that couldn't be done in CheckEventValueKind, because /// the specific accessor wasn't known. /// </remarks> private BoundExpression BindEventAssignment(AssignmentExpressionSyntax node, BoundEventAccess left, BoundExpression right, BinaryOperatorKind opKind, DiagnosticBag diagnostics) { Debug.Assert(opKind == BinaryOperatorKind.Addition || opKind == BinaryOperatorKind.Subtraction); bool hasErrors = false; EventSymbol eventSymbol = left.EventSymbol; BoundExpression receiverOpt = left.ReceiverOpt; TypeSymbol delegateType = left.Type; HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion argumentConversion = this.Conversions.ClassifyConversionFromExpression(right, delegateType, ref useSiteDiagnostics); if (!argumentConversion.IsImplicit || !argumentConversion.IsValid) // NOTE: dev10 appears to allow user-defined conversions here. { hasErrors = true; if (delegateType.IsDelegateType()) // Otherwise, suppress cascading. { GenerateImplicitConversionError(diagnostics, node, argumentConversion, right, delegateType); } } BoundExpression argument = CreateConversion(right, argumentConversion, delegateType, diagnostics); bool isAddition = opKind == BinaryOperatorKind.Addition; MethodSymbol method = isAddition ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; TypeSymbol type; if ((object)method == null) { type = this.GetSpecialType(SpecialType.System_Void, diagnostics, node); //we know the return type would have been void // There will be a diagnostic on the declaration if it is from source. if (!eventSymbol.OriginalDefinition.IsFromCompilation(this.Compilation)) { // CONSIDER: better error code? ERR_EventNeedsBothAccessors? Error(diagnostics, ErrorCode.ERR_MissingPredefinedMember, node, delegateType, SourceEventSymbol.GetAccessorName(eventSymbol.Name, isAddition)); } } else if (eventSymbol.IsWindowsRuntimeEvent) { // Return type is actually void because this call will be later encapsulated in a call // to WindowsRuntimeMarshal.AddEventHandler or RemoveEventHandler, which has the return // type of void. type = this.GetSpecialType(SpecialType.System_Void, diagnostics, node); } else { type = method.ReturnType; if (!this.IsAccessible(method, ref useSiteDiagnostics, this.GetAccessThroughType(receiverOpt))) { // CONSIDER: depending on the accessibility (e.g. if it's private), dev10 might just report the whole event bogus. Error(diagnostics, ErrorCode.ERR_BadAccess, node, method); hasErrors = true; } } diagnostics.Add(node, useSiteDiagnostics); return new BoundEventAssignmentOperator( syntax: node, @event: eventSymbol, isAddition: isAddition, isDynamic: right.HasDynamicType(), receiverOpt: receiverOpt, argument: argument, type: type, hasErrors: hasErrors); }
private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string operatorText, BoundExpression operand, DiagnosticBag diagnostics) { UnaryOperatorKind kind = SyntaxKindToUnaryOperatorKind(node.Kind()); bool isOperandTypeNull = (object)operand.Type == null; if (isOperandTypeNull) { // Dev10 does not allow unary prefix operators to be applied to the null literal // (or other typeless expressions). Error(diagnostics, ErrorCode.ERR_BadUnaryOp, node, operatorText, operand.Display); } // If the operand is bad, avoid generating cascading errors. if (operand.HasAnyErrors || isOperandTypeNull) { // Note: no candidate user-defined operators. return new BoundUnaryOperator(node, kind, operand, ConstantValue.NotAvailable, methodOpt: null, resultKind: LookupResultKind.Empty, type: GetSpecialType(SpecialType.System_Object, diagnostics, node), hasErrors: true); } // If the operand is dynamic then we do not attempt to do overload resolution at compile // time; we defer that until runtime. If we did overload resolution then the dynamic // operand would be implicitly convertible to the parameter type of each operator // signature, and therefore every operator would be an applicable candidate. Instead // of changing overload resolution to handle dynamic, we just handle it here and let // overload resolution implement the specification. if (operand.HasDynamicType()) { return new BoundUnaryOperator( syntax: node, operatorKind: kind.WithType(UnaryOperatorKind.Dynamic).WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), operand: operand, constantValueOpt: ConstantValue.NotAvailable, methodOpt: null, resultKind: LookupResultKind.Viable, type: operand.Type); } LookupResultKind resultKind; ImmutableArray<MethodSymbol> originalUserDefinedOperators; var best = this.UnaryOperatorOverloadResolution(kind, operand, node, diagnostics, out resultKind, out originalUserDefinedOperators); if (!best.HasValue) { ReportUnaryOperatorError(node, diagnostics, operatorText, operand, resultKind); return new BoundUnaryOperator( node, kind, operand, ConstantValue.NotAvailable, null, resultKind, originalUserDefinedOperators, GetSpecialType(SpecialType.System_Object, diagnostics, node), hasErrors: true); } var signature = best.Signature; var resultOperand = CreateConversion(operand.Syntax, operand, best.Conversion, false, signature.OperandType, diagnostics); var resultType = signature.ReturnType; UnaryOperatorKind resultOperatorKind = signature.Kind; var resultMethod = signature.Method; var resultConstant = FoldUnaryOperator(node, resultOperatorKind, resultOperand, resultType.SpecialType, diagnostics); return new BoundUnaryOperator( node, resultOperatorKind.WithOverflowChecksIfApplicable(CheckOverflowAtRuntime), resultOperand, resultConstant, resultMethod, resultKind, resultType); }
/// <summary> /// Finds and validates the required members of an awaitable expression, as described in spec 7.7.7.1. /// </summary> /// <returns>True if the expression is awaitable; false otherwise.</returns> private bool GetAwaitableExpressionInfo( BoundExpression expression, ref MethodSymbol getAwaiter, ref PropertySymbol isCompleted, ref MethodSymbol getResult, CSharpSyntaxNode node, DiagnosticBag diagnostics) { if (!ValidateAwaitedExpression(expression, node, diagnostics)) { return false; } if (expression.HasDynamicType()) { return true; } if (!GetGetAwaiterMethod(expression, node, diagnostics, out getAwaiter)) { return false; } TypeSymbol awaiterType = getAwaiter.ReturnType; return GetIsCompletedProperty(awaiterType, node, expression.Type, diagnostics, out isCompleted) && AwaiterImplementsINotifyCompletion(awaiterType, node, diagnostics) && GetGetResultMethod(awaiterType, node, expression.Type, diagnostics, out getResult); }
private bool GetEnumeratorInfoAndInferCollectionElementType(ref ForEachEnumeratorInfo.Builder builder, ref BoundExpression collectionExpr, DiagnosticBag diagnostics, out TypeSymbol inferredType) { UnwrapCollectionExpressionIfNullable(ref collectionExpr, diagnostics); bool gotInfo = GetEnumeratorInfo(ref builder, collectionExpr, diagnostics); if (!gotInfo) { inferredType = null; } else if (collectionExpr.HasDynamicType()) { // If the enumerator is dynamic, it yields dynamic values inferredType = DynamicTypeSymbol.Instance; } else if (collectionExpr.Type.SpecialType == SpecialType.System_String && builder.CollectionType.SpecialType == SpecialType.System_Collections_IEnumerable) { // Reproduce dev11 behavior: we're always going to lower a foreach loop over a string to a for loop // over the string's Chars indexer. Therefore, we should infer "char", regardless of what the spec // indicates the element type is. This actually matters in practice because the System.String in // the portable library doesn't have a pattern GetEnumerator method or implement IEnumerable<char>. inferredType = GetSpecialType(SpecialType.System_Char, diagnostics, collectionExpr.Syntax); } else { inferredType = builder.ElementType; } return gotInfo; }