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; }
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"); }
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); }
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 }); }