Example #1
0
        /// <remarks>
        /// From ExpressionBinder::EnsureQMarkTypesCompatible:
        /// 
        /// The v2.0 specification states that the types of the second and third operands T and S of a ternary operator
        /// must be TT and TS such that either (a) TT==TS, or (b), TT->TS or TS->TT but not both.
        ///
        /// Unfortunately that is not what we implemented in v2.0.  Instead, we implemented
        /// that either (a) TT=TS or (b) T->TS or S->TT but not both.  That is, we looked at the
        /// convertibility of the expressions, not the types.
        ///
        ///
        /// Changing that to the algorithm in the standard would be a breaking change.
        ///
        /// b ? (Func&lt;int&gt;)(delegate(){return 1;}) : (delegate(){return 2;})
        ///
        /// and
        ///
        /// b ? 0 : myenum
        ///
        /// would suddenly stop working.  (The first because o2 has no type, the second because 0 goes to
        /// any enum but enum doesn't go to int.)
        ///
        /// It gets worse.  We would like the 3.0 language features which require type inference to use
        /// a consistent algorithm, and that furthermore, the algorithm be smart about choosing the best
        /// of a set of types.  However, the language committee has decided that this algorithm will NOT
        /// consume information about the convertibility of expressions. Rather, it will gather up all
        /// the possible types and then pick the "largest" of them.
        ///
        /// To maintain backwards compatibility while still participating in the spirit of consistency,
        /// we implement an algorithm here which picks the type based on expression convertibility, but
        /// if there is a conflict, then it chooses the larger type rather than producing a type error.
        /// This means that b?0:myshort will have type int rather than producing an error (because 0->short,
        /// myshort->int).
        /// </remarks>
        private BoundExpression BindConditionalOperator(ConditionalExpressionSyntax node, DiagnosticBag diagnostics)
        {
            BoundExpression condition = BindBooleanExpression(node.Condition, diagnostics);
            BoundExpression trueExpr = BindValue(node.WhenTrue, diagnostics, BindValueKind.RValue);
            BoundExpression falseExpr = BindValue(node.WhenFalse, diagnostics, BindValueKind.RValue);

            TypeSymbol trueType = trueExpr.Type;
            TypeSymbol falseType = falseExpr.Type;

            TypeSymbol type;
            bool hasErrors = false;

            if (trueType == falseType)
            {
                // NOTE: Dev10 lets the type inferrer handle this case (presumably, for maximum consistency),
                // but it seems like a worthwhile short-circuit for a common case.

                if ((object)trueType == null)
                {
                    // If trueExpr and falseExpr both have type null, then we don't have any symbols
                    // to pass to a SymbolDistinguisher (which ERR_InvalidQM would usually require).
                    diagnostics.Add(ErrorCode.ERR_InvalidQM, node.Location, trueExpr.Display, falseExpr.Display);
                    type = CreateErrorType();
                    hasErrors = true;
                }
                else
                {
                    // <expr> ? T : T
                    type = trueType;
                }
            }
            else
            {
                bool hadMultipleCandidates;
                HashSet<DiagnosticInfo> useSiteDiagnostics = null;
                TypeSymbol bestType = BestTypeInferrer.InferBestTypeForConditionalOperator(trueExpr, falseExpr, this.Conversions, out hadMultipleCandidates, ref useSiteDiagnostics);
                diagnostics.Add(node, useSiteDiagnostics);

                if ((object)bestType == null)
                {
                    // CONSIDER: Dev10 suppresses ERR_InvalidQM unless the following is true for both trueType and falseType
                    // (!T->type->IsErrorType() || T->type->AsErrorType()->HasTypeParent() || T->type->AsErrorType()->HasNSParent())
                    if (hadMultipleCandidates)
                    {
                        diagnostics.Add(ErrorCode.ERR_AmbigQM, node.Location, trueExpr.Display, falseExpr.Display);
                    }
                    else
                    {
                        object trueArg = trueExpr.Display;
                        object falseArg = falseExpr.Display;

                        Symbol trueSymbol = trueArg as Symbol;
                        Symbol falseSymbol = falseArg as Symbol;
                        if ((object)trueSymbol != null && (object)falseSymbol != null)
                        {
                            SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, trueSymbol, falseSymbol);
                            trueArg = distinguisher.First;
                            falseArg = distinguisher.Second;
                        }

                        diagnostics.Add(ErrorCode.ERR_InvalidQM, node.Location, trueArg, falseArg);
                    }

                    type = CreateErrorType();
                    hasErrors = true;
                }
                else if (bestType.IsErrorType())
                {
                    type = bestType;
                    hasErrors = true;
                }
                else
                {
                    trueExpr = GenerateConversionForAssignment(bestType, trueExpr, diagnostics);
                    falseExpr = GenerateConversionForAssignment(bestType, falseExpr, diagnostics);

                    if (trueExpr.HasAnyErrors || falseExpr.HasAnyErrors)
                    {
                        // If one of the conversions went wrong (e.g. return type of method group being converted
                        // didn't match), then we don't want to use bestType because it's not accurate.
                        type = CreateErrorType();
                        hasErrors = true;
                    }
                    else
                    {
                        type = bestType;
                    }
                }
            }

            ConstantValue constantValue = null;

            if (!hasErrors)
            {
                constantValue = FoldConditionalOperator(condition, trueExpr, falseExpr);
                hasErrors = constantValue != null && constantValue.IsBad;
            }

            return new BoundConditionalOperator(node, condition, trueExpr, falseExpr, constantValue, type, hasErrors);
        }
Example #2
0
        private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics)
        {
            BoundExpression collectionExpr = this.Next.BindValue(syntax.Expression, diagnostics, BindValueKind.RValue); //bind with next to avoid seeing iteration variable

            ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
            TypeSymbol inferredType;
            bool       hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);

            // These should only occur when special types are missing or malformed.
            hasErrors = hasErrors ||
                        (object)builder.GetEnumeratorMethod == null ||
                        (object)builder.MoveNextMethod == null ||
                        (object)builder.CurrentPropertyGetter == null;

            // Check for local variable conflicts in the *enclosing* binder; obviously the *current*
            // binder has a local that matches!
            hasErrors |= this.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics);

            // If the type in syntax is "var", then the type should be set explicitly so that the
            // Type property doesn't fail.

            TypeSyntax typeSyntax = this.syntax.Type;

            bool        isVar;
            AliasSymbol alias;
            TypeSymbol  declType = BindType(typeSyntax, diagnostics, out isVar, out alias);

            TypeSymbol iterationVariableType;

            if (isVar)
            {
                iterationVariableType = inferredType ?? CreateErrorType("var");
            }
            else
            {
                Debug.Assert((object)declType != null);
                iterationVariableType = declType;
            }


            BoundTypeExpression boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType);

            this.IterationVariable.SetTypeSymbol(iterationVariableType);

            BoundStatement body = BindPossibleEmbeddedStatement(syntax.Statement, diagnostics);

            hasErrors = hasErrors || iterationVariableType.IsErrorType();

            // Skip the conversion checks and array/enumerator differentiation if we know we have an error.
            if (hasErrors)
            {
                return(new BoundForEachStatement(
                           syntax,
                           ImmutableArray <LocalSymbol> .Empty,
                           null, // can't be sure that it's complete
                           default(Conversion),
                           boundIterationVariableType,
                           this.IterationVariable,
                           collectionExpr,
                           body,
                           CheckOverflowAtRuntime,
                           this.BreakLabel,
                           this.ContinueLabel,
                           hasErrors));
            }

            var foreachKeyword = syntax.ForEachKeyword;

            ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false);

            // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case,
            // but it turns out that these are equivalent (when both are available).

            HashSet <DiagnosticInfo> useSiteDiagnostics = null;
            Conversion elementConversion = this.Conversions.ClassifyConversionForCast(inferredType, iterationVariableType, ref useSiteDiagnostics);

            if (!elementConversion.IsValid)
            {
                ImmutableArray <MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions;
                if (originalUserDefinedConversions.Length > 1)
                {
                    diagnostics.Add(ErrorCode.ERR_AmbigUDConv, syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType);
                }
                else
                {
                    SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType);
                    diagnostics.Add(ErrorCode.ERR_NoExplicitConv, syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second);
                }
                hasErrors = true;
            }
            else
            {
                ReportDiagnosticsIfObsolete(diagnostics, elementConversion, syntax.ForEachKeyword, hasBaseReceiver: false);
            }

            // Spec (§8.8.4):
            // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression)
            // to the System.Collections.IEnumerable interface (§6.1.8).
            builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics);
            builder.CurrentConversion    = this.Conversions.ClassifyConversion(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics);

            builder.EnumeratorConversion = this.Conversions.ClassifyConversion(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, this.syntax), ref useSiteDiagnostics);

            diagnostics.Add(syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics);

            // Due to the way we extracted the various types, these conversions should always be possible.
            // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we
            // can't convert from object to a pointer type.  Similarly, if we're iterating over an array of
            // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a
            // value type.  This doesn't matter in practice, since we won't actually use the enumerator pattern
            // when we lower the loop.
            Debug.Assert(builder.CollectionConversion.IsValid);
            Debug.Assert(builder.CurrentConversion.IsValid ||
                         (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) ||
                         (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray()));
            Debug.Assert(builder.EnumeratorConversion.IsValid ||
                         this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error,
                         "Conversions to object succeed unless there's a problem with the object type");

            // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute.
            Debug.Assert((object)builder.CollectionConversion.Method == null,
                         "Conversion from collection expression to collection type should not be user-defined");
            Debug.Assert((object)builder.CurrentConversion.Method == null,
                         "Conversion from Current property type to element type should not be user-defined");
            Debug.Assert((object)builder.EnumeratorConversion.Method == null,
                         "Conversion from GetEnumerator return type to System.Object should not be user-defined");

            // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted
            // type (i.e. builder.CollectionType) will be available in the binding API.
            BoundConversion convertedCollectionExpression = new BoundConversion(
                collectionExpr.Syntax,
                collectionExpr,
                builder.CollectionConversion,
                CheckOverflowAtRuntime,
                false,
                ConstantValue.NotAvailable,
                builder.CollectionType);

            return(new BoundForEachStatement(
                       syntax,
                       ImmutableArray <LocalSymbol> .Empty,
                       builder.Build(this.Flags),
                       elementConversion,
                       boundIterationVariableType,
                       this.IterationVariable,
                       convertedCollectionExpression,
                       body,
                       CheckOverflowAtRuntime,
                       this.BreakLabel,
                       this.ContinueLabel,
                       hasErrors));
        }
Example #3
0
        private static bool ReportAsOperatorConversionDiagnostics(
            CSharpSyntaxNode node,
            DiagnosticBag diagnostics,
            Compilation compilation,
            TypeSymbol operandType,
            TypeSymbol targetType,
            ConversionKind conversionKind,
            ConstantValue operandConstantValue)
        {
            // SPEC:    In an operation of the form E as T, E must be an expression and T must be a reference type,
            // SPEC:    a type parameter known to be a reference type, or a nullable type.
            // SPEC:    Furthermore, at least one of the following must be true, or otherwise a compile-time error occurs:
            // SPEC:    •	An identity (§6.1.1), implicit nullable (§6.1.4), implicit reference (§6.1.6), boxing (§6.1.7), 
            // SPEC:        explicit nullable (§6.2.3), explicit reference (§6.2.4), or unboxing (§6.2.5) conversion exists
            // SPEC:        from E to T.
            // SPEC:    •	The type of E or T is an open type.
            // SPEC:    •	E is the null literal.

            // SPEC VIOLATION:  The specification contains an error in the list of legal conversions above.
            // SPEC VIOLATION:  If we have "class C<T, U> where T : U where U : class" then there is
            // SPEC VIOLATION:  an implicit conversion from T to U, but it is not an identity, reference or
            // SPEC VIOLATION:  boxing conversion. It will be one of those at runtime, but at compile time
            // SPEC VIOLATION:  we do not know which, and therefore cannot classify it as any of those.
            // SPEC VIOLATION:  See Microsoft.CodeAnalysis.CSharp.UnitTests.SyntaxBinderTests.TestAsOperator_SpecErrorCase() test for an example.

            // SPEC VIOLATION:  The specification also unintentionally allows the case where requirement 2 above:
            // SPEC VIOLATION:  "The type of E or T is an open type" is true, but type of E is void type, i.e. T is an open type.
            // SPEC VIOLATION:  Dev10 compiler correctly generates an error for this case and we will maintain compatibility.

            bool hasErrors = false;
            switch (conversionKind)
            {
                case ConversionKind.ImplicitReference:
                case ConversionKind.Boxing:
                case ConversionKind.ImplicitNullable:
                case ConversionKind.Identity:
                case ConversionKind.ExplicitNullable:
                case ConversionKind.ExplicitReference:
                case ConversionKind.Unboxing:
                    break;

                default:
                    // Generate an error if there is no possible legal conversion and both the operandType
                    // and the targetType are closed types OR operandType is void type, otherwise we need a runtime check
                    if (!operandType.ContainsTypeParameter() && !targetType.ContainsTypeParameter() ||
                        operandType.SpecialType == SpecialType.System_Void)
                    {
                        SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, operandType, targetType);
                        Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, distinguisher.First, distinguisher.Second);
                        hasErrors = true;
                    }

                    break;
            }

            if (!hasErrors)
            {
                ReportAsOperatorConstantWarnings(node, diagnostics, operandType, targetType, conversionKind, operandConstantValue);
            }

            return hasErrors;
        }
Example #4
0
        internal void GenerateAnonymousFunctionConversionError(DiagnosticBag diagnostics, CSharpSyntaxNode syntax,
            UnboundLambda anonymousFunction, TypeSymbol targetType)
        {
            Debug.Assert((object)targetType != null);
            Debug.Assert(anonymousFunction != null);

            // Is the target type simply bad?

            // If the target type is an error then we've already reported a diagnostic. Don't bother
            // reporting the conversion error.
            if (targetType.IsErrorType() || syntax.HasErrors)
            {
                return;
            }

            // CONSIDER: Instead of computing this again, cache the reason why the conversion failed in
            // CONSIDER: the Conversion result, and simply report that.

            var reason = Conversions.IsAnonymousFunctionCompatibleWithType(anonymousFunction, targetType);

            // It is possible that the conversion from lambda to delegate is just fine, and 
            // that we ended up here because the target type, though itself is not an error
            // type, contains a type argument which is an error type. For example, converting
            // (Foo foo)=>{} to Action<Foo> is a perfectly legal conversion even if Foo is undefined!
            // In that case we have already reported an error that Foo is undefined, so just bail out.

            if (reason == LambdaConversionResult.Success)
            {
                return;
            }

            var id = anonymousFunction.MessageID.Localize();

            if (reason == LambdaConversionResult.BadTargetType)
            {
                if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, targetType, node: syntax))
                {
                    return;
                }

                // Cannot convert {0} to type '{1}' because it is not a delegate type
                Error(diagnostics, ErrorCode.ERR_AnonMethToNonDel, syntax, id, targetType);
                return;
            }

            if (reason == LambdaConversionResult.ExpressionTreeMustHaveDelegateTypeArgument)
            {
                Debug.Assert(targetType.IsExpressionTree());
                Error(diagnostics, ErrorCode.ERR_ExpressionTreeMustHaveDelegate, syntax, ((NamedTypeSymbol)targetType).TypeArgumentsNoUseSiteDiagnostics[0]);
                return;
            }

            if (reason == LambdaConversionResult.ExpressionTreeFromAnonymousMethod)
            {
                Debug.Assert(targetType.IsExpressionTree());
                Error(diagnostics, ErrorCode.ERR_AnonymousMethodToExpressionTree, syntax);
                return;
            }

            // At this point we know that we have either a delegate type or an expression type for the target.

            var delegateType = targetType.GetDelegateType();

            // The target type is a vaid delegate or expression tree type. Is there something wrong with the 
            // parameter list?

            // First off, is there a parameter list at all?

            if (reason == LambdaConversionResult.MissingSignatureWithOutParameter)
            {
                // COMPATIBILITY: The C# 4 compiler produces two errors for:
                //
                // delegate void D (out int x);
                // ...
                // D d = delegate {};
                //
                // error CS1676: Parameter 1 must be declared with the 'out' keyword
                // error CS1688: Cannot convert anonymous method block without a parameter list 
                // to delegate type 'D' because it has one or more out parameters
                //
                // This seems redundant, (because there is no "parameter 1" in the source code)
                // and unnecessary. I propose that we eliminate the first error.

                Error(diagnostics, ErrorCode.ERR_CantConvAnonMethNoParams, syntax, targetType);
                return;
            }

            // There is a parameter list. Does it have the right number of elements?

            if (reason == LambdaConversionResult.BadParameterCount)
            {
                // Delegate '{0}' does not take {1} arguments
                Error(diagnostics, ErrorCode.ERR_BadDelArgCount, syntax, targetType, anonymousFunction.ParameterCount);
                return;
            }

            // The parameter list exists and had the right number of parameters. Were any of its types bad?

            // If any parameter type of the lambda is an error type then suppress 
            // further errors. We've already reported errors on the bad type.
            if (anonymousFunction.HasExplicitlyTypedParameterList)
            {
                for (int i = 0; i < anonymousFunction.ParameterCount; ++i)
                {
                    if (anonymousFunction.ParameterType(i).IsErrorType())
                    {
                        return;
                    }
                }
            }

            // The parameter list exists and had the right number of parameters. Were any of its types
            // mismatched with the delegate parameter types?

            // The simplest possible case is (x, y, z)=>whatever where the target type has a ref or out parameter.

            var delegateParameters = delegateType.DelegateParameters();
            if (reason == LambdaConversionResult.RefInImplicitlyTypedLambda)
            {
                for (int i = 0; i < anonymousFunction.ParameterCount; ++i)
                {
                    var delegateRefKind = delegateParameters[i].RefKind;
                    if (delegateRefKind != RefKind.None)
                    {
                        // Parameter {0} must be declared with the '{1}' keyword
                        Error(diagnostics, ErrorCode.ERR_BadParamRef, anonymousFunction.ParameterLocation(i),
                            i + 1, delegateRefKind.ToDisplayString());
                    }
                }
                return;
            }

            // See the comments in IsAnonymousFunctionCompatibleWithDelegate for an explanation of this one.
            if (reason == LambdaConversionResult.StaticTypeInImplicitlyTypedLambda)
            {
                for (int i = 0; i < anonymousFunction.ParameterCount; ++i)
                {
                    if (delegateParameters[i].Type.IsStatic)
                    {
                        // {0}: Static types cannot be used as parameter
                        Error(diagnostics, ErrorCode.ERR_ParameterIsStaticClass, anonymousFunction.ParameterLocation(i), delegateParameters[i].Type);
                    }
                }
                return;
            }

            // Otherwise, there might be a more complex reason why the parameter types are mismatched.

            if (reason == LambdaConversionResult.MismatchedParameterType)
            {
                // Cannot convert {0} to delegate type '{1}' because the parameter types do not match the delegate parameter types
                Error(diagnostics, ErrorCode.ERR_CantConvAnonMethParams, syntax, id, targetType);
                Debug.Assert(anonymousFunction.ParameterCount == delegateParameters.Length);
                for (int i = 0; i < anonymousFunction.ParameterCount; ++i)
                {
                    var lambdaParameterType = anonymousFunction.ParameterType(i);
                    if (lambdaParameterType.IsErrorType())
                    {
                        continue;
                    }

                    var lambdaParameterLocation = anonymousFunction.ParameterLocation(i);
                    var lambdaRefKind = anonymousFunction.RefKind(i);
                    var delegateParameterType = delegateParameters[i].Type;
                    var delegateRefKind = delegateParameters[i].RefKind;

                    if (!lambdaParameterType.Equals(delegateParameterType, ignoreCustomModifiers: true, ignoreDynamic: true))
                    {
                        SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, lambdaParameterType, delegateParameterType);

                        // Parameter {0} is declared as type '{1}{2}' but should be '{3}{4}'
                        Error(diagnostics, ErrorCode.ERR_BadParamType, lambdaParameterLocation,
                            i + 1, lambdaRefKind.ToPrefix(), distinguisher.First, delegateRefKind.ToPrefix(), distinguisher.Second);
                    }
                    else if (lambdaRefKind != delegateRefKind)
                    {
                        if (delegateRefKind == RefKind.None)
                        {
                            // Parameter {0} should not be declared with the '{1}' keyword
                            Error(diagnostics, ErrorCode.ERR_BadParamExtraRef, lambdaParameterLocation, i + 1, lambdaRefKind.ToDisplayString());
                        }
                        else
                        {
                            // Parameter {0} must be declared with the '{1}' keyword
                            Error(diagnostics, ErrorCode.ERR_BadParamRef, lambdaParameterLocation, i + 1, delegateRefKind.ToDisplayString());
                        }
                    }
                }
                return;
            }

            if (reason == LambdaConversionResult.BindingFailed)
            {
                var bindingResult = anonymousFunction.Bind(delegateType);
                Debug.Assert(ErrorFacts.PreventsSuccessfulDelegateConversion(bindingResult.Diagnostics));
                diagnostics.AddRange(bindingResult.Diagnostics);
                return;
            }

            // UNDONE: LambdaConversionResult.VoidExpressionLambdaMustBeStatementExpression:

            Debug.Assert(false, "Missing case in lambda conversion error reporting");
        }
Example #5
0
        protected static void GenerateImplicitConversionError(DiagnosticBag diagnostics, Compilation compilation, CSharpSyntaxNode syntax,
            Conversion conversion, TypeSymbol sourceType, TypeSymbol targetType, ConstantValue sourceConstantValueOpt = null)
        {
            Debug.Assert(!conversion.IsImplicit || !conversion.IsValid);

            // If the either type is an error then an error has already been reported
            // for some aspect of the analysis of this expression. (For example, something like
            // "garbage g = null; short s = g;" -- we don't want to report that g is not
            // convertible to short because we've already reported that g does not have a good type.
            if (!sourceType.IsErrorType() && !targetType.IsErrorType())
            {
                if (conversion.IsExplicit)
                {
                    if (sourceType.SpecialType == SpecialType.System_Double && syntax.Kind() == SyntaxKind.NumericLiteralExpression &&
                        (targetType.SpecialType == SpecialType.System_Single || targetType.SpecialType == SpecialType.System_Decimal))
                    {
                        Error(diagnostics, ErrorCode.ERR_LiteralDoubleCast, syntax, (targetType.SpecialType == SpecialType.System_Single) ? "F" : "M", targetType);
                    }
                    else if (conversion.Kind == ConversionKind.ExplicitNumeric && sourceConstantValueOpt != null && sourceConstantValueOpt != ConstantValue.Bad &&
                        ConversionsBase.HasImplicitConstantExpressionConversion(new BoundLiteral(syntax, ConstantValue.Bad, sourceType), targetType))
                    {
                        // CLEVERNESS: By passing ConstantValue.Bad, we tell HasImplicitConstantExpressionConversion to ignore the constant
                        // value and only consider the types.

                        // If there would be an implicit constant conversion for a different constant of the same type 
                        // (i.e. one that's not out of range), then it's more helpful to report the range check failure
                        // than to suggest inserting a cast.
                        Error(diagnostics, ErrorCode.ERR_ConstOutOfRange, syntax, sourceConstantValueOpt.Value, targetType);
                    }
                    else
                    {
                        SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, sourceType, targetType);
                        Error(diagnostics, ErrorCode.ERR_NoImplicitConvCast, syntax, distinguisher.First, distinguisher.Second);
                    }
                }
                else if (conversion.ResultKind == LookupResultKind.OverloadResolutionFailure)
                {
                    Debug.Assert(conversion.IsUserDefined);

                    ImmutableArray<MethodSymbol> originalUserDefinedConversions = conversion.OriginalUserDefinedConversions;
                    if (originalUserDefinedConversions.Length > 1)
                    {
                        Error(diagnostics, ErrorCode.ERR_AmbigUDConv, syntax, originalUserDefinedConversions[0], originalUserDefinedConversions[1], sourceType, targetType);
                    }
                    else
                    {
                        Debug.Assert(originalUserDefinedConversions.Length == 0,
                            "How can there be exactly one applicable user-defined conversion if the conversion doesn't exist?");
                        SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, sourceType, targetType);
                        Error(diagnostics, ErrorCode.ERR_NoImplicitConv, syntax, distinguisher.First, distinguisher.Second);
                    }
                }
                else
                {
                    SymbolDistinguisher distinguisher = new SymbolDistinguisher(compilation, sourceType, targetType);
                    Error(diagnostics, ErrorCode.ERR_NoImplicitConv, syntax, distinguisher.First, distinguisher.Second);
                }
            }
        }
Example #6
0
 public Description(SymbolDistinguisher distinguisher, int index)
 {
     _distinguisher = distinguisher;
     _index         = index;
 }
 public Description(SymbolDistinguisher distinguisher, int index)
 {
     _distinguisher = distinguisher;
     _index = index;
 }
Example #8
0
        private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, Binder originalBinder)
        {
            // Use the right binder to avoid seeing iteration variable
            BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue); 

            ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder();
            TypeSymbol inferredType;
            bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType);

            // These should only occur when special types are missing or malformed.
            hasErrors = hasErrors ||
                (object)builder.GetEnumeratorMethod == null ||
                (object)builder.MoveNextMethod == null ||
                (object)builder.CurrentPropertyGetter == null;

            // Check for local variable conflicts in the *enclosing* binder; obviously the *current*
            // binder has a local that matches!
            var hasNameConflicts = originalBinder.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics);

            // If the type in syntax is "var", then the type should be set explicitly so that the
            // Type property doesn't fail.

            TypeSyntax typeSyntax = _syntax.Type;

            bool isVar;
            AliasSymbol alias;
            TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias);

            TypeSymbol iterationVariableType;
            if (isVar)
            {
                iterationVariableType = inferredType ?? CreateErrorType("var");
            }
            else
            {
                Debug.Assert((object)declType != null);
                iterationVariableType = declType;
            }


            BoundTypeExpression boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType);
            this.IterationVariable.SetTypeSymbol(iterationVariableType);

            BoundStatement body = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics);

            hasErrors = hasErrors || iterationVariableType.IsErrorType();

            // Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts).
            if (hasErrors)
            {
                return new BoundForEachStatement(
                    _syntax,
                    null, // can't be sure that it's complete
                    default(Conversion),
                    boundIterationVariableType,
                    this.IterationVariable,
                    collectionExpr,
                    body,
                    CheckOverflowAtRuntime,
                    this.BreakLabel,
                    this.ContinueLabel,
                    hasErrors);
            }

            hasErrors |= hasNameConflicts;

            var foreachKeyword = _syntax.ForEachKeyword;
            ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false);
            ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false);

            // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case,
            // but it turns out that these are equivalent (when both are available).

            HashSet<DiagnosticInfo> useSiteDiagnostics = null;
            Conversion elementConversion = this.Conversions.ClassifyConversionForCast(inferredType, iterationVariableType, ref useSiteDiagnostics);

            if (!elementConversion.IsValid)
            {
                ImmutableArray<MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions;
                if (originalUserDefinedConversions.Length > 1)
                {
                    diagnostics.Add(ErrorCode.ERR_AmbigUDConv, _syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType);
                }
                else
                {
                    SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType);
                    diagnostics.Add(ErrorCode.ERR_NoExplicitConv, _syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second);
                }
                hasErrors = true;
            }
            else
            {
                ReportDiagnosticsIfObsolete(diagnostics, elementConversion, _syntax.ForEachKeyword, hasBaseReceiver: false);
            }

            // Spec (§8.8.4):
            // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression) 
            // to the System.Collections.IEnumerable interface (§6.1.8). 
            builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics);
            builder.CurrentConversion = this.Conversions.ClassifyConversion(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics);

            builder.EnumeratorConversion = this.Conversions.ClassifyConversion(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, _syntax), ref useSiteDiagnostics);

            diagnostics.Add(_syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics);

            // Due to the way we extracted the various types, these conversions should always be possible.
            // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we
            // can't convert from object to a pointer type.  Similarly, if we're iterating over an array of
            // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a
            // value type.  This doesn't matter in practice, since we won't actually use the enumerator pattern 
            // when we lower the loop.
            Debug.Assert(builder.CollectionConversion.IsValid);
            Debug.Assert(builder.CurrentConversion.IsValid ||
                (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) ||
                (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray()));
            Debug.Assert(builder.EnumeratorConversion.IsValid ||
                this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error ||
                !useSiteDiagnostics.IsNullOrEmpty(),
                "Conversions to object succeed unless there's a problem with the object type or the source type");

            // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute.
            Debug.Assert((object)builder.CollectionConversion.Method == null,
                "Conversion from collection expression to collection type should not be user-defined");
            Debug.Assert((object)builder.CurrentConversion.Method == null,
                "Conversion from Current property type to element type should not be user-defined");
            Debug.Assert((object)builder.EnumeratorConversion.Method == null,
                "Conversion from GetEnumerator return type to System.Object should not be user-defined");

            // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted
            // type (i.e. builder.CollectionType) will be available in the binding API.
            BoundConversion convertedCollectionExpression = new BoundConversion(
                collectionExpr.Syntax,
                collectionExpr,
                builder.CollectionConversion,
                CheckOverflowAtRuntime,
                false,
                ConstantValue.NotAvailable,
                builder.CollectionType);

            return new BoundForEachStatement(
                _syntax,
                builder.Build(this.Flags),
                elementConversion,
                boundIterationVariableType,
                this.IterationVariable,
                convertedCollectionExpression,
                body,
                CheckOverflowAtRuntime,
                this.BreakLabel,
                this.ContinueLabel,
                hasErrors);
        }
Example #9
0
 public Description(SymbolDistinguisher distinguisher, int index)
 {
     this.distinguisher = distinguisher;
     this.index         = index;
 }
Example #10
0
 public Description(SymbolDistinguisher distinguisher, int index)
 {
     this.distinguisher = distinguisher;
     this.index = index;
 }