/// <summary> /// Return the Invoke method symbol if the type is a delegate /// type and the Invoke method is available, otherwise null. /// </summary> private static MethodSymbol GetDelegateInvokeMethodIfAvailable(TypeSymbol type) { var delegateType = type.GetDelegateType(); if ((object)delegateType == null) { return(null); } MethodSymbol methodSymbol = delegateType.DelegateInvokeMethod; if ((object)methodSymbol == null || methodSymbol.HasUseSiteError) { return(null); } return(methodSymbol); }
/// <summary> /// Return the Invoke method symbol if the type is a delegate /// type and the Invoke method is available, otherwise null. /// </summary> private static MethodSymbol GetDelegateInvokeMethodIfAvailable(TypeSymbol type) { var delegateType = type.GetDelegateType(); if ((object)delegateType == null) { return null; } MethodSymbol methodSymbol = delegateType.DelegateInvokeMethod; if ((object)methodSymbol == null || methodSymbol.HasUseSiteError) { return null; } return methodSymbol; }
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"); }
public static MethodSymbol DelegateInvokeMethod(this TypeSymbol type) { Debug.Assert((object)type != null); Debug.Assert(type.IsDelegateType() || type.IsExpressionTree()); return(type.GetDelegateType().DelegateInvokeMethod); }
private static TypeSymbol InferReturnType( BoundBlock block, Binder binder, TypeSymbol delegateType, bool isAsync, ref HashSet <DiagnosticInfo> useSiteDiagnostics, out RefKind refKind, out bool inferredFromSingleType) { int numberOfDistinctReturns; var resultTypes = BlockReturns.GetReturnTypes(block, out refKind, out numberOfDistinctReturns); inferredFromSingleType = numberOfDistinctReturns < 2; TypeSymbol bestResultType; if (resultTypes.IsDefaultOrEmpty) { bestResultType = null; } else if (resultTypes.Length == 1) { bestResultType = resultTypes[0]; } else { bestResultType = BestTypeInferrer.InferBestType(resultTypes, binder.Conversions, ref useSiteDiagnostics); } if (!isAsync) { return(bestResultType); } // For async lambdas, the return type is the return type of the // delegate Invoke method if Invoke has a Task-like return type. // Otherwise the return type is Task or Task<T>. NamedTypeSymbol taskType = null; var delegateReturnType = delegateType?.GetDelegateType()?.DelegateInvokeMethod?.ReturnType as NamedTypeSymbol; if ((object)delegateReturnType != null) { NamedTypeSymbol builderType; MethodSymbol createBuilderMethod; if (delegateReturnType.IsCustomTaskType(out builderType, out createBuilderMethod)) { taskType = delegateReturnType; } } if (resultTypes.IsEmpty) { // No return statements have expressions; use delegate InvokeMethod // or infer type Task if delegate type not available. return((object)taskType != null && taskType.Arity == 0 ? taskType : binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task)); } if ((object)bestResultType == null || bestResultType.SpecialType == SpecialType.System_Void) { // If the best type was 'void', ERR_CantReturnVoid is reported while binding the "return void" // statement(s). return(null); } // Some non-void best type T was found; use delegate InvokeMethod // or infer type Task<T> if delegate type not available. var taskTypeT = (object)taskType != null && taskType.Arity == 1 ? delegateReturnType.ConstructedFrom : binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T); return(taskTypeT.Construct(bestResultType)); }
//////////////////////////////////////////////////////////////////////////////// // // Output types // private static bool DoesOutputTypeContain(BoundExpression argument, TypeSymbol formalParameterType, TypeParameterSymbol typeParameter) { // SPEC: If E is a method group or an anonymous function and T is a delegate // SPEC: type or expression tree type then the return type of T is an output type // SPEC: of E with type T. var delegateType = formalParameterType.GetDelegateType(); if ((object)delegateType == null) { return false; } if (argument.Kind != BoundKind.UnboundLambda && argument.Kind != BoundKind.MethodGroup) { return false; } MethodSymbol delegateInvoke = delegateType.DelegateInvokeMethod; if ((object)delegateInvoke == null || delegateInvoke.HasUseSiteError) { return false; } var delegateReturnType = delegateInvoke.ReturnType; if ((object)delegateReturnType == null) { return false; } return delegateReturnType.ContainsTypeParameter(typeParameter); }
//////////////////////////////////////////////////////////////////////////////// // // Input types // private static bool DoesInputTypeContain(BoundExpression argument, TypeSymbol formalParameterType, TypeParameterSymbol typeParameter) { // SPEC: If E is a method group or an anonymous function and T is a delegate // SPEC: type or expression tree type then all the parameter types of T are // SPEC: input types of E with type T. var delegateType = formalParameterType.GetDelegateType(); if ((object)delegateType == null) { return false; // No input types. } if (argument.Kind != BoundKind.UnboundLambda && argument.Kind != BoundKind.MethodGroup) { return false; // No input types. } var delegateParameters = delegateType.DelegateParameters(); if (delegateParameters.IsDefaultOrEmpty) { return false; } foreach (var delegateParameter in delegateParameters) { if (delegateParameter.Type.ContainsTypeParameter(typeParameter)) { return true; } } return false; }
//////////////////////////////////////////////////////////////////////////////// // // Explicit parameter type inferences // private void ExplicitParameterTypeInference(BoundExpression source, TypeSymbol target, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(source != null); Debug.Assert((object)target != null); // SPEC: An explicit type parameter type inference is made from an expression // SPEC: E to a type T in the following way. // SPEC: If E is an explicitly typed anonymous function with parameter types // SPEC: U1...Uk and T is a delegate type or expression tree type with // SPEC: parameter types V1...Vk then for each Ui an exact inference is made // SPEC: from Ui to the corresponding Vi. if (source.Kind != BoundKind.UnboundLambda) { return; } UnboundLambda anonymousFunction = (UnboundLambda)source; if (!anonymousFunction.HasExplicitlyTypedParameterList) { return; } var delegateType = target.GetDelegateType(); if ((object)delegateType == null) { return; } var delegateParameters = delegateType.DelegateParameters(); if (delegateParameters.IsDefault) { return; } int size = delegateParameters.Length; if (anonymousFunction.ParameterCount < size) { size = anonymousFunction.ParameterCount; } // SPEC ISSUE: What should we do if there is an out/ref mismatch between an // SPEC ISSUE: anonymous function parameter and a delegate parameter? // SPEC ISSUE: The result will not be applicable no matter what, but should // SPEC ISSUE: we make any inferences? This is going to be an error // SPEC ISSUE: ultimately, but it might make a difference for intellisense or // SPEC ISSUE: other analysis. for (int i = 0; i < size; ++i) { ExactInference(anonymousFunction.ParameterType(i), delegateParameters[i].Type, ref useSiteDiagnostics); } }
private bool MethodGroupReturnTypeInference(Binder binder, BoundExpression source, TypeSymbol target, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(source != null); Debug.Assert((object)target != null); // SPEC: * Otherwise, if E is a method group and T is a delegate type or // SPEC: expression tree type with parameter types T1...Tk and return // SPEC: type Tb and overload resolution of E with the types T1...Tk // SPEC: yields a single method with return type U then a lower-bound // SPEC: inference is made from U to Tb. if (source.Kind != BoundKind.MethodGroup) { return false; } var delegateType = target.GetDelegateType(); if ((object)delegateType == null) { return false; } // this part of the code is only called if the targetType has an unfixed type argument in the output // type, which is not the case for invalid delegate invoke methods. Debug.Assert((object)delegateType.DelegateInvokeMethod != null && !delegateType.DelegateInvokeMethod.HasUseSiteError, "This method should only be called for valid delegate types"); TypeSymbol delegateReturnType = delegateType.DelegateInvokeMethod.ReturnType; if ((object)delegateReturnType == null || delegateReturnType.SpecialType == SpecialType.System_Void) { return false; } // At this point we are in the second phase; we know that all the input types are fixed. var fixedDelegateParameters = GetFixedDelegate(delegateType).DelegateParameters(); if (fixedDelegateParameters.IsDefault) { return false; } var returnType = MethodGroupReturnType(binder, (BoundMethodGroup)source, fixedDelegateParameters, ref useSiteDiagnostics); if ((object)returnType == null || returnType.SpecialType == SpecialType.System_Void) { return false; } LowerBoundInference(returnType, delegateReturnType, ref useSiteDiagnostics); return true; }
private bool InferredReturnTypeInference(BoundExpression source, TypeSymbol target, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(source != null); Debug.Assert((object)target != null); // SPEC: * If E is an anonymous function with inferred return type U and // SPEC: T is a delegate type or expression tree with return type Tb // SPEC: then a lower bound inference is made from U to Tb. var delegateType = target.GetDelegateType(); if ((object)delegateType == null) { return false; } // cannot be hit, because an invalid delegate does not have an unfixed return type // this will be checked earlier. Debug.Assert((object)delegateType.DelegateInvokeMethod != null && !delegateType.DelegateInvokeMethod.HasUseSiteError, "This method should only be called for valid delegate types."); var returnType = delegateType.DelegateInvokeMethod.ReturnType; if ((object)returnType == null || returnType.SpecialType == SpecialType.System_Void) { return false; } var inferredReturnType = InferReturnType(source, delegateType, ref useSiteDiagnostics); if ((object)inferredReturnType == null) { return false; } LowerBoundInference(inferredReturnType, returnType, ref useSiteDiagnostics); return true; }