private static bool PreventsSuccessfulDelegateConversion(FirstAmongEqualsSet <Diagnostic> set) { foreach (var diagnostic in set) { if (ErrorFacts.PreventsSuccessfulDelegateConversion((ErrorCode)diagnostic.Code)) { return(true); } } return(false); }
internal static bool PreventsSuccessfulDelegateConversion(DiagnosticBag diagnostics) { foreach (Diagnostic diag in diagnostics.AsEnumerable()) // Checking the code would have resolved them anyway. { if (ErrorFacts.PreventsSuccessfulDelegateConversion((ErrorCode)diag.Code)) { return(true); } } return(false); }
internal static bool PreventsSuccessfulDelegateConversion(ImmutableArray <Diagnostic> diagnostics) { foreach (var diag in diagnostics) { if (ErrorFacts.PreventsSuccessfulDelegateConversion((ErrorCode)diag.Code)) { return(true); } } return(false); }
/// <summary> /// What we need to do is find a *repeatable* arbitrary way to choose between /// two errors; we can for example simply take the one that is lower in alphabetical /// order when converted to a string. As an optimization, we compare error codes /// first and skip string comparison if they differ. /// </summary> private static int CanonicallyCompareDiagnostics(Diagnostic x, Diagnostic y) { ErrorCode xCode = (ErrorCode)x.Code; ErrorCode yCode = (ErrorCode)y.Code; int codeCompare = xCode.CompareTo(yCode); // ToString fails for a diagnostic with an error code that does not prevernt successful delegate conversion. // Also, the order doesn't matter, since all such diagnostics will be dropped. if (!ErrorFacts.PreventsSuccessfulDelegateConversion(xCode) || !ErrorFacts.PreventsSuccessfulDelegateConversion(yCode)) { return(codeCompare); } // Optimization: don't bother return(codeCompare == 0 ? string.CompareOrdinal(x.ToString(), y.ToString()) : codeCompare); }
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); }
public bool GenerateSummaryErrors(DiagnosticBag diagnostics) { // It is highly likely that "the same" error will be given for two different // bindings of the same lambda but with different values for the parameters // of the error. For example, if we have x=>x.Blah() where x could be int // or string, then the two errors will be "int does not have member Blah" and // "string does not have member Blah", but the locations and errors numbers // will be the same. // // We should first see if there is a set of errors that are "the same" by // this definition that occur in every lambda binding; if there are then // those are the errors we should report. // // If there are no errors that are common to *every* binding then we // can report the complete set of errors produced by every binding. However, // we still wish to avoid duplicates, so we will use the same logic for // building the union as the intersection; two errors with the same code // and location are to be treated as the same error and only reported once, // regardless of how that error is parameterized. // // The question then rears its head: when given two of "the same" error // to report that are nevertheless different in their arguments, which one // do we choose? To the user it hardly matters; either one points to the // right location in source code. But it surely matters to our testing team; // we do not want to be in a position where some small change to our internal // representation of lambdas causes tests to break because errors are reported // differently. // // What we need to do is find a *repeatable* arbitrary way to choose between // two errors; we can for example simply take the one that is lower in alphabetical // order when converted to a string. var equalityComparer = new CommonDiagnosticComparer(); Func <Diagnostic, Diagnostic, int> canonicalComparer = CanonicallyCompareDiagnostics; FirstAmongEqualsSet <Diagnostic> intersection = null; var convBags = from boundLambda in bindingCache.Values select boundLambda.Diagnostics; var retBags = from boundLambda in returnInferenceCache.Values select boundLambda.Diagnostics; var allBags = convBags.Concat(retBags); foreach (ImmutableArray <Diagnostic> bag in allBags) { if (intersection == null) { intersection = new FirstAmongEqualsSet <Diagnostic>(bag, equalityComparer, canonicalComparer); } else { intersection.IntersectWith(bag); } } if (intersection != null) { foreach (var diagnostic in intersection) { if (ErrorFacts.PreventsSuccessfulDelegateConversion((ErrorCode)diagnostic.Code)) { diagnostics.AddRange(intersection); return(true); } } } FirstAmongEqualsSet <Diagnostic> union = null; foreach (ImmutableArray <Diagnostic> bag in allBags) { if (union == null) { union = new FirstAmongEqualsSet <Diagnostic>(bag, equalityComparer, canonicalComparer); } else { union.UnionWith(bag); } } if (union != null) { foreach (var diagnostic in union) { if (ErrorFacts.PreventsSuccessfulDelegateConversion((ErrorCode)diagnostic.Code)) { diagnostics.AddRange(union); return(true); } } } return(false); }
private BoundLambda ReallyBind(NamedTypeSymbol delegateType) { var returnType = DelegateReturnType(delegateType); LambdaSymbol lambdaSymbol; Binder lambdaBodyBinder; BoundBlock block; var diagnostics = DiagnosticBag.GetInstance(); // when binding for real (not for return inference), there is still // a good chance that we could reuse a body of a lambda previously bound for // return type inference. MethodSymbol cacheKey = GetCacheKey(delegateType); BoundLambda returnInferenceLambda; if (returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType) { var lambdaSym = returnInferenceLambda.Symbol; var lambdaRetType = lambdaSym.ReturnType; if (lambdaRetType == returnType) { lambdaSymbol = lambdaSym; lambdaBodyBinder = returnInferenceLambda.Binder; block = returnInferenceLambda.Body; diagnostics.AddRange(returnInferenceLambda.Diagnostics); goto haveLambdaBodyAndBinders; } } var parameters = DelegateParameters(delegateType); lambdaSymbol = new LambdaSymbol(binder.Compilation, binder.ContainingMemberOrLambda, this.unboundLambda, parameters, returnType); lambdaBodyBinder = new ExecutableCodeBinder(this.unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder)); block = BindLambdaBody(lambdaSymbol, ref lambdaBodyBinder, diagnostics); ValidateUnsafeParameters(diagnostics, parameters); haveLambdaBodyAndBinders: bool reachableEndpoint = ControlFlowPass.Analyze(binder.Compilation, lambdaSymbol, block, diagnostics); if (reachableEndpoint) { if (DelegateNeedsReturn(delegateType)) { // Not all code paths return a value in {0} of type '{1}' diagnostics.Add(ErrorCode.ERR_AnonymousReturnExpected, lambdaSymbol.Locations[0], this.MessageID.Localize(), delegateType); } else { block = FlowAnalysisPass.AppendImplicitReturn(block, lambdaSymbol, this.unboundLambda.Syntax); } } if (IsAsync && !ErrorFacts.PreventsSuccessfulDelegateConversion(diagnostics)) { if ((object)returnType != null && // Can be null if "delegateType" is not actually a delegate type. returnType.SpecialType != SpecialType.System_Void && returnType != binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task) && returnType.OriginalDefinition != binder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_Task_T)) { // Cannot convert async {0} to delegate type '{1}'. An async {0} may return void, Task or Task<T>, none of which are convertible to '{1}'. diagnostics.Add(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, lambdaSymbol.Locations[0], lambdaSymbol.MessageID.Localize(), delegateType); } } if (IsAsync) { Debug.Assert(lambdaSymbol.IsAsync); SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol, diagnostics, lambdaSymbol.Locations[0]); } var result = new BoundLambda(this.unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType) { WasCompilerGenerated = this.unboundLambda.WasCompilerGenerated }; return(result); }
private BoundLambda ReallyBind(NamedTypeSymbol delegateType) { var invokeMethod = DelegateInvokeMethod(delegateType); RefKind refKind; var returnType = DelegateReturnType(invokeMethod, out refKind); LambdaSymbol lambdaSymbol; Binder lambdaBodyBinder; BoundBlock block; var diagnostics = DiagnosticBag.GetInstance(); // when binding for real (not for return inference), there is still // a good chance that we could reuse a body of a lambda previously bound for // return type inference. var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync); BoundLambda returnInferenceLambda; if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType) { lambdaSymbol = returnInferenceLambda.Symbol; if ((object)LambdaSymbol.InferenceFailureReturnType != lambdaSymbol.ReturnType && lambdaSymbol.ReturnType == returnType && lambdaSymbol.RefKind == refKind) { lambdaBodyBinder = returnInferenceLambda.Binder; block = returnInferenceLambda.Body; diagnostics.AddRange(returnInferenceLambda.Diagnostics); goto haveLambdaBodyAndBinders; } } lambdaSymbol = new LambdaSymbol( binder.Compilation, binder.ContainingMemberOrLambda, _unboundLambda, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds, refKind, returnType); lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder)); block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); ((ExecutableCodeBinder)lambdaBodyBinder).ValidateIteratorMethods(diagnostics); ValidateUnsafeParameters(diagnostics, cacheKey.ParameterTypes); haveLambdaBodyAndBinders: bool reachableEndpoint = ControlFlowPass.Analyze(binder.Compilation, lambdaSymbol, block, diagnostics); if (reachableEndpoint) { if (DelegateNeedsReturn(invokeMethod)) { // Not all code paths return a value in {0} of type '{1}' diagnostics.Add(ErrorCode.ERR_AnonymousReturnExpected, lambdaSymbol.DiagnosticLocation, this.MessageID.Localize(), delegateType); } else { block = FlowAnalysisPass.AppendImplicitReturn(block, lambdaSymbol); } } if (IsAsync && !ErrorFacts.PreventsSuccessfulDelegateConversion(diagnostics)) { if ((object)returnType != null && // Can be null if "delegateType" is not actually a delegate type. returnType.SpecialType != SpecialType.System_Void && !returnType.IsNonGenericTaskType(binder.Compilation) && !returnType.IsGenericTaskType(binder.Compilation)) { // Cannot convert async {0} to delegate type '{1}'. An async {0} may return void, Task or Task<T>, none of which are convertible to '{1}'. diagnostics.Add(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, lambdaSymbol.DiagnosticLocation, lambdaSymbol.MessageID.Localize(), delegateType); } } if (IsAsync) { Debug.Assert(lambdaSymbol.IsAsync); SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol.Parameters, diagnostics, lambdaSymbol.DiagnosticLocation); } var result = new BoundLambda(_unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnType: false) { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; return(result); }
private BoundLambda ReallyBind(NamedTypeSymbol delegateType) { var invokeMethod = DelegateInvokeMethod(delegateType); RefKind refKind; var returnType = DelegateReturnType(invokeMethod, out refKind); LambdaSymbol lambdaSymbol; Binder lambdaBodyBinder; BoundBlock block; var diagnostics = DiagnosticBag.GetInstance(); // when binding for real (not for return inference), there is still // a good chance that we could reuse a body of a lambda previously bound for // return type inference. MethodSymbol cacheKey = GetCacheKey(delegateType); BoundLambda returnInferenceLambda; if (_returnInferenceCache.TryGetValue(cacheKey, out returnInferenceLambda) && returnInferenceLambda.InferredFromSingleType && returnInferenceLambda.Symbol.ReturnType == returnType) { lambdaSymbol = returnInferenceLambda.Symbol; Debug.Assert(lambdaSymbol.RefKind == refKind); lambdaBodyBinder = returnInferenceLambda.Binder; block = returnInferenceLambda.Body; diagnostics.AddRange(returnInferenceLambda.Diagnostics); goto haveLambdaBodyAndBinders; } var parameters = DelegateParameters(invokeMethod); lambdaSymbol = new LambdaSymbol( binder.Compilation, binder.ContainingMemberOrLambda, _unboundLambda, parameters, refKind, returnType); lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, binder)); block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); ((ExecutableCodeBinder)lambdaBodyBinder).ValidateIteratorMethods(diagnostics); ValidateUnsafeParameters(diagnostics, parameters); haveLambdaBodyAndBinders: bool reachableEndpoint = ControlFlowPass.Analyze(binder.Compilation, lambdaSymbol, block, diagnostics); if (reachableEndpoint) { if (DelegateNeedsReturn(invokeMethod)) { // Not all code paths return a value in {0} of type '{1}' diagnostics.Add(ErrorCode.ERR_AnonymousReturnExpected, lambdaSymbol.Locations[0], this.MessageID.Localize(), delegateType); } else { block = FlowAnalysisPass.AppendImplicitReturn(block, lambdaSymbol); } } if (IsAsync && !ErrorFacts.PreventsSuccessfulDelegateConversion(diagnostics)) { if ((object)returnType != null && // Can be null if "delegateType" is not actually a delegate type. returnType.SpecialType != SpecialType.System_Void && !returnType.IsNonGenericTaskType(binder.Compilation) && !returnType.IsGenericTaskType(binder.Compilation)) { // Cannot convert async {0} to delegate type '{1}'. An async {0} may return void, Task or Task<T>, none of which are convertible to '{1}'. diagnostics.Add(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, lambdaSymbol.Locations[0], lambdaSymbol.MessageID.Localize(), delegateType); } } if (IsAsync) { Debug.Assert(lambdaSymbol.IsAsync); SourceMemberMethodSymbol.ReportAsyncParameterErrors(lambdaSymbol.Parameters, diagnostics, lambdaSymbol.Locations[0]); } // This is an attempt to get a repro for https://devdiv.visualstudio.com/DevDiv/_workitems?id=278481 if ((object)returnType != null && returnType.SpecialType != SpecialType.System_Void && !block.HasErrors && !diagnostics.HasAnyResolvedErrors() && block.Statements.Length > 0) { BoundStatement first = block.Statements[0]; if (first.Kind == BoundKind.ReturnStatement) { var returnStmt = (BoundReturnStatement)first; if (returnStmt.ExpressionOpt != null && (object)returnStmt.ExpressionOpt.Type == null) { throw ExceptionUtilities.Unreachable; } } } var result = new BoundLambda(_unboundLambda.Syntax, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferReturnType: false) { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; return(result); }