private static LambdaConversionResult IsAnonymousFunctionCompatibleWithDelegate(UnboundLambda anonymousFunction, TypeSymbol type)
        {
            Debug.Assert((object)anonymousFunction != null);
            Debug.Assert((object)type != null);

            // SPEC: An anonymous-method-expression or lambda-expression is classified as an anonymous function. 
            // SPEC: The expression does not have a type but can be implicitly converted to a compatible delegate 
            // SPEC: type or expression tree type. Specifically, a delegate type D is compatible with an 
            // SPEC: anonymous function F provided:

            var delegateType = (NamedTypeSymbol)type;
            var invokeMethod = delegateType.DelegateInvokeMethod;

            if ((object)invokeMethod == null || invokeMethod.HasUseSiteError)
            {
                return LambdaConversionResult.BadTargetType;
            }

            var delegateParameters = invokeMethod.Parameters;

            // SPEC: If F contains an anonymous-function-signature, then D and F have the same number of parameters.
            // SPEC: If F does not contain an anonymous-function-signature, then D may have zero or more parameters 
            // SPEC: of any type, as long as no parameter of D has the out parameter modifier.

            if (anonymousFunction.HasSignature)
            {
                if (anonymousFunction.ParameterCount != invokeMethod.ParameterCount)
                {
                    return LambdaConversionResult.BadParameterCount;
                }

                // SPEC: If F has an explicitly typed parameter list, each parameter in D has the same type 
                // SPEC: and modifiers as the corresponding parameter in F.
                // SPEC: If F has an implicitly typed parameter list, D has no ref or out parameters.

                if (anonymousFunction.HasExplicitlyTypedParameterList)
                {
                    for (int p = 0; p < delegateParameters.Length; ++p)
                    {
                        if (delegateParameters[p].RefKind != anonymousFunction.RefKind(p) ||
                            !delegateParameters[p].Type.Equals(anonymousFunction.ParameterType(p), ignoreCustomModifiers: true, ignoreDynamic: true))
                        {
                            return LambdaConversionResult.MismatchedParameterType;
                        }
                    }
                }
                else
                {
                    for (int p = 0; p < delegateParameters.Length; ++p)
                    {
                        if (delegateParameters[p].RefKind != RefKind.None)
                        {
                            return LambdaConversionResult.RefInImplicitlyTypedLambda;
                        }
                    }

                    // In C# it is not possible to make a delegate type
                    // such that one of its parameter types is a static type. But static types are 
                    // in metadata just sealed abstract types; there is nothing stopping someone in
                    // another language from creating a delegate with a static type for a parameter,
                    // though the only argument you could pass for that parameter is null.
                    // 
                    // In the native compiler we forbid conversion of an anonymous function that has
                    // an implicitly-typed parameter list to a delegate type that has a static type
                    // for a formal parameter type. However, we do *not* forbid it for an explicitly-
                    // typed lambda (because we already require that the explicitly typed parameter not
                    // be static) and we do not forbid it for an anonymous method with the entire
                    // parameter list missing (because the body cannot possibly have a parameter that
                    // is of static type, even though this means that we will be generating a hidden
                    // method with a parameter of static type.)
                    //
                    // We also allow more exotic situations to work in the native compiler. For example,
                    // though it is not possible to convert x=>{} to Action<GC>, it is possible to convert
                    // it to Action<List<GC>> should there be a language that allows you to construct 
                    // a variable of that type.
                    //
                    // We might consider beefing up this rule to disallow a conversion of *any* anonymous
                    // function to *any* delegate that has a static type *anywhere* in the parameter list.

                    for (int p = 0; p < delegateParameters.Length; ++p)
                    {
                        if (delegateParameters[p].Type.IsStatic)
                        {
                            return LambdaConversionResult.StaticTypeInImplicitlyTypedLambda;
                        }
                    }
                }
            }
            else
            {
                for (int p = 0; p < delegateParameters.Length; ++p)
                {
                    if (delegateParameters[p].RefKind == RefKind.Out)
                    {
                        return LambdaConversionResult.MissingSignatureWithOutParameter;
                    }
                }
            }

            // Ensure the body can be converted to that delegate type
            var bound = anonymousFunction.Bind(delegateType);
            if (ErrorFacts.PreventsSuccessfulDelegateConversion(bound.Diagnostics))
            {
                return LambdaConversionResult.BindingFailed;
            }

            return LambdaConversionResult.Success;
        }
Exemplo n.º 2
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");
        }
Exemplo n.º 3
0
        private static LambdaConversionResult IsAnonymousFunctionCompatibleWithDelegate(UnboundLambda anonymousFunction, TypeSymbol type)
        {
            Debug.Assert((object)anonymousFunction != null);
            Debug.Assert((object)type != null);

            // SPEC: An anonymous-method-expression or lambda-expression is classified as an anonymous function.
            // SPEC: The expression does not have a type but can be implicitly converted to a compatible delegate
            // SPEC: type or expression tree type. Specifically, a delegate type D is compatible with an
            // SPEC: anonymous function F provided:

            var delegateType = (NamedTypeSymbol)type;
            var invokeMethod = delegateType.DelegateInvokeMethod;

            if ((object)invokeMethod == null || invokeMethod.HasUseSiteError)
            {
                return(LambdaConversionResult.BadTargetType);
            }

            var delegateParameters = invokeMethod.Parameters;

            // SPEC: If F contains an anonymous-function-signature, then D and F have the same number of parameters.
            // SPEC: If F does not contain an anonymous-function-signature, then D may have zero or more parameters
            // SPEC: of any type, as long as no parameter of D has the out parameter modifier.

            if (anonymousFunction.HasSignature)
            {
                if (anonymousFunction.ParameterCount != invokeMethod.ParameterCount)
                {
                    return(LambdaConversionResult.BadParameterCount);
                }

                // SPEC: If F has an explicitly typed parameter list, each parameter in D has the same type
                // SPEC: and modifiers as the corresponding parameter in F.
                // SPEC: If F has an implicitly typed parameter list, D has no ref or out parameters.

                if (anonymousFunction.HasExplicitlyTypedParameterList)
                {
                    for (int p = 0; p < delegateParameters.Length; ++p)
                    {
                        if (delegateParameters[p].RefKind != anonymousFunction.RefKind(p) ||
                            !delegateParameters[p].Type.Equals(anonymousFunction.ParameterType(p), ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true))
                        {
                            return(LambdaConversionResult.MismatchedParameterType);
                        }
                    }
                }
                else
                {
                    for (int p = 0; p < delegateParameters.Length; ++p)
                    {
                        if (delegateParameters[p].RefKind != RefKind.None)
                        {
                            return(LambdaConversionResult.RefInImplicitlyTypedLambda);
                        }
                    }

                    // In C# it is not possible to make a delegate type
                    // such that one of its parameter types is a static type. But static types are
                    // in metadata just sealed abstract types; there is nothing stopping someone in
                    // another language from creating a delegate with a static type for a parameter,
                    // though the only argument you could pass for that parameter is null.
                    //
                    // In the native compiler we forbid conversion of an anonymous function that has
                    // an implicitly-typed parameter list to a delegate type that has a static type
                    // for a formal parameter type. However, we do *not* forbid it for an explicitly-
                    // typed lambda (because we already require that the explicitly typed parameter not
                    // be static) and we do not forbid it for an anonymous method with the entire
                    // parameter list missing (because the body cannot possibly have a parameter that
                    // is of static type, even though this means that we will be generating a hidden
                    // method with a parameter of static type.)
                    //
                    // We also allow more exotic situations to work in the native compiler. For example,
                    // though it is not possible to convert x=>{} to Action<GC>, it is possible to convert
                    // it to Action<List<GC>> should there be a language that allows you to construct
                    // a variable of that type.
                    //
                    // We might consider beefing up this rule to disallow a conversion of *any* anonymous
                    // function to *any* delegate that has a static type *anywhere* in the parameter list.

                    for (int p = 0; p < delegateParameters.Length; ++p)
                    {
                        if (delegateParameters[p].Type.IsStatic)
                        {
                            return(LambdaConversionResult.StaticTypeInImplicitlyTypedLambda);
                        }
                    }
                }
            }
            else
            {
                for (int p = 0; p < delegateParameters.Length; ++p)
                {
                    if (delegateParameters[p].RefKind == RefKind.Out)
                    {
                        return(LambdaConversionResult.MissingSignatureWithOutParameter);
                    }
                }
            }

            // Ensure the body can be converted to that delegate type
            var bound = anonymousFunction.Bind(delegateType);

            if (ErrorFacts.PreventsSuccessfulDelegateConversion(bound.Diagnostics))
            {
                return(LambdaConversionResult.BindingFailed);
            }

            return(LambdaConversionResult.Success);
        }
Exemplo n.º 4
0
        private BoundExpression BindCodeblock(SyntaxNode syntax, UnboundLambda unboundLambda, Conversion conversion, bool isCast, TypeSymbol destination, DiagnosticBag diagnostics)
        {
            if (!Compilation.Options.HasRuntime)
            {
                return(null);
            }
            var isCodeblock = syntax.XIsCodeBlock;

            if (!isCodeblock)
            {
                isCodeblock = !destination.IsDelegateType() && !destination.IsExpressionTree();
            }
            if (!isCodeblock)
            {
                return(null);
            }

            Conversion conv = Conversion.ImplicitReference;

            if (destination != Compilation.CodeBlockType() && !destination.IsObjectType())
            {
                HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                conv = Conversions.ClassifyConversionFromType(Compilation.CodeBlockType(), destination, ref useSiteDiagnostics);
                diagnostics.Add(syntax, useSiteDiagnostics);
            }
            if (Compilation.Options.HasRuntime)
            {
                Debug.Assert(destination == Compilation.CodeBlockType() || conv.Exists);
            }
            if (!syntax.XIsCodeBlock && !Compilation.Options.MacroScript && !syntax.XNode.IsAliasExpression())
            {
                Error(diagnostics, ErrorCode.ERR_CodeblockWithLambdaSyntax, syntax);
            }

            AnonymousTypeManager manager = this.Compilation.AnonymousTypeManager;
            var delegateSignature        = new TypeSymbol[unboundLambda.ParameterCount + 1];
            var usualType = this.Compilation.UsualType();

            for (int i = 0; i < delegateSignature.Length; i++)
            {
                delegateSignature[i] = usualType;
            }
            NamedTypeSymbol cbType       = manager.ConstructCodeblockTypeSymbol(delegateSignature, syntax.Location);
            var             delType      = manager.GetCodeblockDelegateType(cbType);
            var             _boundLambda = unboundLambda.Bind(delType);

            diagnostics.AddRange(_boundLambda.Diagnostics);
            var cbDel = new BoundConversion(
                syntax,
                _boundLambda,
                conversion,
                @checked: false,
                explicitCastInCode: false,
                constantValueOpt: ConstantValue.NotAvailable,
                type: delType)
            {
                WasCompilerGenerated = unboundLambda.WasCompilerGenerated
            };
            var             cbSrc  = new BoundLiteral(syntax, ConstantValue.Create(syntax.XCodeBlockSource), Compilation.GetSpecialType(SpecialType.System_String));
            BoundExpression cbInst = new BoundAnonymousObjectCreationExpression(syntax,
                                                                                cbType.InstanceConstructors[0],
                                                                                new BoundExpression[] { cbDel, cbSrc }.ToImmutableArrayOrEmpty(),
                                                                                System.Collections.Immutable.ImmutableArray <BoundAnonymousPropertyDeclaration> .Empty, cbType)
            {
                WasCompilerGenerated = unboundLambda.WasCompilerGenerated
            };;

            if (conv != Conversion.ImplicitReference)
            {
                cbInst = new BoundConversion(syntax, cbInst, Conversion.ImplicitReference, false, false, ConstantValue.NotAvailable, Compilation.CodeBlockType())
                {
                    WasCompilerGenerated = unboundLambda.WasCompilerGenerated
                };;
            }
            if (!conv.IsValid || (!isCast && conv.IsExplicit))
            {
                GenerateImplicitConversionError(diagnostics, syntax, conv, cbInst, destination);

                return(new BoundConversion(
                           syntax,
                           cbInst,
                           conv,
                           false,
                           explicitCastInCode: isCast,
                           constantValueOpt: ConstantValue.NotAvailable,
                           type: destination,
                           hasErrors: true)
                {
                    WasCompilerGenerated = unboundLambda.WasCompilerGenerated
                });
            }
            return(new BoundConversion(
                       syntax,
                       cbInst,
                       conv,
                       false,
                       explicitCastInCode: isCast,
                       constantValueOpt: ConstantValue.NotAvailable,
                       type: destination)
            {
                WasCompilerGenerated = unboundLambda.WasCompilerGenerated
            });
        }