From 110254425aee3b2e4816ea7fffe4e97562da07f4 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 8 Jun 2021 14:49:35 -0700 Subject: [PATCH 1/8] Lambda expression explicit return type: binding --- .../Binder/Binder.QueryUnboundLambdaState.cs | 8 + .../CSharp/Portable/Binder/Binder_Lambda.cs | 44 +- .../Portable/Binder/Binder_Statements.cs | 13 +- .../Semantics/Conversions/ConversionsBase.cs | 11 +- .../Conversions/LambdaConversionResult.cs | 7 +- .../Binder/WithLambdaParametersBinder.cs | 26 +- .../Portable/BoundTree/UnboundLambda.cs | 187 ++++--- .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../CSharp/Portable/Errors/MessageID.cs | 2 + .../Portable/Symbols/Source/LambdaSymbol.cs | 3 + .../SourceMethodSymbolWithAttributes.cs | 57 ++- .../CSharp/Portable/Syntax/SyntaxFacts.cs | 3 + .../Portable/xlf/CSharpResources.cs.xlf | 10 + .../Portable/xlf/CSharpResources.de.xlf | 10 + .../Portable/xlf/CSharpResources.es.xlf | 10 + .../Portable/xlf/CSharpResources.fr.xlf | 10 + .../Portable/xlf/CSharpResources.it.xlf | 10 + .../Portable/xlf/CSharpResources.ja.xlf | 10 + .../Portable/xlf/CSharpResources.ko.xlf | 10 + .../Portable/xlf/CSharpResources.pl.xlf | 10 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 + .../Portable/xlf/CSharpResources.ru.xlf | 10 + .../Portable/xlf/CSharpResources.tr.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 + .../Semantic/Semantics/DelegateTypeTests.cs | 1 + .../Test/Semantic/Semantics/LambdaTests.cs | 481 ++++++++++++++++++ .../Parsing/LambdaReturnTypeParsingTests.cs | 71 +++ 29 files changed, 927 insertions(+), 124 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs index f81b856f417dc..b750043fad8c2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs @@ -35,6 +35,14 @@ public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap, public override SyntaxList ParameterAttributes(int index) => default; public override bool HasNames { get { return true; } } public override bool HasSignature { get { return true; } } + + public override bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations returnType) + { + refKind = default; + returnType = default; + return false; + } + public override bool HasExplicitlyTypedParameterList { get { return false; } } public override int ParameterCount { get { return _parameters.Length; } } public override bool IsAsync { get { return false; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 7eb03207090c9..48a513f367079 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -38,10 +38,12 @@ private UnboundLambda AnalyzeAnonymousFunction( Debug.Assert(syntax != null); Debug.Assert(syntax.IsAnonymousFunction()); - var names = default(ImmutableArray); - var refKinds = default(ImmutableArray); - var types = default(ImmutableArray); - var attributes = default(ImmutableArray>); + ImmutableArray names = default; + ImmutableArray refKinds = default; + ImmutableArray types = default; + RefKind returnRefKind = RefKind.None; + TypeWithAnnotations returnType = default; + ImmutableArray> parameterAttributes = default; var namesBuilder = ArrayBuilder.GetInstance(); ImmutableArray discardsOpt = default; @@ -67,6 +69,10 @@ private UnboundLambda AnalyzeAnonymousFunction( // (x, y) => ... hasSignature = true; var paren = (ParenthesizedLambdaExpressionSyntax)syntax; + if (paren.ReturnType is { } returnTypeSyntax) + { + (returnRefKind, returnType) = BindExplicitLambdaReturnType(returnTypeSyntax, diagnostics); + } parameterSyntaxList = paren.ParameterList.Parameters; CheckParenthesizedLambdaParameters(parameterSyntaxList.Value, diagnostics); break; @@ -187,7 +193,7 @@ private UnboundLambda AnalyzeAnonymousFunction( if (attributesBuilder.Any(a => a.Count > 0)) { - attributes = attributesBuilder.ToImmutable(); + parameterAttributes = attributesBuilder.ToImmutable(); } typesBuilder.Free(); @@ -202,7 +208,7 @@ private UnboundLambda AnalyzeAnonymousFunction( namesBuilder.Free(); - return new UnboundLambda(syntax, this, diagnostics.AccumulatesDependencies, attributes, refKinds, types, names, discardsOpt, isAsync, isStatic); + return new UnboundLambda(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, types, names, discardsOpt, isAsync, isStatic); static ImmutableArray computeDiscards(SeparatedSyntaxList parameters, int underscoresCount) { @@ -237,6 +243,32 @@ static void checkAttributes(AnonymousFunctionExpressionSyntax syntax, SyntaxList } } + private (RefKind, TypeWithAnnotations) BindExplicitLambdaReturnType(TypeSyntax syntax, BindingDiagnosticBag diagnostics) + { + MessageID.IDS_FeatureLambdaReturnType.CheckFeatureAvailability(diagnostics, syntax); + + RefKind refKind = RefKind.None; + if (syntax is RefTypeSyntax refTypeSyntax) + { + refKind = RefKind.Ref; + syntax = refTypeSyntax.Type; + } + + var returnType = BindType(syntax, diagnostics); + var type = returnType.Type; + + if (returnType.IsStatic) + { + diagnostics.Add(ErrorFacts.GetStaticClassReturnCode(useWarning: false), syntax.Location, type); + } + else if (returnType.IsRestrictedType(ignoreSpanLikeTypes: true)) + { + diagnostics.Add(ErrorCode.ERR_MethodReturnCantBeRefAny, syntax.Location, type); + } + + return (refKind, returnType); + } + private static void CheckParenthesizedLambdaParameters( SeparatedSyntaxList parameterSyntaxList, BindingDiagnosticBag diagnostics) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 233fcb3033edf..2baa95f094d44 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1809,6 +1809,7 @@ internal BoundExpression GenerateConversionForAssignment(TypeSymbol targetType, return CreateConversion(expression.Syntax, expression, conversion, isCast: false, conversionGroupOpt: null, targetType, diagnostics); } +#nullable enable internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diagnostics, SyntaxNode syntax, UnboundLambda anonymousFunction, TypeSymbol targetType) { @@ -1868,9 +1869,13 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag return; } - // At this point we know that we have either a delegate type or an expression type for the target. + if (reason == LambdaConversionResult.MismatchedReturnType) + { + Error(diagnostics, ErrorCode.ERR_CantConvAnonMethReturnType, syntax, id, targetType); + return; + } - var delegateType = targetType.GetDelegateType(); + // At this point we know that we have either a delegate type or an expression type for the target. // The target type is a valid delegate or expression tree type. Is there something wrong with the // parameter list? @@ -1896,6 +1901,9 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag return; } + var delegateType = targetType.GetDelegateType(); + Debug.Assert(delegateType is not null); + // There is a parameter list. Does it have the right number of elements? if (reason == LambdaConversionResult.BadParameterCount) @@ -2013,6 +2021,7 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag Debug.Assert(false, "Missing case in lambda conversion error reporting"); diagnostics.Add(ErrorCode.ERR_InternalError, syntax.Location); } +#nullable disable protected static void GenerateImplicitConversionError(BindingDiagnosticBag diagnostics, CSharpCompilation compilation, SyntaxNode syntax, Conversion conversion, TypeSymbol sourceType, TypeSymbol targetType, ConstantValue sourceConstantValueOpt = null) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index a7a247c33fff5..ac97e512467e1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -1246,7 +1246,6 @@ source.Type is object && IsNumericType(source.Type) && IsConstantNumericZero(sourceConstantValue); } -#nullable disable private static LambdaConversionResult IsAnonymousFunctionCompatibleWithDelegate(UnboundLambda anonymousFunction, TypeSymbol type) { @@ -1266,6 +1265,15 @@ private static LambdaConversionResult IsAnonymousFunctionCompatibleWithDelegate( return LambdaConversionResult.BadTargetType; } + if (anonymousFunction.HasExplicitReturnType(out var refKind, out var returnType)) + { + if (invokeMethod.RefKind != refKind || + !invokeMethod.ReturnType.Equals(returnType.Type, TypeCompareKind.AllIgnoreOptions)) + { + return LambdaConversionResult.MismatchedReturnType; + } + } + var delegateParameters = invokeMethod.Parameters; // SPEC: If F contains an anonymous-function-signature, then D and F have the same number of parameters. @@ -1426,6 +1434,7 @@ private static bool HasAnonymousFunctionConversion(BoundExpression source, TypeS return IsAnonymousFunctionCompatibleWithType((UnboundLambda)source, destination) == LambdaConversionResult.Success; } +#nullable disable internal Conversion ClassifyImplicitUserDefinedConversionForV6SwitchGoverningType(TypeSymbol sourceType, out TypeSymbol switchGoverningType, ref CompoundUseSiteInfo useSiteInfo) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/LambdaConversionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/LambdaConversionResult.cs index c75d6033426cd..5a10903cb935a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/LambdaConversionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/LambdaConversionResult.cs @@ -2,12 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; - namespace Microsoft.CodeAnalysis.CSharp { internal enum LambdaConversionResult @@ -16,6 +10,7 @@ internal enum LambdaConversionResult BadTargetType, BadParameterCount, MissingSignatureWithOutParameter, + MismatchedReturnType, MismatchedParameterType, RefInImplicitlyTypedLambda, StaticTypeInImplicitlyTypedLambda, diff --git a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs index e6ee9f1727492..1db796a46d6db 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithLambdaParametersBinder.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -18,7 +17,7 @@ internal class WithLambdaParametersBinder : LocalScopeBinder { protected readonly LambdaSymbol lambdaSymbol; protected readonly MultiDictionary parameterMap; - private SmallDictionary _definitionMap; + private readonly SmallDictionary _definitionMap; public WithLambdaParametersBinder(LambdaSymbol lambdaSymbol, Binder enclosing) : base(enclosing) @@ -29,24 +28,17 @@ public WithLambdaParametersBinder(LambdaSymbol lambdaSymbol, Binder enclosing) var parameters = lambdaSymbol.Parameters; if (!parameters.IsDefaultOrEmpty) { - recordDefinitions(parameters); - foreach (var parameter in lambdaSymbol.Parameters) + _definitionMap = new SmallDictionary(); + foreach (var parameter in parameters) { if (!parameter.IsDiscard) { - this.parameterMap.Add(parameter.Name, parameter); - } - } - } - - void recordDefinitions(ImmutableArray definitions) - { - var declarationMap = _definitionMap ??= new SmallDictionary(); - foreach (var s in definitions) - { - if (!s.IsDiscard && !declarationMap.ContainsKey(s.Name)) - { - declarationMap.Add(s.Name, s); + var name = parameter.Name; + this.parameterMap.Add(name, parameter); + if (!_definitionMap.ContainsKey(name)) + { + _definitionMap.Add(name, parameter); + } } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 7f03fb6cb86a0..fe41f29b8c412 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -64,7 +64,7 @@ internal sealed partial class BoundLambda : IBoundLambdaOrFunction { public MessageID MessageID { get { return Syntax.Kind() == SyntaxKind.AnonymousMethodExpression ? MessageID.IDS_AnonMethod : MessageID.IDS_Lambda; } } - internal InferredLambdaReturnType InferredReturnType { get; private set; } + internal InferredLambdaReturnType InferredReturnType { get; } MethodSymbol IBoundLambdaOrFunction.Symbol { get { return Symbol; } } @@ -165,6 +165,7 @@ internal static InferredLambdaReturnType InferReturnType(ArrayBuilder<(BoundRetu internal static InferredLambdaReturnType InferReturnType(ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)> returnTypes, UnboundLambda node, Binder binder, TypeSymbol? delegateType, bool isAsync, ConversionsBase conversions) { + Debug.Assert(!node.HasExplicitReturnType(out _, out _)); return InferReturnTypeImpl(returnTypes, node, binder, delegateType, isAsync, conversions, node.WithDependencies); } @@ -342,14 +343,16 @@ public UnboundLambda( CSharpSyntaxNode syntax, Binder binder, bool withDependencies, - ImmutableArray> attributes, + RefKind returnRefKind, + TypeWithAnnotations returnType, + ImmutableArray> parameterAttributes, ImmutableArray refKinds, ImmutableArray types, ImmutableArray names, ImmutableArray discardsOpt, bool isAsync, bool isStatic) - : this(syntax, new PlainUnboundLambdaState(binder, attributes, names, discardsOpt, types, refKinds, isAsync, isStatic, includeCache: true), withDependencies, !types.IsDefault && types.Any(t => t.Type?.Kind == SymbolKind.ErrorType)) + : this(syntax, new PlainUnboundLambdaState(binder, returnRefKind, returnType, parameterAttributes, names, discardsOpt, types, refKinds, isAsync, isStatic, includeCache: true), withDependencies, !types.IsDefault && types.Any(t => t.Type?.Kind == SymbolKind.ErrorType)) { Debug.Assert(binder != null); Debug.Assert(syntax.IsAnonymousFunction()); @@ -401,6 +404,8 @@ private BoundLambda SuppressIfNeeded(BoundLambda lambda) => this.IsSuppressed ? (BoundLambda)lambda.WithSuppression() : lambda; public bool HasSignature { get { return Data.HasSignature; } } + public bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations returnType) + => Data.HasExplicitReturnType(out refKind, out returnType); public bool HasExplicitlyTypedParameterList { get { return Data.HasExplicitlyTypedParameterList; } } public int ParameterCount { get { return Data.ParameterCount; } } public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref CompoundUseSiteInfo useSiteInfo) @@ -478,6 +483,7 @@ internal UnboundLambdaState WithCaching(bool includeCache) public abstract bool ParameterIsDiscard(int index); public abstract SyntaxList ParameterAttributes(int index); public abstract bool HasSignature { get; } + public abstract bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations returnType); public abstract bool HasExplicitlyTypedParameterList { get; } public abstract int ParameterCount { get; } public abstract bool IsAsync { get; } @@ -575,49 +581,54 @@ private bool DelegateNeedsReturn(MethodSymbol? invokeMethod) { Debug.Assert(Binder.ContainingMemberOrLambda is { }); - var compilation = Binder.Compilation; + if (!HasExplicitlyTypedParameterList) + { + return null; + } + var parameterRefKindsBuilder = ArrayBuilder.GetInstance(ParameterCount); var parameterTypesBuilder = ArrayBuilder.GetInstance(ParameterCount); for (int i = 0; i < ParameterCount; i++) { - parameterRefKindsBuilder.Add(HasExplicitlyTypedParameterList ? RefKind(i) : default); - parameterTypesBuilder.Add(HasExplicitlyTypedParameterList ? ParameterTypeWithAnnotations(i) : TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Object))); + parameterRefKindsBuilder.Add(RefKind(i)); + parameterTypesBuilder.Add(ParameterTypeWithAnnotations(i)); } var parameterRefKinds = parameterRefKindsBuilder.ToImmutableAndFree(); var parameterTypes = parameterTypesBuilder.ToImmutableAndFree(); - var lambdaSymbol = new LambdaSymbol( - Binder, - compilation, - Binder.ContainingMemberOrLambda, - _unboundLambda, - parameterTypes, - parameterRefKinds, - refKind: default, - returnType: default); - var lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, Binder)); - var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, BindingDiagnosticBag.Discarded); - var returnTypes = ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>.GetInstance(); - BoundLambda.BlockReturns.GetReturnTypes(returnTypes, block); - var inferredReturnType = BoundLambda.InferReturnType( - returnTypes, - _unboundLambda, - lambdaBodyBinder, - delegateType: null, - isAsync: IsAsync, - Binder.Conversions); - - if (HasExplicitlyTypedParameterList && (inferredReturnType.TypeWithAnnotations.HasType || returnTypes.Count == 0)) + if (!HasExplicitReturnType(out var returnRefKind, out var returnType)) { - return Binder.GetMethodGroupOrLambdaDelegateType( - inferredReturnType.RefKind, - inferredReturnType.TypeWithAnnotations, - parameterRefKinds, + var lambdaSymbol = new LambdaSymbol( + Binder, + Binder.Compilation, + Binder.ContainingMemberOrLambda, + _unboundLambda, parameterTypes, - ref useSiteInfo); + parameterRefKinds, + refKind: default, + returnType: default); + var lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, Binder)); + var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, BindingDiagnosticBag.Discarded); + var returnTypes = ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>.GetInstance(); + BoundLambda.BlockReturns.GetReturnTypes(returnTypes, block); + var inferredReturnType = BoundLambda.InferReturnType( + returnTypes, + _unboundLambda, + lambdaBodyBinder, + delegateType: null, + isAsync: IsAsync, + Binder.Conversions); + + returnType = inferredReturnType.TypeWithAnnotations; + returnRefKind = inferredReturnType.RefKind; + + if (!returnType.HasType && returnTypes.Count > 0) + { + return null; + } } - return null; + return Binder.GetMethodGroupOrLambdaDelegateType(returnRefKind, returnType, parameterRefKinds, parameterTypes, ref useSiteInfo); } private BoundLambda ReallyBind(NamedTypeSymbol delegateType) @@ -715,12 +726,6 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType) } } - if (IsAsync) - { - Debug.Assert(lambdaSymbol.IsAsync); - lambdaSymbol.ReportAsyncParameterErrors(diagnostics, lambdaSymbol.DiagnosticLocation); - } - var result = new BoundLambda(_unboundLambda.Syntax, _unboundLambda, block, diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, inferredReturnType: default) { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; @@ -747,8 +752,8 @@ internal LambdaSymbol CreateLambdaSymbol(NamedTypeSymbol delegateType, Symbol co { var invokeMethod = DelegateInvokeMethod(delegateType); var returnType = DelegateReturnTypeWithAnnotations(invokeMethod, out RefKind refKind); - var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync); - return CreateLambdaSymbol(containingSymbol, returnType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds, refKind); + ReturnInferenceCacheKey.GetFields(delegateType, IsAsync, out var parameterTypes, out var parameterRefKinds, out _); + return CreateLambdaSymbol(containingSymbol, returnType, parameterTypes, parameterRefKinds, refKind); } private void ValidateUnsafeParameters(BindingDiagnosticBag diagnostics, ImmutableArray targetParameterTypes) @@ -781,10 +786,39 @@ private BoundLambda ReallyInferReturnType( ImmutableArray parameterTypes, ImmutableArray parameterRefKinds) { - (var lambdaSymbol, var block, var lambdaBodyBinder, var diagnostics) = BindWithParameterAndReturnType(parameterTypes, parameterRefKinds, returnType: default, refKind: CodeAnalysis.RefKind.None); - var returnTypes = ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>.GetInstance(); - BoundLambda.BlockReturns.GetReturnTypes(returnTypes, block); - var inferredReturnType = BoundLambda.InferReturnType(returnTypes, _unboundLambda, lambdaBodyBinder, delegateType, lambdaSymbol.IsAsync, lambdaBodyBinder.Conversions); + bool hasExplicitReturnType = HasExplicitReturnType(out var refKind, out var returnType); + (var lambdaSymbol, var block, var lambdaBodyBinder, var diagnostics) = BindWithParameterAndReturnType(parameterTypes, parameterRefKinds, returnType, refKind); + InferredLambdaReturnType inferredReturnType; + if (hasExplicitReturnType) + { + // The InferredLambdaReturnType fields other than RefKind and ReturnType + // are only used when actually inferring a type, not when the type is explicit. + inferredReturnType = new InferredLambdaReturnType( + numExpressions: 0, + hadExpressionlessReturn: false, + refKind, + returnType, + ImmutableArray.Empty, + ImmutableArray.Empty); + } + else + { + var returnTypes = ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>.GetInstance(); + BoundLambda.BlockReturns.GetReturnTypes(returnTypes, block); + inferredReturnType = BoundLambda.InferReturnType(returnTypes, _unboundLambda, lambdaBodyBinder, delegateType, lambdaSymbol.IsAsync, lambdaBodyBinder.Conversions); + // TODO: Should InferredReturnType.UseSiteDiagnostics be merged into BoundLambda.Diagnostics? + refKind = inferredReturnType.RefKind; + returnType = inferredReturnType.TypeWithAnnotations; + if (!returnType.HasType) + { + bool forErrorRecovery = delegateType is null; + returnType = (forErrorRecovery && returnTypes.Count == 0) + ? TypeWithAnnotations.Create(this.Binder.Compilation.GetSpecialType(SpecialType.System_Void)) + : TypeWithAnnotations.Create(LambdaSymbol.InferenceFailureReturnType); + } + returnTypes.Free(); + } + var result = new BoundLambda( _unboundLambda.Syntax, _unboundLambda, @@ -795,18 +829,11 @@ private BoundLambda ReallyInferReturnType( inferredReturnType) { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; - // TODO: Should InferredReturnType.UseSiteDiagnostics be merged into BoundLambda.Diagnostics? - var returnType = inferredReturnType.TypeWithAnnotations; - if (!returnType.HasType) + if (!hasExplicitReturnType) { - bool forErrorRecovery = delegateType is null; - returnType = (forErrorRecovery && returnTypes.Count == 0) - ? TypeWithAnnotations.Create(this.Binder.Compilation.GetSpecialType(SpecialType.System_Void)) - : TypeWithAnnotations.Create(LambdaSymbol.InferenceFailureReturnType); + lambdaSymbol.SetInferredReturnType(refKind, returnType); } - lambdaSymbol.SetInferredReturnType(inferredReturnType.RefKind, returnType); - returnTypes.Free(); return result; } @@ -824,6 +851,7 @@ private BoundLambda ReallyInferReturnType( refKind); var lambdaBodyBinder = new ExecutableCodeBinder(_unboundLambda.Syntax, lambdaSymbol, ParameterBinder(lambdaSymbol, Binder)); var block = BindLambdaBody(lambdaSymbol, lambdaBodyBinder, diagnostics); + lambdaSymbol.GetDeclarationDiagnostics(diagnostics); return (lambdaSymbol, block, lambdaBodyBinder, diagnostics); } @@ -900,12 +928,27 @@ public override int GetHashCode() } public static ReturnInferenceCacheKey Create(NamedTypeSymbol? delegateType, bool isAsync) + { + GetFields(delegateType, isAsync, out var parameterTypes, out var parameterRefKinds, out var taskLikeReturnTypeOpt); + if (parameterTypes.IsEmpty && parameterRefKinds.IsEmpty && taskLikeReturnTypeOpt is null) + { + return Empty; + } + return new ReturnInferenceCacheKey(parameterTypes, parameterRefKinds, taskLikeReturnTypeOpt); + } + + public static void GetFields( + NamedTypeSymbol? delegateType, + bool isAsync, + out ImmutableArray parameterTypes, + out ImmutableArray parameterRefKinds, + out NamedTypeSymbol? taskLikeReturnTypeOpt) { // delegateType or DelegateInvokeMethod can be null in cases of malformed delegates // in such case we would want something trivial with no parameters - var parameterTypes = ImmutableArray.Empty; - var parameterRefKinds = ImmutableArray.Empty; - NamedTypeSymbol? taskLikeReturnTypeOpt = null; + parameterTypes = ImmutableArray.Empty; + parameterRefKinds = ImmutableArray.Empty; + taskLikeReturnTypeOpt = null; MethodSymbol? invoke = DelegateInvokeMethod(delegateType); if (invoke is not null) { @@ -937,13 +980,6 @@ public static ReturnInferenceCacheKey Create(NamedTypeSymbol? delegateType, bool } } } - - if (parameterTypes.IsEmpty && parameterRefKinds.IsEmpty && taskLikeReturnTypeOpt is null) - { - return Empty; - } - - return new ReturnInferenceCacheKey(parameterTypes, parameterRefKinds, taskLikeReturnTypeOpt); } } @@ -996,7 +1032,7 @@ private BoundLambda ReallyBindForErrorRecovery() return GuessBestBoundLambda(_bindingCache!) ?? rebind(GuessBestBoundLambda(_returnInferenceCache!)) - ?? rebind(ReallyInferReturnType(null, ImmutableArray.Empty, ImmutableArray.Empty)); + ?? rebind(ReallyInferReturnType(delegateType: null, ImmutableArray.Empty, ImmutableArray.Empty)); // Rebind a lambda to push target conversions through the return/result expressions [return: NotNullIfNotNull("lambda")] BoundLambda? rebind(BoundLambda? lambda) @@ -1004,8 +1040,8 @@ private BoundLambda ReallyBindForErrorRecovery() if (lambda is null) return null; var delegateType = (NamedTypeSymbol?)lambda.Type; - var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync); - return ReallyBindForErrorRecovery(delegateType, lambda.InferredReturnType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds); + ReturnInferenceCacheKey.GetFields(delegateType, IsAsync, out var parameterTypes, out var parameterRefKinds, out _); + return ReallyBindForErrorRecovery(delegateType, lambda.InferredReturnType, parameterTypes, parameterRefKinds); } } @@ -1221,6 +1257,8 @@ private static int CanonicallyCompareDiagnostics(Diagnostic x, Diagnostic y) internal sealed class PlainUnboundLambdaState : UnboundLambdaState { + private readonly RefKind _returnRefKind; + private readonly TypeWithAnnotations _returnType; private readonly ImmutableArray> _parameterAttributes; private readonly ImmutableArray _parameterNames; private readonly ImmutableArray _parameterIsDiscardOpt; @@ -1231,6 +1269,8 @@ internal sealed class PlainUnboundLambdaState : UnboundLambdaState internal PlainUnboundLambdaState( Binder binder, + RefKind returnRefKind, + TypeWithAnnotations returnType, ImmutableArray> parameterAttributes, ImmutableArray parameterNames, ImmutableArray parameterIsDiscardOpt, @@ -1241,6 +1281,8 @@ internal PlainUnboundLambdaState( bool includeCache) : base(binder, includeCache) { + _returnRefKind = returnRefKind; + _returnType = returnType; _parameterAttributes = parameterAttributes; _parameterNames = parameterNames; _parameterIsDiscardOpt = parameterIsDiscardOpt; @@ -1254,6 +1296,13 @@ internal PlainUnboundLambdaState( public override bool HasSignature { get { return !_parameterNames.IsDefault; } } + public override bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations returnType) + { + refKind = _returnRefKind; + returnType = _returnType; + return _returnType.HasType; + } + public override bool HasExplicitlyTypedParameterList { get { return !_parameterTypesWithAnnotations.IsDefault; } } public override int ParameterCount { get { return _parameterNames.IsDefault ? 0 : _parameterNames.Length; } } @@ -1321,7 +1370,7 @@ public override TypeWithAnnotations ParameterTypeWithAnnotations(int index) protected override UnboundLambdaState WithCachingCore(bool includeCache) { - return new PlainUnboundLambdaState(Binder, _parameterAttributes, _parameterNames, _parameterIsDiscardOpt, _parameterTypesWithAnnotations, _parameterRefKinds, _isAsync, _isStatic, includeCache); + return new PlainUnboundLambdaState(Binder, _returnRefKind, _returnType, _parameterAttributes, _parameterNames, _parameterIsDiscardOpt, _parameterTypesWithAnnotations, _parameterRefKinds, _isAsync, _isStatic, includeCache); } protected override BoundExpression? GetLambdaExpressionBody(BoundBlock body) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 6a05f90e59e15..df1d0e32f0122 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3019,6 +3019,9 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Cannot convert {0} to type '{1}' because the parameter types do not match the delegate parameter types + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type @@ -6613,6 +6616,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ lambda attributes + + lambda return type + inferred delegate type diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 63fdfd9bee009..4cb0f3ad4805e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1938,6 +1938,7 @@ internal enum ErrorCode ERR_GlobalUsingOutOfOrder = 8915, ERR_AttributesRequireParenthesizedLambdaExpression = 8916, ERR_CannotInferDelegateType = 8917, + ERR_CantConvAnonMethReturnType = 8918, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 6272644a9d2d3..0087403da4d3a 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -224,6 +224,7 @@ internal enum MessageID IDS_FeatureInferredDelegateType = MessageBase + 12799, IDS_FeatureLambdaAttributes = MessageBase + 12800, IDS_FeatureWithOnAnonymousTypes = MessageBase + 12801, + IDS_FeatureLambdaReturnType = MessageBase + 12802, } // Message IDs may refer to strings that need to be localized. @@ -340,6 +341,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureGlobalUsing: case MessageID.IDS_FeatureInferredDelegateType: // semantic check case MessageID.IDS_FeatureLambdaAttributes: // semantic check + case MessageID.IDS_FeatureLambdaReturnType: // semantic check return LanguageVersion.Preview; // C# 9.0 features. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index 5a19816808171..0cabd9baf1867 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -235,6 +235,8 @@ internal Location DiagnosticLocation } } + private bool HasExplicitReturnType => (Syntax as ParenthesizedLambdaExpressionSyntax)?.ReturnType is not null; + public override ImmutableArray DeclaringSyntaxReferences { get @@ -281,6 +283,7 @@ internal void GetDeclarationDiagnostics(BindingDiagnosticBag addTo) GetAttributes(); GetReturnTypeAttributes(); + AsyncMethodChecks(verifyReturnType: HasExplicitReturnType, DiagnosticLocation, _declarationDiagnostics); addTo.AddRange(_declarationDiagnostics, allowMismatchInDependencyAccumulation: true); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 503c3ffdaf5e6..3d79898d1cf41 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -975,7 +975,6 @@ static UnmanagedCallersOnlyAttributeData DecodeUnmanagedCallersOnlyAttributeData return UnmanagedCallersOnlyAttributeData.Create(callingConventionTypes); } } -#nullable disable internal sealed override void PostDecodeWellKnownAttributes(ImmutableArray boundAttributes, ImmutableArray allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) { @@ -1032,38 +1031,50 @@ internal sealed override void PostDecodeWellKnownAttributes(ImmutableArray methodReturnType, - LocalFunctionStatementSyntax { ReturnType: var localReturnType } => localReturnType, - var unexpected => throw ExceptionUtilities.UnexpectedValue(unexpected) - }; + var returnTypeSyntax = this.SyntaxNode switch + { + MethodDeclarationSyntax { ReturnType: var methodReturnType } => methodReturnType, + LocalFunctionStatementSyntax { ReturnType: var localReturnType } => localReturnType, + ParenthesizedLambdaExpressionSyntax { ReturnType: { } lambdaReturnType } => lambdaReturnType, + var unexpected => throw ExceptionUtilities.UnexpectedValue(unexpected) + }; - ReportBadRefToken(returnTypeSyntax, diagnostics); - hasErrors = true; - } - else if (ReturnType.IsBadAsyncReturn(this.DeclaringCompilation)) - { - diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, errorLocation); - hasErrors = true; + ReportBadRefToken(returnTypeSyntax, diagnostics); + hasErrors = true; + } + else if (ReturnType.IsBadAsyncReturn(this.DeclaringCompilation)) + { + diagnostics.Add(ErrorCode.ERR_BadAsyncReturn, errorLocation); + hasErrors = true; + } } - for (NamedTypeSymbol curr = this.ContainingType; (object)curr != null; curr = curr.ContainingType) + // Avoid checking attributes on containing types to avoid a potential cycle when a lambda + // is used in an attribute argument - see https://github.com/dotnet/roslyn/issues/54074. + if (this.MethodKind != MethodKind.LambdaMethod) { - var sourceNamedTypeSymbol = curr as SourceNamedTypeSymbol; - if ((object)sourceNamedTypeSymbol != null && sourceNamedTypeSymbol.HasSecurityCriticalAttributes) + for (NamedTypeSymbol curr = this.ContainingType; (object)curr != null; curr = curr.ContainingType) { - diagnostics.Add(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsyncInClassOrStruct, errorLocation); - hasErrors = true; - break; + if (curr is SourceNamedTypeSymbol { HasSecurityCriticalAttributes: true }) + { + diagnostics.Add(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsyncInClassOrStruct, errorLocation); + hasErrors = true; + break; + } } } @@ -1204,13 +1215,13 @@ internal override bool HasDeclarativeSecurity return SpecializedCollections.EmptyEnumerable(); } - public override DllImportData GetDllImportData() + public override DllImportData? GetDllImportData() { var data = this.GetDecodedWellKnownAttributeData(); return data != null ? data.DllImportPlatformInvokeData : null; } - internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation + internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation { get { diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs index 16e0365bcad28..4fc04e7e4efba 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs @@ -183,6 +183,9 @@ public static bool IsInTypeOnlyContext(ExpressionSyntax node) case LocalFunctionStatement: return ((LocalFunctionStatementSyntax)parent).ReturnType == node; + case ParenthesizedLambdaExpression: + return ((ParenthesizedLambdaExpressionSyntax)parent).ReturnType == node; + case SimpleBaseType: return true; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 4dd8850d512fe..c9a5659d12d58 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -207,6 +207,11 @@ {0} musí odpovídat vlastnosti jenom pro inicializaci přepsaného člena {1}. + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist nemůže mít argument předávaný pomocí in nebo out @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 87df0b3c48442..0ef79f17d34d4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -207,6 +207,11 @@ "{0}" muss mit der init-Zugriffsmethode des außer Kraft gesetzten Members "{1}" übereinstimmen. + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' "__arglist" darf kein über "in" oder "out" übergebenes Argument umfassen. @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 152290f4c9c0e..17590d3e3f645 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -207,6 +207,11 @@ "{0}" debe coincidir por solo inicialización del miembro invalidado "{1}" + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist no puede tener un argumento que se ha pasado con "in" o "out" @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 4c722d4180d60..8657071f987de 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -207,6 +207,11 @@ '{0}' doit correspondre par initialisation uniquement au membre substitué '{1}' + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist ne peut pas avoir un argument passé par 'in' ou 'out' @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index c5fe96b48b4bd..91b84be130955 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -207,6 +207,11 @@ '{0}' deve corrispondere per sola inizializzazione del membro '{1}' di cui è stato eseguito l'override + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist non può contenere un argomento passato da 'in' o 'out' @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index ae625bf8f8479..85133bb720e97 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -207,6 +207,11 @@ '{0}' は、オーバーライドされたメンバー '{1}' と同じく、初期化専用である必要があります + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist では、'in' や 'out' で引数を渡すことができません @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 8f0f818383244..ae0faadce06e9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -207,6 +207,11 @@ '{0}'은(는) 재정의된 멤버 '{1}'의 초기화 전용으로 일치해야 합니다. + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist는 'in' 또는 'out'으로 전달되는 인수를 가질 수 없습니다. @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index c7e04062445c4..e32beb01bb590 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -207,6 +207,11 @@ Element „{0}” musi odpowiadać zmiennymi tylko do inicjowania przesłoniętej składowej „{1}” + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' Element __arglist nie może mieć argumentu przekazywanego przez parametr „in” ani „out” @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index abf8209997982..d421a652e6109 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -207,6 +207,11 @@ '{0}' precisa corresponder por somente de inicialização do membro substituído '{1}' + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist não pode ter um argumento passado por 'in' ou 'out' @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 0e780bbea42ee..3869560136b94 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -207,6 +207,11 @@ "{0}" и переопределяемый элемент "{1}" должны соответствовать по методу доступа, вызываемому только во время инициализации. + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' В __arglist невозможно передать аргумент с помощью in или out @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 9ab6883e83acc..aa5690fe6ed8c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -207,6 +207,11 @@ '{0}', geçersiz kılınmış '{1}' üyesinin yalnızca init öğesi bakımından eşleşmelidir + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist, 'in' veya 'out' tarafından geçirilen bir bağımsız değişkene sahip olamaz @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index d6d00dde3bc9b..bcea5a4f7899e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -207,6 +207,11 @@ “{0}”必须与重写成员“{1}”的“仅 init”匹配 + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist 不能有 "in" 或 "out" 传递的参数 @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 8356fd02a7354..4c7b8eb4ae439 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -207,6 +207,11 @@ '{0}' 必須符合被覆寫之成員 '{1}' 的僅供初始化 + + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + Cannot convert {0} to type '{1}' because the return type does not match the delegate return type + + __arglist cannot have an argument passed by 'in' or 'out' __arglist 不得包含 'in' 或 'out' 傳遞的引數 @@ -1017,6 +1022,11 @@ lambda attributes + + lambda return type + lambda return type + + positional fields in records positional fields in records diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 6c601d03f70a6..54cd101a7cdf2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -303,6 +303,7 @@ static void Main() yield return getData("(int i) => { if (i > 0) return i; return default; }", "System.Func"); yield return getData("(int x, short y) => { if (x > 0) return x; return y; }", "System.Func"); yield return getData("(int x, short y) => { if (x > 0) return y; return x; }", "System.Func"); + yield return getData("object () => default", "System.Func"); static object?[] getData(string expr, string? expectedType) => new object?[] { expr, expectedType }; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index c73417b967423..57e2cd1bbfd2d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -4329,6 +4329,487 @@ static void Main() Assert.Equal(FlowAnalysisAnnotations.NotNullWhenTrue, lambda.Parameters[0].FlowAnalysisAnnotations); } + [Fact] + public void LambdaReturnType_01() + { + var source = +@"using System; +class Program +{ + static void F() + { + Func f1 = T () => default; + Func f2 = T (x) => { return x; }; + Func f3 = T (T x) => x; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (6,22): error CS8652: The feature 'lambda return type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func f1 = T () => default; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("lambda return type").WithLocation(6, 22), + // (7,25): error CS8652: The feature 'lambda return type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func f2 = T (x) => { return x; }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("lambda return type").WithLocation(7, 25), + // (8,25): error CS8652: The feature 'lambda return type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func f3 = T (T x) => x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "T").WithArguments("lambda return type").WithLocation(8, 25)); + + comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + } + + [Fact] + public void LambdaReturnType_02() + { + var source = +@"using System; +class Program +{ + static void F() + { + Func f1; + Func f2; + f1 = T () => default; + f2 = T () => default; + f1 = U () => default; + f2 = U () => default; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (9,14): error CS8918: Cannot convert lambda expression to type 'Func' because the return type does not match the delegate return type + // f2 = T () => default; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "T () => default").WithArguments("lambda expression", "System.Func").WithLocation(9, 14), + // (10,14): error CS8918: Cannot convert lambda expression to type 'Func' because the return type does not match the delegate return type + // f1 = U () => default; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "U () => default").WithArguments("lambda expression", "System.Func").WithLocation(10, 14)); + } + + [Fact] + public void LambdaReturnType_03() + { + var source = +@"using System; +using System.Linq.Expressions; +class Program +{ + static void F() + { + Expression> e1; + Expression> e2; + e1 = T () => default; + e2 = T () => default; + e1 = U () => default; + e2 = U () => default; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (10,14): error CS8918: Cannot convert lambda expression to type 'Expression>' because the return type does not match the delegate return type + // e2 = T () => default; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "T () => default").WithArguments("lambda expression", "System.Linq.Expressions.Expression>").WithLocation(10, 14), + // (11,14): error CS8918: Cannot convert lambda expression to type 'Expression>' because the return type does not match the delegate return type + // e1 = U () => default; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "U () => default").WithArguments("lambda expression", "System.Linq.Expressions.Expression>").WithLocation(11, 14)); + } + + [Fact] + public void LambdaReturnType_04() + { + var source = +@"#nullable enable +using System; +class Program +{ + static void Main() + { + Func f1 = object () => default!; + Func<(int, int)> f2 = (int X, int Y) () => default; + Func f3 = string () => default!; + Func f4 = nint () => default; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + } + + [Fact] + public void LambdaReturnType_05() + { + var source = +@"#nullable enable +using System; +using System.Linq.Expressions; +class Program +{ + static void Main() + { + Expression> e1 = dynamic () => default!; + Expression> e2 = (int, int) () => default; + Expression> e3 = string? () => default; + Expression> e4 = IntPtr () => default; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + // PROTOTYPE: Should not report nullability warning. + comp.VerifyDiagnostics( + // (10,53): warning CS8603: Possible null reference return. + // Expression> e3 = string? () => default; + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(10, 53)); + } + + [Fact] + public void LambdaReturnType_06() + { + var source = +@"delegate T D1(ref T t); +delegate ref T D2(ref T t); +class Program +{ + static void F() + { + D1 d1; + D2 d2; + d1 = T (ref T t) => t; + d2 = T (ref T t) => t; + d1 = (ref T (ref T t) => ref t); + d2 = (ref T (ref T t) => ref t); + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (10,14): error CS8918: Cannot convert lambda expression to type 'D2' because the return type does not match the delegate return type + // d2 = T (ref T t) => t; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "T (ref T t) => t").WithArguments("lambda expression", "D2").WithLocation(10, 14), + // (11,15): error CS8918: Cannot convert lambda expression to type 'D1' because the return type does not match the delegate return type + // d1 = (ref T (ref T t) => ref t); + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "ref T (ref T t) => ref t").WithArguments("lambda expression", "D1").WithLocation(11, 15)); + } + + [Fact] + public void LambdaReturnType_07() + { + var source = +@"using System; +class Program +{ + static void Main() + { + Delegate d; + d = (ref void () => { }); + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (7,14): error CS8917: The delegate type could not be inferred. + // d = (ref void () => { }); + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "ref void () => { }").WithLocation(7, 14), + // (7,18): error CS1547: Keyword 'void' cannot be used in this context + // d = (ref void () => { }); + Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(7, 18)); + } + + [ConditionalFact(typeof(DesktopOnly))] + public void LambdaReturnType_08() + { + var source = +@"using System; +class Program +{ + static void Main() + { + Delegate d; + d = TypedReference () => throw null; + d = RuntimeArgumentHandle () => throw null; + d = ArgIterator () => throw null; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (7,13): error CS1599: The return type of a method, delegate, or function pointer cannot be 'TypedReference' + // d = TypedReference () => throw null; + Diagnostic(ErrorCode.ERR_MethodReturnCantBeRefAny, "TypedReference").WithArguments("System.TypedReference").WithLocation(7, 13), + // (7,13): error CS8917: The delegate type could not be inferred. + // d = TypedReference () => throw null; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "TypedReference () => throw null").WithLocation(7, 13), + // (8,13): error CS1599: The return type of a method, delegate, or function pointer cannot be 'RuntimeArgumentHandle' + // d = RuntimeArgumentHandle () => throw null; + Diagnostic(ErrorCode.ERR_MethodReturnCantBeRefAny, "RuntimeArgumentHandle").WithArguments("System.RuntimeArgumentHandle").WithLocation(8, 13), + // (8,13): error CS8917: The delegate type could not be inferred. + // d = RuntimeArgumentHandle () => throw null; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "RuntimeArgumentHandle () => throw null").WithLocation(8, 13), + // (9,13): error CS1599: The return type of a method, delegate, or function pointer cannot be 'ArgIterator' + // d = ArgIterator () => throw null; + Diagnostic(ErrorCode.ERR_MethodReturnCantBeRefAny, "ArgIterator").WithArguments("System.ArgIterator").WithLocation(9, 13), + // (9,13): error CS8917: The delegate type could not be inferred. + // d = ArgIterator () => throw null; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "ArgIterator () => throw null").WithLocation(9, 13)); + } + + [Fact] + public void LambdaReturnType_09() + { + var source = +@"static class S { } +delegate S D(); +class Program +{ + static void Main() + { + D d = S () => default; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (7,15): error CS0722: 'S': static types cannot be used as return types + // D d = S () => default; + Diagnostic(ErrorCode.ERR_ReturnTypeIsStaticClass, "S").WithArguments("S").WithLocation(7, 15)); + } + + [Fact] + public void LambdaReturnType_10() + { + var source = +@"using System; +class Program +{ + static void Main() + { + Delegate d = async int () => 0; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (6,35): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator + // Delegate d = async int () => 0; + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=>").WithLocation(6, 35), + // (6,35): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. + // Delegate d = async int () => 0; + Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(6, 35)); + } + + [Fact] + public void LambdaReturnType_11() + { + var source = +@"using System; +using System.Threading.Tasks; +delegate ref Task D(string s); +class Program +{ + static void Main() + { + Delegate d1 = async ref Task (s) => { _ = s.Length; await Task.Yield(); }; + D d2 = async ref Task (s) => { _ = s.Length; await Task.Yield(); }; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (8,23): error CS8917: The delegate type could not be inferred. + // Delegate d1 = async ref Task (s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "async ref Task (s) => { _ = s.Length; await Task.Yield(); }").WithLocation(8, 23), + // (8,29): error CS1073: Unexpected token 'ref' + // Delegate d1 = async ref Task (s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(8, 29), + // (9,22): error CS1073: Unexpected token 'ref' + // D d2 = async ref Task (s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 22)); + } + + [Fact] + public void LambdaReturnType_12() + { + var source = +@"using System; +using System.Threading.Tasks; +delegate ref Task D(string s); +class Program +{ + static void Main() + { + Delegate d1 = async ref Task (string s) => { _ = s.Length; await Task.Yield(); }; + D d2 = async ref Task (string s) => { _ = s.Length; await Task.Yield(); }; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (8,23): error CS8917: The delegate type could not be inferred. + // Delegate d1 = async ref Task (string s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "async ref Task (string s) => { _ = s.Length; await Task.Yield(); }").WithLocation(8, 23), + // (8,29): error CS1073: Unexpected token 'ref' + // Delegate d1 = async ref Task (string s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(8, 29), + // (9,22): error CS1073: Unexpected token 'ref' + // D d2 = async ref Task (string s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 22)); + } + + // PROTOTYPE: Test use-site error from explicit return type. + + [Fact] + public void AsyncLambdaParameters_01() + { + var source = +@"using System; +using System.Threading.Tasks; +delegate Task D(ref string s); +class Program +{ + static void Main() + { + Delegate d1 = async (ref string s) => { _ = s.Length; await Task.Yield(); }; + D d2 = async (ref string s) => { _ = s.Length; await Task.Yield(); }; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (8,23): error CS8917: The delegate type could not be inferred. + // Delegate d1 = async (ref string s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "async (ref string s) => { _ = s.Length; await Task.Yield(); }").WithLocation(8, 23), + // (8,41): error CS1988: Async methods cannot have ref, in or out parameters + // Delegate d1 = async (ref string s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "s").WithLocation(8, 41), + // (9,34): error CS1988: Async methods cannot have ref, in or out parameters + // D d2 = async (ref string s) => { _ = s.Length; await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "s").WithLocation(9, 34)); + } + + [ConditionalFact(typeof(DesktopOnly))] + public void AsyncLambdaParameters_02() + { + var source = +@"using System; +using System.Threading.Tasks; +delegate void D1(TypedReference r); +delegate void D2(RuntimeArgumentHandle h); +delegate void D3(ArgIterator i); +class Program +{ + static void Main() + { + D1 d1 = async (TypedReference r) => { await Task.Yield(); }; + D2 d2 = async (RuntimeArgumentHandle h) => { await Task.Yield(); }; + D3 d3 = async (ArgIterator i) => { await Task.Yield(); }; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (10,39): error CS4012: Parameters or locals of type 'TypedReference' cannot be declared in async methods or async lambda expressions. + // D1 d1 = async (TypedReference r) => { await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "r").WithArguments("System.TypedReference").WithLocation(10, 39), + // (11,46): error CS4012: Parameters or locals of type 'RuntimeArgumentHandle' cannot be declared in async methods or async lambda expressions. + // D2 d2 = async (RuntimeArgumentHandle h) => { await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 46), + // (12,36): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + // D3 d3 = async (ArgIterator i) => { await Task.Yield(); }; + Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "i").WithArguments("System.ArgIterator").WithLocation(12, 36)); + } + + [Fact] + public void BestType_01() + { + var source = +@"using System; +class A { } +class B1 : A { } +class B2 : A { } +interface I { } +class C1 : I { } +class C2 : I { } +class Program +{ + static void F(Func f) { } + static void Main() + { + F((bool b) => { if (b) return new B1(); return new B2(); }); + F((bool b) => { if (b) return new C1(); return new C2(); }); + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (13,9): error CS0411: The type arguments for method 'Program.F(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F((bool b) => { if (b) return new B1(); return new B2(); }); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(System.Func)").WithLocation(13, 9), + // (14,9): error CS0411: The type arguments for method 'Program.F(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F((bool b) => { if (b) return new C1(); return new C2(); }); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(System.Func)").WithLocation(14, 9)); + } + + // As above but with explicit return type. + [Fact] + public void BestType_02() + { + var source = +@"using System; +class A { } +class B1 : A { } +class B2 : A { } +interface I { } +class C1 : I { } +class C2 : I { } +class Program +{ + static void F(Func f) { Console.WriteLine(typeof(T)); } + static void Main() + { + F(A (bool b) => { if (b) return new B1(); return new B2(); }); + F(I (bool b) => { if (b) return new C1(); return new C2(); }); + } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: +@"A +I"); + } + + // CS4031 is not reported for async lambda in [SecurityCritical] type. + [Fact] + [WorkItem(54074, "https://github.com/dotnet/roslyn/issues/54074")] + public void SecurityCritical_AsyncLambda() + { + var source = +@"using System; +using System.Security; +using System.Threading.Tasks; +[SecurityCritical] +class Program +{ + static void Main() + { + Func f = async () => await Task.Yield(); + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + // CS4031 is not reported for async lambda in [SecurityCritical] type. + [Fact] + [WorkItem(54074, "https://github.com/dotnet/roslyn/issues/54074")] + public void SecurityCritical_AsyncLambda_AttributeArgument() + { + var source = +@"using System; +using System.Security; +using System.Threading.Tasks; +class A : Attribute +{ + internal A(int i) { } +} +[SecurityCritical] +[A(F(async () => await Task.Yield()))] +class Program +{ + internal static int F(Func f) => 0; +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (9,4): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [A(F(async () => await Task.Yield()))] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "F(async () => await Task.Yield())").WithLocation(9, 4)); + } + private static LambdaSymbol GetLambdaSymbol(SemanticModel model, LambdaExpressionSyntax syntax) { return model.GetSymbolInfo(syntax).Symbol.GetSymbol(); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs index ea9c6d38bf71d..5bd62ae6507be 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs @@ -3582,6 +3582,77 @@ public void Async_06() EOF(); } + [Fact] + public void Dynamic_01() + { + string source = "dynamic () => default"; + UsingExpression(source, TestOptions.Regular9); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "dynamic"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + } + EOF(); + } + + [Fact] + public void Dynamic_02() + { + string source = "Delegate d = dynamic () => default;"; + UsingDeclaration(source, TestOptions.Regular9); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Delegate"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "d"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "dynamic"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] public void Attributes_01(string modifiers, params SyntaxKind[] modifierKinds) From 9b2d9ed574127c8d16d44e163e8cfc8e90b23803 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Mon, 14 Jun 2021 14:24:43 -0700 Subject: [PATCH 2/8] Update NullableWalker --- .../Portable/FlowAnalysis/NullableWalker.cs | 62 +++++++-- .../Portable/Symbols/Source/LambdaSymbol.cs | 9 +- .../Test/Semantic/Semantics/LambdaTests.cs | 119 ++++++++++++++++-- 3 files changed, 167 insertions(+), 23 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index aec39b98d0afa..b16af5ac7d2aa 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -143,6 +143,12 @@ public VisitArgumentResult(VisitResult visitResult, Optional stateFo /// private bool _useDelegateInvokeParameterTypes; + /// + /// If true, the return type and nullability from _delegateInvokeMethod is used. + /// If false, the signature of CurrentSymbol is used instead. + /// + private bool _useDelegateInvokeReturnType; + /// /// Method signature used for return or parameter types. Distinct from CurrentSymbol signature /// when CurrentSymbol is a lambda and type is inferred from MethodTypeInferrer. @@ -377,6 +383,7 @@ private NullableWalker( Symbol? symbol, bool useConstructorExitWarnings, bool useDelegateInvokeParameterTypes, + bool useDelegateInvokeReturnType, MethodSymbol? delegateInvokeMethodOpt, BoundNode node, Binder binder, @@ -395,6 +402,7 @@ private NullableWalker( _conversions = (Conversions)conversions.WithNullability(true); _useConstructorExitWarnings = useConstructorExitWarnings; _useDelegateInvokeParameterTypes = useDelegateInvokeParameterTypes; + _useDelegateInvokeReturnType = useDelegateInvokeReturnType; _delegateInvokeMethod = delegateInvokeMethodOpt; _analyzedNullabilityMapOpt = analyzedNullabilityMapOpt; _returnTypesOpt = returnTypesOpt; @@ -1073,6 +1081,7 @@ private static void Analyze( diagnostics, useConstructorExitWarnings, useDelegateInvokeParameterTypes: false, + useDelegateInvokeReturnType: false, delegateInvokeMethodOpt: null, initialState: initialNullableState, analyzedNullabilityMapOpt: null, @@ -1176,6 +1185,7 @@ private static (SnapshotManager?, ImmutableDictionary.Builder? analyzedNullabilityMapOpt, @@ -1380,6 +1395,7 @@ private static void Analyze( symbol, useConstructorExitWarnings, useDelegateInvokeParameterTypes, + useDelegateInvokeReturnType, delegateInvokeMethodOpt, node, binder, @@ -2524,7 +2540,7 @@ private bool TryGetReturnType(out TypeWithAnnotations type, out FlowAnalysisAnno return false; } - var delegateOrMethod = _delegateInvokeMethod ?? method; + var delegateOrMethod = _useDelegateInvokeReturnType ? _delegateInvokeMethod : method; var returnType = delegateOrMethod.ReturnTypeWithAnnotations; Debug.Assert((object)returnType != LambdaSymbol.ReturnTypeIsBeingInferred); @@ -2650,7 +2666,8 @@ private void VisitStatementsWithLocalFunctions(BoundBlock block) localFunc, state, delegateInvokeMethod: null, - useDelegateInvokeParameterTypes: false); + useDelegateInvokeParameterTypes: false, + useDelegateInvokeReturnType: false); SetInvalidResult(); @@ -2674,16 +2691,21 @@ private void AnalyzeLocalFunctionOrLambda( MethodSymbol lambdaOrFunctionSymbol, LocalState state, MethodSymbol? delegateInvokeMethod, - bool useDelegateInvokeParameterTypes) + bool useDelegateInvokeParameterTypes, + bool useDelegateInvokeReturnType) { + Debug.Assert(!useDelegateInvokeParameterTypes || delegateInvokeMethod is object); + Debug.Assert(!useDelegateInvokeReturnType || delegateInvokeMethod is object); + var oldSymbol = this.CurrentSymbol; this.CurrentSymbol = lambdaOrFunctionSymbol; - Debug.Assert(!useDelegateInvokeParameterTypes || delegateInvokeMethod is object); var oldDelegateInvokeMethod = _delegateInvokeMethod; _delegateInvokeMethod = delegateInvokeMethod; var oldUseDelegateInvokeParameterTypes = _useDelegateInvokeParameterTypes; _useDelegateInvokeParameterTypes = useDelegateInvokeParameterTypes; + var oldUseDelegateInvokeReturnType = _useDelegateInvokeReturnType; + _useDelegateInvokeReturnType = useDelegateInvokeReturnType; var oldReturnTypes = _returnTypesOpt; _returnTypesOpt = null; @@ -2721,6 +2743,7 @@ private void AnalyzeLocalFunctionOrLambda( _variables = _variables.Container!; this.State = oldState; _returnTypesOpt = oldReturnTypes; + _useDelegateInvokeReturnType = oldUseDelegateInvokeReturnType; _useDelegateInvokeParameterTypes = oldUseDelegateInvokeParameterTypes; _delegateInvokeMethod = oldDelegateInvokeMethod; this.CurrentSymbol = oldSymbol; @@ -3471,6 +3494,7 @@ internal static TypeWithAnnotations BestTypeForLambdaReturns( symbol: null, useConstructorExitWarnings: false, useDelegateInvokeParameterTypes: false, + useDelegateInvokeReturnType: false, delegateInvokeMethodOpt: null, node, binder, @@ -6566,6 +6590,15 @@ private void ReportNullabilityMismatchWithTargetDelegate(Location location, Name } Debug.Assert(delegateType is object); + + if (unboundLambda.HasExplicitReturnType(out _, out var returnType) && + IsNullabilityMismatch(invoke.ReturnTypeWithAnnotations, returnType, requireIdentity: true)) + { + ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, location, + unboundLambda.MessageID.Localize(), + delegateType); + } + int count = Math.Min(invoke.ParameterCount, unboundLambda.ParameterCount); for (int i = 0; i < count; i++) { @@ -7536,7 +7569,7 @@ private void VisitLambda(BoundLambda node, NamedTypeSymbol? delegateTypeOpt, Opt Debug.Assert(delegateTypeOpt?.IsDelegateType() != false); var delegateInvokeMethod = delegateTypeOpt?.DelegateInvokeMethod; - bool useDelegateInvokeParameterTypes = UseDelegateInvokeParameterTypes(node, delegateInvokeMethod); + UseDelegateInvokeParameterAndReturnTypes(node, delegateInvokeMethod, out bool useDelegateInvokeParameterTypes, out bool useDelegateInvokeReturnType); if (useDelegateInvokeParameterTypes && _snapshotBuilderOpt is object) { SetUpdatedSymbol(node, node.Symbol, delegateTypeOpt!); @@ -7547,12 +7580,23 @@ private void VisitLambda(BoundLambda node, NamedTypeSymbol? delegateTypeOpt, Opt node.Symbol, initialState.HasValue ? initialState.Value : State.Clone(), delegateInvokeMethod, - useDelegateInvokeParameterTypes); + useDelegateInvokeParameterTypes, + useDelegateInvokeReturnType); } - private static bool UseDelegateInvokeParameterTypes(BoundLambda lambda, MethodSymbol? delegateInvokeMethod) + private static void UseDelegateInvokeParameterAndReturnTypes(BoundLambda lambda, MethodSymbol? delegateInvokeMethod, out bool useDelegateInvokeParameterTypes, out bool useDelegateInvokeReturnType) { - return delegateInvokeMethod is object && !lambda.UnboundLambda.HasExplicitlyTypedParameterList; + if (delegateInvokeMethod is null) + { + useDelegateInvokeParameterTypes = false; + useDelegateInvokeReturnType = false; + } + else + { + var unboundLambda = lambda.UnboundLambda; + useDelegateInvokeParameterTypes = !unboundLambda.HasExplicitlyTypedParameterList; + useDelegateInvokeReturnType = !unboundLambda.HasExplicitReturnType(out _, out _); + } } public override BoundNode? VisitUnboundLambda(UnboundLambda node) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index 0cabd9baf1867..f90c26848618b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -49,8 +49,11 @@ public LambdaSymbol( _binder = binder; _containingSymbol = containingSymbol; _messageID = unboundLambda.Data.MessageID; - _refKind = refKind; - _returnType = !returnType.HasType ? TypeWithAnnotations.Create(ReturnTypeIsBeingInferred) : returnType; + if (!unboundLambda.HasExplicitReturnType(out _refKind, out _returnType)) + { + _refKind = refKind; + _returnType = !returnType.HasType ? TypeWithAnnotations.Create(ReturnTypeIsBeingInferred) : returnType; + } _isSynthesized = unboundLambda.WasCompilerGenerated; _isAsync = unboundLambda.IsAsync; _isStatic = unboundLambda.IsStatic; @@ -317,7 +320,7 @@ private ImmutableArray MakeParameters( for (int p = 0; p < unboundLambda.ParameterCount; ++p) { - // If there are no types given in the lambda then used the delegate type. + // If there are no types given in the lambda then use the delegate type. // If the lambda is typed then the types probably match the delegate types; // if they do not, use the lambda types for binding. Either way, if we // can, then we use the lambda types. (Whatever you do, do not use the names diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 57e2cd1bbfd2d..fddc71d760242 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -4432,7 +4432,10 @@ static void Main() } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (9,28): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func' (possibly because of nullability attributes). + // Func f3 = string () => default!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "string () => default!").WithArguments("lambda expression", "System.Func").WithLocation(9, 28)); } [Fact] @@ -4453,17 +4456,85 @@ static void Main() } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); - // PROTOTYPE: Should not report nullability warning. comp.VerifyDiagnostics( - // (10,53): warning CS8603: Possible null reference return. + // (10,39): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func' (possibly because of nullability attributes). // Expression> e3 = string? () => default; - Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(10, 53)); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "string? () => default").WithArguments("lambda expression", "System.Func").WithLocation(10, 39)); } [Fact] public void LambdaReturnType_06() { var source = +@"#nullable enable +using System; +struct S { } +class Program +{ + static void Main() + { + Delegate d1 = string? () => default; + Delegate d2 = string () => default; + Delegate d3 = S () => default(S); + Delegate d4 = S () => default(S); + Delegate d5 = S () => default(S); + Delegate d6 = S () => default(S); + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (9,36): warning CS8603: Possible null reference return. + // Delegate d2 = string () => default; + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(9, 36), + // (11,40): warning CS8619: Nullability of reference types in value of type 'S' doesn't match target type 'S'. + // Delegate d4 = S () => default(S); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "default(S)").WithArguments("S", "S").WithLocation(11, 40), + // (12,39): warning CS8619: Nullability of reference types in value of type 'S' doesn't match target type 'S'. + // Delegate d5 = S () => default(S); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "default(S)").WithArguments("S", "S").WithLocation(12, 39)); + } + + [Fact] + public void LambdaReturnType_07() + { + var source = +@"#nullable enable +using System; +struct S { } +class Program +{ + static void Main() + { + Func f1 = string? () => throw null!; + Func f2 = string () => throw null!; + Func f3 = string? () => throw null!; + Func f4 = string () => throw null!; + Func> f5 = S () => throw null!; + Func> f6 = S () => throw null!; + Func> f7 = S () => throw null!; + Func> f8 = S () => throw null!; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (9,28): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func' (possibly because of nullability attributes). + // Func f2 = string () => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "string () => throw null!").WithArguments("lambda expression", "System.Func").WithLocation(9, 28), + // (10,27): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func' (possibly because of nullability attributes). + // Func f3 = string? () => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "string? () => throw null!").WithArguments("lambda expression", "System.Func").WithLocation(10, 27), + // (13,31): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func>' (possibly because of nullability attributes). + // Func> f6 = S () => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "S () => throw null!").WithArguments("lambda expression", "System.Func>").WithLocation(13, 31), + // (14,30): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func>' (possibly because of nullability attributes). + // Func> f7 = S () => throw null!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "S () => throw null!").WithArguments("lambda expression", "System.Func>").WithLocation(14, 30)); + } + + [Fact] + public void LambdaReturnType_08() + { + var source = @"delegate T D1(ref T t); delegate ref T D2(ref T t); class Program @@ -4489,7 +4560,7 @@ static void F() } [Fact] - public void LambdaReturnType_07() + public void LambdaReturnType_09() { var source = @"using System; @@ -4512,7 +4583,7 @@ static void Main() } [ConditionalFact(typeof(DesktopOnly))] - public void LambdaReturnType_08() + public void LambdaReturnType_10() { var source = @"using System; @@ -4549,7 +4620,7 @@ static void Main() } [Fact] - public void LambdaReturnType_09() + public void LambdaReturnType_11() { var source = @"static class S { } @@ -4569,7 +4640,7 @@ static void Main() } [Fact] - public void LambdaReturnType_10() + public void LambdaReturnType_12() { var source = @"using System; @@ -4591,7 +4662,7 @@ static void Main() } [Fact] - public void LambdaReturnType_11() + public void LambdaReturnType_13() { var source = @"using System; @@ -4619,7 +4690,7 @@ static void Main() } [Fact] - public void LambdaReturnType_12() + public void LambdaReturnType_14() { var source = @"using System; @@ -4646,7 +4717,33 @@ static void Main() Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 22)); } - // PROTOTYPE: Test use-site error from explicit return type. + [Fact] + public void LambdaReturnType_UseSiteErrors() + { + var sourceA = +@".class public sealed A extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.RequiredAttributeAttribute::.ctor(class [mscorlib]System.Type) = ( 01 00 FF 00 00 ) + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"using System; +class B +{ + static void F(Func f) { } + static void Main() + { + F(A () => default); + } +}"; + var comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (7,11): error CS0648: 'A' is a type not supported by the language + // F(A () => default); + Diagnostic(ErrorCode.ERR_BogusType, "A").WithArguments("A").WithLocation(7, 11)); + } [Fact] public void AsyncLambdaParameters_01() From f8f6c7a756fe4d68facf086dccd0631ea7478bdf Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Mon, 14 Jun 2021 21:51:31 -0700 Subject: [PATCH 3/8] Fix warning --- src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index b16af5ac7d2aa..8ded48d0973cd 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -2540,7 +2540,7 @@ private bool TryGetReturnType(out TypeWithAnnotations type, out FlowAnalysisAnno return false; } - var delegateOrMethod = _useDelegateInvokeReturnType ? _delegateInvokeMethod : method; + var delegateOrMethod = _useDelegateInvokeReturnType ? _delegateInvokeMethod! : method; var returnType = delegateOrMethod.ReturnTypeWithAnnotations; Debug.Assert((object)returnType != LambdaSymbol.ReturnTypeIsBeingInferred); From 08d21a9d5d60359fa6a66647e96e2f5284fa929e Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Mon, 14 Jun 2021 23:13:46 -0700 Subject: [PATCH 4/8] PR feedback --- .../CSharp/Portable/Binder/Binder_Lambda.cs | 8 +- .../SourceMethodSymbolWithAttributes.cs | 3 +- .../Semantic/Semantics/DelegateTypeTests.cs | 1 + .../Test/Semantic/Semantics/LambdaTests.cs | 121 ++++++++++++++++-- .../CommonMethodWellKnownAttributeData.cs | 15 +-- 5 files changed, 118 insertions(+), 30 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 48a513f367079..ee4dad5e40adf 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -247,13 +247,7 @@ static void checkAttributes(AnonymousFunctionExpressionSyntax syntax, SyntaxList { MessageID.IDS_FeatureLambdaReturnType.CheckFeatureAvailability(diagnostics, syntax); - RefKind refKind = RefKind.None; - if (syntax is RefTypeSyntax refTypeSyntax) - { - refKind = RefKind.Ref; - syntax = refTypeSyntax.Type; - } - + syntax = syntax.SkipRef(out RefKind refKind); var returnType = BindType(syntax, diagnostics); var type = returnType.Type; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 3d79898d1cf41..307d90fc6c3e6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using System.Threading; @@ -1205,7 +1204,7 @@ internal override bool HasDeclarativeSecurity var wellKnownData = (MethodWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData; if (wellKnownData != null) { - SecurityWellKnownAttributeData securityData = wellKnownData.SecurityInformation; + SecurityWellKnownAttributeData? securityData = wellKnownData.SecurityInformation; if (securityData != null) { return securityData.GetSecurityAttributes(attributesBag.Attributes); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 54cd101a7cdf2..02010ef5ab222 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -304,6 +304,7 @@ static void Main() yield return getData("(int x, short y) => { if (x > 0) return x; return y; }", "System.Func"); yield return getData("(int x, short y) => { if (x > 0) return y; return x; }", "System.Func"); yield return getData("object () => default", "System.Func"); + yield return getData("void () => { }", "System.Action"); static object?[] getData(string expr, string? expectedType) => new object?[] { expr, expectedType }; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index fddc71d760242..8ce140c30cec1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -4392,6 +4392,33 @@ public void LambdaReturnType_03() { var source = @"using System; +class Program +{ + static void F() where U : T + { + Func f1; + Func f2; + f1 = T () => default; + f2 = T () => default; + f1 = U () => default; + f2 = U () => default; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (9,14): error CS8918: Cannot convert lambda expression to type 'Func' because the return type does not match the delegate return type + // f2 = T () => default; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "T () => default").WithArguments("lambda expression", "System.Func").WithLocation(9, 14), + // (10,14): error CS8918: Cannot convert lambda expression to type 'Func' because the return type does not match the delegate return type + // f1 = U () => default; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "U () => default").WithArguments("lambda expression", "System.Func").WithLocation(10, 14)); + } + + [Fact] + public void LambdaReturnType_04() + { + var source = +@"using System; using System.Linq.Expressions; class Program { @@ -4416,7 +4443,7 @@ static void F() } [Fact] - public void LambdaReturnType_04() + public void LambdaReturnType_05() { var source = @"#nullable enable @@ -4439,7 +4466,7 @@ static void Main() } [Fact] - public void LambdaReturnType_05() + public void LambdaReturnType_06() { var source = @"#nullable enable @@ -4463,7 +4490,7 @@ static void Main() } [Fact] - public void LambdaReturnType_06() + public void LambdaReturnType_07() { var source = @"#nullable enable @@ -4495,7 +4522,7 @@ static void Main() } [Fact] - public void LambdaReturnType_07() + public void LambdaReturnType_08() { var source = @"#nullable enable @@ -4532,7 +4559,7 @@ static void Main() } [Fact] - public void LambdaReturnType_08() + public void LambdaReturnType_09() { var source = @"delegate T D1(ref T t); @@ -4560,7 +4587,7 @@ static void F() } [Fact] - public void LambdaReturnType_09() + public void LambdaReturnType_10() { var source = @"using System; @@ -4583,7 +4610,7 @@ static void Main() } [ConditionalFact(typeof(DesktopOnly))] - public void LambdaReturnType_10() + public void LambdaReturnType_11() { var source = @"using System; @@ -4620,7 +4647,7 @@ static void Main() } [Fact] - public void LambdaReturnType_11() + public void LambdaReturnType_12() { var source = @"static class S { } @@ -4640,7 +4667,7 @@ static void Main() } [Fact] - public void LambdaReturnType_12() + public void LambdaReturnType_13() { var source = @"using System; @@ -4662,7 +4689,7 @@ static void Main() } [Fact] - public void LambdaReturnType_13() + public void LambdaReturnType_14() { var source = @"using System; @@ -4690,7 +4717,7 @@ static void Main() } [Fact] - public void LambdaReturnType_14() + public void LambdaReturnType_15() { var source = @"using System; @@ -4860,6 +4887,78 @@ static void Main() I"); } + [Fact] + public void BestType_03() + { + var source = +@"using System; +class A { } +class B1 : A { } +class B2 : A { } +class Program +{ + static void F(Func x, Func y) { } + static void Main() + { + F(B2 () => null, B2 () => null); + F(A () => null, B2 () => null); + F(B1 () => null, B2 () => null); + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (11,25): error CS8918: Cannot convert lambda expression to type 'Func' because the return type does not match the delegate return type + // F(A () => null, B2 () => null); + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "B2 () => null").WithArguments("lambda expression", "System.Func").WithLocation(11, 25), + // (12,9): error CS0411: The type arguments for method 'Program.F(Func, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F(B1 () => null, B2 () => null); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(System.Func, System.Func)").WithLocation(12, 9)); + } + + [Fact] + public void TypeInference_01() + { + var source = +@"using System; +class Program +{ + static void F(Func f) + { + Console.WriteLine(typeof(T)); + } + static void Main() + { + F(long (o) => 1); + } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: @"System.Int64"); + } + + // Should method type inference make an explicit type inference + // from the lambda expression return type? + [Fact] + public void TypeInference_02() + { + var source = +@"using System; +class Program +{ + static void F(Func f) + { + Console.WriteLine(typeof(T)); + } + static void Main() + { + F(int (i) => i); + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (10,9): error CS0411: The type arguments for method 'Program.F(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F(int (i) => i); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(System.Func)").WithLocation(10, 9)); + } + // CS4031 is not reported for async lambda in [SecurityCritical] type. [Fact] [WorkItem(54074, "https://github.com/dotnet/roslyn/issues/54074")] diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodWellKnownAttributeData.cs index d4fb46418911b..a4c0da5526f9c 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonMethodWellKnownAttributeData.cs @@ -2,13 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Diagnostics; using System.Reflection; -using System.Runtime.CompilerServices; -using Microsoft.CodeAnalysis.Text; -using Cci = Microsoft.Cci; namespace Microsoft.CodeAnalysis { @@ -36,7 +31,7 @@ public CommonMethodWellKnownAttributeData() private readonly bool _preserveSigFirstWriteWins; // data from DllImportAttribute - private DllImportData _platformInvokeInfo; + private DllImportData? _platformInvokeInfo; private bool _dllImportPreserveSig; private int _dllImportIndex; // -1 .. not specified @@ -67,7 +62,7 @@ public void SetMethodImplementation(int attributeIndex, MethodImplAttributes att } // used by DllImportAttribute - public void SetDllImport(int attributeIndex, string moduleName, string entryPointName, MethodImportAttributes flags, bool preserveSig) + public void SetDllImport(int attributeIndex, string? moduleName, string? entryPointName, MethodImportAttributes flags, bool preserveSig) { VerifySealed(expected: false); Debug.Assert(attributeIndex >= 0); @@ -77,7 +72,7 @@ public void SetDllImport(int attributeIndex, string moduleName, string entryPoin SetDataStored(); } - public DllImportData DllImportPlatformInvokeData + public DllImportData? DllImportPlatformInvokeData { get { @@ -181,7 +176,7 @@ public bool HasSuppressUnmanagedCodeSecurityAttribute #endregion #region Security Attributes - private SecurityWellKnownAttributeData _lazySecurityAttributeData; + private SecurityWellKnownAttributeData? _lazySecurityAttributeData; SecurityWellKnownAttributeData ISecurityAttributeTarget.GetOrCreateData() { @@ -208,7 +203,7 @@ internal bool HasDeclarativeSecurity /// /// Returns data decoded from security attributes or null if there are no security attributes. /// - public SecurityWellKnownAttributeData SecurityInformation + public SecurityWellKnownAttributeData? SecurityInformation { get { From a7520104b96620f89a751752d375032362792145 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 15 Jun 2021 15:15:40 -0700 Subject: [PATCH 5/8] Add tests --- .../Test/Semantic/Semantics/LambdaTests.cs | 77 ++++++++++++++++--- .../Parsing/LambdaReturnTypeParsingTests.cs | 68 ++++++++++++---- 2 files changed, 121 insertions(+), 24 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 8ce140c30cec1..05541eca039a3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -4562,32 +4562,89 @@ static void Main() public void LambdaReturnType_09() { var source = +@"#nullable enable +struct S { } +delegate ref T D1(); +delegate ref readonly T D2(); +class Program +{ + static void Main() + { + D1> f1 = (ref S () => throw null!); + D1> f2 = (ref S () => throw null!); + D1> f3 = (ref S () => throw null!); + D1> f4 = (ref S () => throw null!); + D2> f5 = (ref readonly S () => throw null!); + D2> f6 = (ref readonly S () => throw null!); + D2> f7 = (ref readonly S () => throw null!); + D2> f8 = (ref readonly S () => throw null!); + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (10,30): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'D1>' (possibly because of nullability attributes). + // D1> f2 = (ref S () => throw null!); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "ref S () => throw null!").WithArguments("lambda expression", "D1>").WithLocation(10, 30), + // (11,29): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'D1>' (possibly because of nullability attributes). + // D1> f3 = (ref S () => throw null!); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "ref S () => throw null!").WithArguments("lambda expression", "D1>").WithLocation(11, 29), + // (14,30): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'D2>' (possibly because of nullability attributes). + // D2> f6 = (ref readonly S () => throw null!); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "ref readonly S () => throw null!").WithArguments("lambda expression", "D2>").WithLocation(14, 30), + // (15,29): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'D2>' (possibly because of nullability attributes). + // D2> f7 = (ref readonly S () => throw null!); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "ref readonly S () => throw null!").WithArguments("lambda expression", "D2>").WithLocation(15, 29)); + } + + [Fact] + public void LambdaReturnType_10() + { + var source = @"delegate T D1(ref T t); delegate ref T D2(ref T t); +delegate ref readonly T D3(ref T t); class Program { static void F() { D1 d1; D2 d2; + D3 d3; d1 = T (ref T t) => t; d2 = T (ref T t) => t; + d3 = T (ref T t) => t; d1 = (ref T (ref T t) => ref t); d2 = (ref T (ref T t) => ref t); + d3 = (ref T (ref T t) => ref t); + d1 = (ref readonly T (ref T t) => ref t); + d2 = (ref readonly T (ref T t) => ref t); + d3 = (ref readonly T (ref T t) => ref t); } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( - // (10,14): error CS8918: Cannot convert lambda expression to type 'D2' because the return type does not match the delegate return type + // (12,14): error CS8918: Cannot convert lambda expression to type 'D2' because the return type does not match the delegate return type // d2 = T (ref T t) => t; - Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "T (ref T t) => t").WithArguments("lambda expression", "D2").WithLocation(10, 14), - // (11,15): error CS8918: Cannot convert lambda expression to type 'D1' because the return type does not match the delegate return type + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "T (ref T t) => t").WithArguments("lambda expression", "D2").WithLocation(12, 14), + // (13,14): error CS8918: Cannot convert lambda expression to type 'D3' because the return type does not match the delegate return type + // d3 = T (ref T t) => t; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "T (ref T t) => t").WithArguments("lambda expression", "D3").WithLocation(13, 14), + // (14,15): error CS8918: Cannot convert lambda expression to type 'D1' because the return type does not match the delegate return type // d1 = (ref T (ref T t) => ref t); - Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "ref T (ref T t) => ref t").WithArguments("lambda expression", "D1").WithLocation(11, 15)); + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "ref T (ref T t) => ref t").WithArguments("lambda expression", "D1").WithLocation(14, 15), + // (16,15): error CS8918: Cannot convert lambda expression to type 'D3' because the return type does not match the delegate return type + // d3 = (ref T (ref T t) => ref t); + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "ref T (ref T t) => ref t").WithArguments("lambda expression", "D3").WithLocation(16, 15), + // (17,15): error CS8918: Cannot convert lambda expression to type 'D1' because the return type does not match the delegate return type + // d1 = (ref readonly T (ref T t) => ref t); + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "ref readonly T (ref T t) => ref t").WithArguments("lambda expression", "D1").WithLocation(17, 15), + // (18,15): error CS8918: Cannot convert lambda expression to type 'D2' because the return type does not match the delegate return type + // d2 = (ref readonly T (ref T t) => ref t); + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "ref readonly T (ref T t) => ref t").WithArguments("lambda expression", "D2").WithLocation(18, 15)); } [Fact] - public void LambdaReturnType_10() + public void LambdaReturnType_11() { var source = @"using System; @@ -4610,7 +4667,7 @@ static void Main() } [ConditionalFact(typeof(DesktopOnly))] - public void LambdaReturnType_11() + public void LambdaReturnType_12() { var source = @"using System; @@ -4647,7 +4704,7 @@ static void Main() } [Fact] - public void LambdaReturnType_12() + public void LambdaReturnType_13() { var source = @"static class S { } @@ -4667,7 +4724,7 @@ static void Main() } [Fact] - public void LambdaReturnType_13() + public void LambdaReturnType_14() { var source = @"using System; @@ -4689,7 +4746,7 @@ static void Main() } [Fact] - public void LambdaReturnType_14() + public void LambdaReturnType_15() { var source = @"using System; @@ -4717,7 +4774,7 @@ static void Main() } [Fact] - public void LambdaReturnType_15() + public void LambdaReturnType_16() { var source = @"using System; diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs index 5bd62ae6507be..1d692aa61be24 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs @@ -36,7 +36,7 @@ public static IEnumerable AsyncAndStaticModifiers() [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void IdentifierReturnType_01(string modifiers, params SyntaxKind[] modifierKinds) + public void IdentifierReturnType_01(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " T () => default"; UsingExpression(source, TestOptions.Regular9); @@ -334,7 +334,7 @@ void verify(string source, CSharpParseOptions parseOptions) [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void PrimitiveReturnType_01(string modifiers, params SyntaxKind[] modifierKinds) + public void PrimitiveReturnType_01(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " int (_) => 0"; UsingExpression(source); @@ -369,7 +369,7 @@ public void PrimitiveReturnType_01(string modifiers, params SyntaxKind[] modifie [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void PrimitiveReturnType_02(string modifiers, params SyntaxKind[] modifierKinds) + public void PrimitiveReturnType_02(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " void () => { }"; UsingExpression(source); @@ -401,7 +401,7 @@ public void PrimitiveReturnType_02(string modifiers, params SyntaxKind[] modifie [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void ArrayReturnType(string modifiers, params SyntaxKind[] modifierKinds) + public void ArrayReturnType(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " T[] () => null"; UsingExpression(source); @@ -444,7 +444,7 @@ public void ArrayReturnType(string modifiers, params SyntaxKind[] modifierKinds) [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void PointerReturnType_01(string modifiers, params SyntaxKind[] modifierKinds) + public void PointerReturnType_01(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " T* () => default"; UsingExpression(source); @@ -539,7 +539,7 @@ public void PointerReturnType_03() [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void FunctionPointerReturnType_01(string modifiers, params SyntaxKind[] modifierKinds) + public void FunctionPointerReturnType_01(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " delegate* () => default"; UsingExpression(source); @@ -2061,7 +2061,7 @@ public void QualifiedNameReturnType_01() [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void QualifiedNameReturnType_02(string modifiers, params SyntaxKind[] modifierKinds) + public void QualifiedNameReturnType_02(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " A::B () => null"; UsingExpression(source); @@ -2169,7 +2169,7 @@ public void GenericReturnType_01() [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void GenericReturnType_02(string modifiers, params SyntaxKind[] modifierKinds) + public void GenericReturnType_02(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " A.C () => null"; UsingExpression(source); @@ -2217,7 +2217,7 @@ public void GenericReturnType_02(string modifiers, params SyntaxKind[] modifierK [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void TupleReturnType_01(string modifiers, params SyntaxKind[] modifierKinds) + public void TupleReturnType_01(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " (x, y) (x, y) => (x, y)"; UsingExpression(source); @@ -2448,7 +2448,7 @@ public void Nested_03() [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void Ref_01(string modifiers, params SyntaxKind[] modifierKinds) + public void Ref_01(string modifiers, SyntaxKind[] modifierKinds) { string source = modifiers + " ref int (ref int x) => ref x"; UsingExpression(source); @@ -2494,8 +2494,48 @@ public void Ref_01(string modifiers, params SyntaxKind[] modifierKinds) EOF(); } + [MemberData(nameof(AsyncAndStaticModifiers))] + [Theory] + public void Ref_02(string modifiers, SyntaxKind[] modifierKinds) + { + string source = modifiers + " ref readonly A () => ref x"; + UsingExpression(source); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + foreach (var modifier in modifierKinds) + { + N(modifier); + } + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.ReadOnlyKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + EOF(); + } + [Fact] - public void Ref_02() + public void Ref_03() { string source = "ref D () => ref int () => ref x"; UsingExpression(source); @@ -3655,7 +3695,7 @@ public void Dynamic_02() [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void Attributes_01(string modifiers, params SyntaxKind[] modifierKinds) + public void Attributes_01(string modifiers, SyntaxKind[] modifierKinds) { string source = $"[A] {modifiers} void () => {{ }}"; UsingExpression(source); @@ -3749,7 +3789,7 @@ public void Attributes_02() [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void Attributes_03(string modifiers, params SyntaxKind[] modifierKinds) + public void Attributes_03(string modifiers, SyntaxKind[] modifierKinds) { string source = $"[A, B] {modifiers} ref T () => default"; UsingExpression(source); @@ -3804,7 +3844,7 @@ public void Attributes_03(string modifiers, params SyntaxKind[] modifierKinds) [MemberData(nameof(AsyncAndStaticModifiers))] [Theory] - public void AttributeArgument_01(string modifiers, params SyntaxKind[] modifierKinds) + public void AttributeArgument_01(string modifiers, SyntaxKind[] modifierKinds) { string source = $"[A({modifiers} void () => {{ }})] class C {{ }}"; UsingDeclaration(source); From 6c05d62561c83e164d9454d1351e01e19abef9c9 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 18 Jun 2021 22:32:46 -0700 Subject: [PATCH 6/8] Fix tests --- src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs | 2 ++ src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index d09ed9112d04e..8302fbd13831c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -8413,6 +8413,8 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate) ImmutableArray parameterTypes, ref CompoundUseSiteInfo useSiteInfo) { + Debug.Assert(returnTypeOpt.Type?.IsVoidType() != true); // expecting !returnTypeOpt.HasType rather than System.Void + if (returnRefKind == RefKind.None && (parameterRefKinds.IsDefault || parameterRefKinds.All(refKind => refKind == RefKind.None))) { diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index fe41f29b8c412..c9d67742ab73a 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -628,7 +628,12 @@ private bool DelegateNeedsReturn(MethodSymbol? invokeMethod) } } - return Binder.GetMethodGroupOrLambdaDelegateType(returnRefKind, returnType, parameterRefKinds, parameterTypes, ref useSiteInfo); + return Binder.GetMethodGroupOrLambdaDelegateType( + returnRefKind, + returnType.Type?.IsVoidType() == true ? default : returnType, + parameterRefKinds, + parameterTypes, + ref useSiteInfo); } private BoundLambda ReallyBind(NamedTypeSymbol delegateType) From ed9af1335ae82a013f688b225d70126d6ab2a132 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 18 Jun 2021 21:46:43 -0700 Subject: [PATCH 7/8] PR feedback --- .../Portable/Symbols/Source/LambdaSymbol.cs | 2 +- .../SourceMethodSymbolWithAttributes.cs | 4 +- .../Parsing/LambdaParameterParsingTests.cs | 86 +++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs index f90c26848618b..87528154a4d92 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs @@ -238,7 +238,7 @@ internal Location DiagnosticLocation } } - private bool HasExplicitReturnType => (Syntax as ParenthesizedLambdaExpressionSyntax)?.ReturnType is not null; + private bool HasExplicitReturnType => Syntax is ParenthesizedLambdaExpressionSyntax { ReturnType: not null }; public override ImmutableArray DeclaringSyntaxReferences { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 307d90fc6c3e6..1a15a16564f7e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -1063,7 +1063,9 @@ protected void AsyncMethodChecks(bool verifyReturnType, Location errorLocation, } // Avoid checking attributes on containing types to avoid a potential cycle when a lambda - // is used in an attribute argument - see https://github.com/dotnet/roslyn/issues/54074. + // is used in an attribute argument - see https://github.com/dotnet/roslyn/issues/54074. + // (ERR_SecurityCriticalOrSecuritySafeCriticalOnAsyncInClassOrStruct was never reported + // for lambda expressions and is not a .NET Core scenario so it's not necessary to handle.) if (this.MethodKind != MethodKind.LambdaMethod) { for (NamedTypeSymbol curr = this.ContainingType; (object)curr != null; curr = curr.ContainingType) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs index dd566a968bc30..de2bf0dd18253 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs @@ -577,5 +577,91 @@ public void HangingLambdaParsing_Bug14167() EOF(); } + [Fact] + public void Arglist_01() + { + string source = "(__arglist) => { }"; + UsingExpression(source, + // (1,1): error CS1073: Unexpected token '=>' + // (__arglist) => { } + Diagnostic(ErrorCode.ERR_UnexpectedToken, "(__arglist)").WithArguments("=>").WithLocation(1, 1)); + + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ArgListExpression); + { + N(SyntaxKind.ArgListKeyword); + } + N(SyntaxKind.CloseParenToken); + } + EOF(); + } + + [Fact] + public void Arglist_02() + { + string source = "(int x, __arglist) => { }"; + UsingExpression(source, + // (1,1): error CS1073: Unexpected token '=>' + // (int x, __arglist) => { } + Diagnostic(ErrorCode.ERR_UnexpectedToken, "(int x, __arglist)").WithArguments("=>").WithLocation(1, 1)); + + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.ArgListExpression); + { + N(SyntaxKind.ArgListKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + EOF(); + } + + [Fact] + public void Arglist_03() + { + string source = "static (__arglist) => { }"; + UsingExpression(source, + // (1,9): error CS1041: Identifier expected; '__arglist' is a keyword + // static (__arglist) => { } + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "__arglist").WithArguments("", "__arglist").WithLocation(1, 9)); + + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } } } From 20a262bd30a55b98a19708805e30be21bf16ccea Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Tue, 15 Jun 2021 23:03:35 -0700 Subject: [PATCH 8/8] PR feedback --- .../Portable/BoundTree/UnboundLambda.cs | 20 +++- .../Test/Semantic/Semantics/LambdaTests.cs | 97 +++++++++++++++++++ 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index c9d67742ab73a..c1782b14daabb 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -37,6 +37,7 @@ internal sealed partial class BoundLocalFunctionStatement : IBoundLambdaOrFuncti internal readonly struct InferredLambdaReturnType { internal readonly int NumExpressions; + internal readonly bool IsExplicitType; internal readonly bool HadExpressionlessReturn; internal readonly RefKind RefKind; internal readonly TypeWithAnnotations TypeWithAnnotations; @@ -45,6 +46,7 @@ internal readonly struct InferredLambdaReturnType internal InferredLambdaReturnType( int numExpressions, + bool isExplicitType, bool hadExpressionlessReturn, RefKind refKind, TypeWithAnnotations typeWithAnnotations, @@ -52,6 +54,7 @@ internal InferredLambdaReturnType( ImmutableArray dependencies) { NumExpressions = numExpressions; + IsExplicitType = isExplicitType; HadExpressionlessReturn = hadExpressionlessReturn; RefKind = refKind; TypeWithAnnotations = typeWithAnnotations; @@ -105,12 +108,13 @@ public TypeWithAnnotations GetInferredReturnType(ConversionsBase? conversions, N useSiteInfo.AddDependencies(InferredReturnType.Dependencies); } - if (nullableState == null) + if (nullableState == null || InferredReturnType.IsExplicitType) { return InferredReturnType.TypeWithAnnotations; } else { + Debug.Assert(!UnboundLambda.HasExplicitReturnType(out _, out _)); Debug.Assert(conversions != null); // Diagnostics from NullableWalker.Analyze can be dropped here since Analyze // will be called again from NullableWalker.ApplyConversion when the @@ -159,6 +163,7 @@ internal LambdaSymbol CreateLambdaSymbol( internal static InferredLambdaReturnType InferReturnType(ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)> returnTypes, BoundLambda node, Binder binder, TypeSymbol? delegateType, bool isAsync, ConversionsBase conversions) { + Debug.Assert(!node.UnboundLambda.HasExplicitReturnType(out _, out _)); return InferReturnTypeImpl(returnTypes, node, binder, delegateType, isAsync, conversions, node.UnboundLambda.WithDependencies); } @@ -200,9 +205,12 @@ private static InferredLambdaReturnType InferReturnTypeImpl(ArrayBuilder<(BoundR var bestType = CalculateReturnType(binder, conversions, delegateType, types, isAsync, node, ref useSiteInfo); int numExpressions = types.Count; types.Free(); - return new InferredLambdaReturnType(numExpressions, hasReturnWithoutArgument, refKind, bestType, - useSiteInfo.Diagnostics.AsImmutableOrEmpty(), - useSiteInfo.AccumulatesDependencies ? useSiteInfo.Dependencies.AsImmutableOrEmpty() : ImmutableArray.Empty); + return new InferredLambdaReturnType( + numExpressions, + isExplicitType: false, + hasReturnWithoutArgument, refKind, bestType, + useSiteInfo.Diagnostics.AsImmutableOrEmpty(), + useSiteInfo.AccumulatesDependencies ? useSiteInfo.Dependencies.AsImmutableOrEmpty() : ImmutableArray.Empty); } private static TypeWithAnnotations CalculateReturnType( @@ -800,6 +808,7 @@ private BoundLambda ReallyInferReturnType( // are only used when actually inferring a type, not when the type is explicit. inferredReturnType = new InferredLambdaReturnType( numExpressions: 0, + isExplicitType: true, hadExpressionlessReturn: false, refKind, returnType, @@ -1060,6 +1069,7 @@ private BoundLambda ReallyBindForErrorRecovery( var refKind = inferredReturnType.RefKind; if (!returnType.HasType) { + Debug.Assert(!inferredReturnType.IsExplicitType); var invokeMethod = DelegateInvokeMethod(delegateType); returnType = DelegateReturnTypeWithAnnotations(invokeMethod, out refKind); if (!returnType.HasType || returnType.Type.ContainsTypeParameter()) @@ -1080,7 +1090,7 @@ private BoundLambda ReallyBindForErrorRecovery( diagnostics.ToReadOnlyAndFree(), lambdaBodyBinder, delegateType, - new InferredLambdaReturnType(inferredReturnType.NumExpressions, inferredReturnType.HadExpressionlessReturn, refKind, returnType, ImmutableArray.Empty, ImmutableArray.Empty)) + new InferredLambdaReturnType(inferredReturnType.NumExpressions, isExplicitType: inferredReturnType.IsExplicitType, inferredReturnType.HadExpressionlessReturn, refKind, returnType, ImmutableArray.Empty, ImmutableArray.Empty)) { WasCompilerGenerated = _unboundLambda.WasCompilerGenerated }; } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 05541eca039a3..8f6d0589ff71f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -4801,6 +4801,103 @@ static void Main() Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(9, 22)); } + [Fact] + public void LambdaReturnType_17() + { + var source = +@"#nullable enable +using System; +class Program +{ + static void F(string? x, string y) + { + Func f1 = string () => { if (x is null) return x; return y; }; + Func f2 = string? () => { if (x is not null) return x; return y; }; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (7,28): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func' (possibly because of nullability attributes). + // Func f1 = string () => { if (x is null) return x; return y; }; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "string () => { if (x is null) return x; return y; }").WithArguments("lambda expression", "System.Func").WithLocation(7, 28), + // (7,65): warning CS8603: Possible null reference return. + // Func f1 = string () => { if (x is null) return x; return y; }; + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "x").WithLocation(7, 65), + // (8,27): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func' (possibly because of nullability attributes). + // Func f2 = string? () => { if (x is not null) return x; return y; }; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "string? () => { if (x is not null) return x; return y; }").WithArguments("lambda expression", "System.Func").WithLocation(8, 27)); + } + + [Fact] + public void LambdaReturnType_CustomModifiers_01() + { + var sourceA = +@".class public auto ansi sealed D extends [mscorlib]System.MulticastDelegate +{ + .method public hidebysig specialname rtspecialname instance void .ctor (object 'object', native int 'method') runtime managed { } + .method public hidebysig newslot virtual instance int32 modopt([mscorlib]System.Int16) Invoke () runtime managed { } + .method public hidebysig newslot virtual instance class [mscorlib]System.IAsyncResult BeginInvoke (class [mscorlib]System.AsyncCallback callback, object 'object') runtime managed { } + .method public hidebysig newslot virtual instance int32 modopt([mscorlib]System.Int16) EndInvoke (class [mscorlib]System.IAsyncResult result) runtime managed { } +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class Program +{ + static void F(D d) + { + System.Console.WriteLine(d()); + } + static void Main() + { + F(() => 1); + F(int () => 2); + } +}"; + CompileAndVerify(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularPreview, expectedOutput: +@"1 +2"); + } + + [Fact] + public void LambdaReturnType_CustomModifiers_02() + { + var sourceA = +@".class public auto ansi sealed D extends [mscorlib]System.MulticastDelegate +{ + .method public hidebysig specialname rtspecialname instance void .ctor (object 'object', native int 'method') runtime managed { } + .method public hidebysig newslot virtual instance int32 modreq([mscorlib]System.Int16) Invoke () runtime managed { } + .method public hidebysig newslot virtual instance class [mscorlib]System.IAsyncResult BeginInvoke (class [mscorlib]System.AsyncCallback callback, object 'object') runtime managed { } + .method public hidebysig newslot virtual instance int32 modreq([mscorlib]System.Int16) EndInvoke (class [mscorlib]System.IAsyncResult result) runtime managed { } +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class Program +{ + static void F(D d) + { + System.Console.WriteLine(d()); + } + static void Main() + { + F(() => 1); + F(int () => 2); + } +}"; + var comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (5,34): error CS0570: 'D.Invoke()' is not supported by the language + // System.Console.WriteLine(d()); + Diagnostic(ErrorCode.ERR_BindToBogus, "d()").WithArguments("D.Invoke()").WithLocation(5, 34), + // (9,11): error CS0570: 'D.Invoke()' is not supported by the language + // F(() => 1); + Diagnostic(ErrorCode.ERR_BindToBogus, "() => 1").WithArguments("D.Invoke()").WithLocation(9, 11), + // (10,11): error CS0570: 'D.Invoke()' is not supported by the language + // F(int () => 2); + Diagnostic(ErrorCode.ERR_BindToBogus, "int () => 2").WithArguments("D.Invoke()").WithLocation(10, 11)); + } + [Fact] public void LambdaReturnType_UseSiteErrors() {