Example #1
0
        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);
        }
Example #2
0
        /// <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));
        }
Example #3
0
        /// <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);
        }
Example #4
0
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        /// <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);
        }
Example #8
0
        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);
        }
Example #9
0
        /// <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);
        }
Example #10
0
        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;
        }