diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 4b229e1d5de20..eb6ea19dca883 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -259,6 +259,11 @@ private BoundExpression BindDynamicInvocation( BoundMethodGroup methodGroup = (BoundMethodGroup)expression; BoundExpression receiver = methodGroup.ReceiverOpt; + if ((methodGroup.LookupSymbolOpt as MethodSymbol)?.MethodKind == MethodKind.LocalFunction) + { + diagnostics.Add(ErrorCode.ERR_DynamicLocalFunctionParameter, node.Location, methodGroup.Syntax); + } + // receiver is null if we are calling a static method declared on an outer class via its simple name: if (receiver != null) { @@ -324,8 +329,8 @@ private BoundExpression BindDynamicInvocation( arguments.GetNames(), arguments.RefKinds.ToImmutableOrNull(), applicableMethods, - type: Compilation.DynamicType, - hasErrors: hasErrors); + Compilation.DynamicType, + hasErrors); } private ImmutableArray BuildArgumentsForDynamicInvocation(AnalyzedArguments arguments, DiagnosticBag diagnostics) @@ -511,16 +516,9 @@ private BoundExpression BindMethodGroupInvocation( { // If overload resolution found one or more applicable methods and at least one argument // was dynamic then treat this as a dynamic call. - if (resolution.AnalyzedArguments.HasDynamicArgument && - resolution.OverloadResolutionResult.HasAnyApplicableMember) + if (resolution.AnalyzedArguments.HasDynamicArgument && resolution.OverloadResolutionResult.HasAnyApplicableMember) { - if (resolution.IsLocalFunctionInvocation) - { - result = BindLocalFunctionInvocationWithDynamicArgument( - syntax, expression, methodName, methodGroup, - diagnostics, queryClause, resolution); - } - else if (resolution.IsExtensionMethodGroup) + if (resolution.IsExtensionMethodGroup) { // error CS1973: 'T' has no applicable method named 'M' but appears to have an // extension method by that name. Extension methods cannot be dynamically dispatched. Consider @@ -574,78 +572,6 @@ private BoundExpression BindMethodGroupInvocation( return result; } - private BoundExpression BindLocalFunctionInvocationWithDynamicArgument( - CSharpSyntaxNode syntax, - CSharpSyntaxNode expression, - string methodName, - BoundMethodGroup methodGroup, - DiagnosticBag diagnostics, - CSharpSyntaxNode queryClause, - MethodGroupResolution resolution) - { - // Invocations of local functions with dynamic arguments - // don't need to be dispatched as dynamic invocations - // since they cannot be overloaded. Instead, we'll just - // emit a standard call with dynamic implicit conversions - // for any dynamic arguments. The one exception is params - // arguments which cannot be targeted by dynamic arguments - // because there is an ambiguity between an array target - // and the params element target. See - // https://github.com/dotnet/roslyn/issues/10708 - - Debug.Assert(resolution.OverloadResolutionResult.Succeeded); - Debug.Assert(queryClause == null); - - var validResult = resolution.OverloadResolutionResult.ValidResult; - - var args = resolution.AnalyzedArguments.Arguments; - var localFunction = validResult.Member; - var parameters = localFunction.Parameters; - var methodResult = validResult.Result; - - // We're only in trouble if a dynamic argument is passed to the - // params parameter and is ambiguous at compile time between - // normal and expanded form i.e., there is exactly one dynamic - // argument to a params parameter - if (OverloadResolution.IsValidParams(localFunction) && - methodResult.Kind == MemberResolutionKind.ApplicableInNormalForm) - { - Debug.Assert(parameters.Last().IsParams); - var lastParamIndex = parameters.Length - 1; - - for (int i = 0; i < args.Count; ++i) - { - var arg = args[i]; - if (arg.HasDynamicType() && - methodResult.ParameterFromArgument(i) == lastParamIndex) - { - Error(diagnostics, - ErrorCode.ERR_DynamicLocalFunctionParamsParameter, - syntax, parameters.Last().Name, localFunction.Name); - return BindDynamicInvocation( - syntax, - methodGroup, - resolution.AnalyzedArguments, - resolution.OverloadResolutionResult.GetAllApplicableMembers(), - diagnostics, - queryClause); - } - } - } - - return BindInvocationExpressionContinued( - node: syntax, - expression: expression, - methodName: methodName, - result: resolution.OverloadResolutionResult, - analyzedArguments: resolution.AnalyzedArguments, - methodGroup: resolution.MethodGroup, - delegateTypeOpt: null, - diagnostics: diagnostics, - extensionMethodsOfSameViabilityAreAvailable: resolution.ExtensionMethodsOfSameViabilityAreAvailable, - queryClause: queryClause); - } - private ImmutableArray GetCandidatesPassingFinalValidation(CSharpSyntaxNode syntax, OverloadResolutionResult overloadResolutionResult, BoundMethodGroup methodGroup, DiagnosticBag diagnostics) { Debug.Assert(overloadResolutionResult.HasAnyApplicableMember); diff --git a/src/Compilers/CSharp/Portable/Binder/MethodGroupResolution.cs b/src/Compilers/CSharp/Portable/Binder/MethodGroupResolution.cs index ed32d9fe8a57e..5de1700e64eb1 100644 --- a/src/Compilers/CSharp/Portable/Binder/MethodGroupResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/MethodGroupResolution.cs @@ -91,10 +91,6 @@ public bool IsExtensionMethodGroup get { return (this.MethodGroup != null) && this.MethodGroup.IsExtensionMethodGroup; } } - public bool IsLocalFunctionInvocation => - MethodGroup.Methods.Count == 1 && // Local functions cannot be overloaded - MethodGroup.Methods[0].MethodKind == MethodKind.LocalFunction; - public void Free() { if (this.MethodGroup != null) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index eaae9e1d8588f..25e6bd5de17f2 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -3554,11 +3554,11 @@ internal static string ERR_DynamicAttributeMissing { } /// - /// Looks up a localized string similar to Cannot pass argument with dynamic type to params parameter '{0}' of local function '{1}'.. + /// Looks up a localized string similar to Cannot invoke the local function '{0}' with dynamic parameters.. /// - internal static string ERR_DynamicLocalFunctionParamsParameter { + internal static string ERR_DynamicLocalFunctionParameter { get { - return ResourceManager.GetString("ERR_DynamicLocalFunctionParamsParameter", resourceCulture); + return ResourceManager.GetString("ERR_DynamicLocalFunctionParameter", resourceCulture); } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index be14fe84221d1..832a7a363fdb1 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4821,8 +4821,8 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot infer the return type of '{0}' due to differing return types. - - Cannot pass argument with dynamic type to params parameter '{0}' of local function '{1}'. + + Cannot invoke the local function '{0}' with dynamic parameters. Cannot infer the type of '{0}' as it does not return a value. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 431894085a234..8a25a0a1a73d7 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1323,7 +1323,7 @@ internal enum ErrorCode ERR_TooManyUserStrings = 8103, ERR_PeWritingFailure = 8104, ERR_ReturnTypesDontMatch = 8105, - ERR_DynamicLocalFunctionParamsParameter = 8106, + ERR_DynamicLocalFunctionParameter = 8106, ERR_CantInferVoid = 8107, ERR_PatternNullableType = 8108, ERR_BadIsPatternExpression = 8109, diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs index 1be18aafb7e88..0e214d338f63e 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs @@ -27,14 +27,13 @@ private CompilationVerifier CompileAndVerifyIL( MetadataReference[] references = null, bool allowUnsafe = false, [CallerFilePath]string callerPath = null, - [CallerLineNumber]int callerLine = 0, - CSharpParseOptions parseOptions = null) + [CallerLineNumber]int callerLine = 0) { references = references ?? new[] { SystemCoreRef, CSharpRef }; // verify that we emit correct optimized and unoptimized IL: - var unoptimizedCompilation = CreateCompilationWithMscorlib45(source, references, parseOptions: parseOptions, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All).WithAllowUnsafe(allowUnsafe)); - var optimizedCompilation = CreateCompilationWithMscorlib45(source, references, parseOptions: parseOptions, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All).WithAllowUnsafe(allowUnsafe)); + var unoptimizedCompilation = CreateCompilationWithMscorlib45(source, references, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All).WithAllowUnsafe(allowUnsafe)); + var optimizedCompilation = CreateCompilationWithMscorlib45(source, references, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All).WithAllowUnsafe(allowUnsafe)); var unoptimizedVerifier = CompileAndVerify(unoptimizedCompilation); var optimizedVerifier = CompileAndVerify(optimizedCompilation); @@ -54,8 +53,6 @@ private CompilationVerifier CompileAndVerifyIL( return (expectedUnoptimizedIL != null) ^ (expectedOptimizedIL != null) ? (unoptimizedVerifier ?? optimizedVerifier) : null; } - private readonly CSharpParseOptions _localFunctionParseOptions = TestOptions.Regular.WithLocalFunctionsFeature(); - #endregion @@ -1491,120 +1488,6 @@ .locals init (C.<>c__DisplayClass11_0 V_0, //CS$<>8__locals0 #region Conversions - [Fact] - public void LocalFunctionArgumentConversion() - { - string src = @" -using System; -public class C -{ - static void Main() - { - int capture1 = 0; - void L1(int x) => Console.Write(x); - Action L2(int x) - { - Console.Write(capture1); - Console.Write(x); - void L3(int y) - { - Console.Write(y + x); - } - return L3; - } - dynamic d = 2; - L1(d); - dynamic l3 = L2(d); - l3(d); - } -}"; - CompileAndVerify(src, - expectedOutput: "2024", - parseOptions: _localFunctionParseOptions, - additionalRefs: new[] { SystemCoreRef, CSharpRef }).VerifyDiagnostics(); - CompileAndVerifyIL(src, "C.Main", - parseOptions: _localFunctionParseOptions, - expectedOptimizedIL: @" -{ - // Code size 250 (0xfa) - .maxstack 7 - .locals init (C.<>c__DisplayClass0_0 V_0, //CS$<>8__locals0 - object V_1, //d - object V_2) //l3 - IL_0000: ldloca.s V_0 - IL_0002: initobj ""C.<>c__DisplayClass0_0"" - IL_0008: ldloca.s V_0 - IL_000a: ldc.i4.0 - IL_000b: stfld ""int C.<>c__DisplayClass0_0.capture1"" - IL_0010: ldc.i4.2 - IL_0011: box ""int"" - IL_0016: stloc.1 - IL_0017: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0"" - IL_001c: brtrue.s IL_0042 - IL_001e: ldc.i4.0 - IL_001f: ldtoken ""int"" - IL_0024: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" - IL_0029: ldtoken ""C"" - IL_002e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" - IL_0033: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)"" - IL_0038: call ""System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_003d: stsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0"" - IL_0042: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0"" - IL_0047: ldfld ""System.Func System.Runtime.CompilerServices.CallSite>.Target"" - IL_004c: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__0"" - IL_0051: ldloc.1 - IL_0052: callvirt ""int System.Func.Invoke(System.Runtime.CompilerServices.CallSite, object)"" - IL_0057: call ""void C.
g__L10_0(int)"" - IL_005c: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__1"" - IL_0061: brtrue.s IL_0087 - IL_0063: ldc.i4.0 - IL_0064: ldtoken ""int"" - IL_0069: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" - IL_006e: ldtoken ""C"" - IL_0073: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" - IL_0078: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)"" - IL_007d: call ""System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0082: stsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__1"" - IL_0087: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__1"" - IL_008c: ldfld ""System.Func System.Runtime.CompilerServices.CallSite>.Target"" - IL_0091: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__1"" - IL_0096: ldloc.1 - IL_0097: callvirt ""int System.Func.Invoke(System.Runtime.CompilerServices.CallSite, object)"" - IL_009c: ldloca.s V_0 - IL_009e: call ""System.Action C.
g__L20_1(int, ref C.<>c__DisplayClass0_0)"" - IL_00a3: stloc.2 - IL_00a4: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__2"" - IL_00a9: brtrue.s IL_00e3 - IL_00ab: ldc.i4 0x100 - IL_00b0: ldtoken ""C"" - IL_00b5: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" - IL_00ba: ldc.i4.2 - IL_00bb: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo"" - IL_00c0: dup - IL_00c1: ldc.i4.0 - IL_00c2: ldc.i4.0 - IL_00c3: ldnull - IL_00c4: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" - IL_00c9: stelem.ref - IL_00ca: dup - IL_00cb: ldc.i4.1 - IL_00cc: ldc.i4.0 - IL_00cd: ldnull - IL_00ce: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" - IL_00d3: stelem.ref - IL_00d4: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Invoke(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_00d9: call ""System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_00de: stsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__2"" - IL_00e3: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__2"" - IL_00e8: ldfld ""System.Action System.Runtime.CompilerServices.CallSite>.Target"" - IL_00ed: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__0.<>p__2"" - IL_00f2: ldloc.2 - IL_00f3: ldloc.1 - IL_00f4: callvirt ""void System.Action.Invoke(System.Runtime.CompilerServices.CallSite, object, object)"" - IL_00f9: ret -}"); - } - [Fact] public void Conversion_Assignment() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index e7b21c7d31c97..1a21e8a84e3fd 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -76,34 +76,6 @@ private void VerifyDiagnostics(string source, CSharpCompilationOptions options, comp.VerifyDiagnostics(expected); } - [Fact] - [CompilerTrait(CompilerFeature.Dynamic)] - public void DynamicParameterLocalFunction() - { - var src = @" -using System; - -class C -{ - static void Main(string[] args) => M(0); - - static void M(int x) - { - dynamic y = x + 1; - Action a; - Action local(dynamic z) - { - Console.Write(z); - Console.Write(y); - return () => Console.Write(y + z + 1); - } - a = local(x); - a(); - } -}"; - VerifyOutput(src, "012"); - } - [Fact] public void EndToEnd() { @@ -219,7 +191,6 @@ void LocalFuncName() } [Fact] - [CompilerTrait(CompilerFeature.Params)] public void BadParams() { var source = @" @@ -2294,7 +2265,6 @@ void Local() } [Fact] - [CompilerTrait(CompilerFeature.Dynamic)] public void DynamicArgument() { var source = @" @@ -2303,92 +2273,20 @@ class Program { static void Main() { - int capture1 = 0; - void L1(int x) => Console.Write(x); - void L2(int x) + void Local(int x) { - Console.Write(capture1); Console.Write(x); } - dynamic L4(int x) - { - Console.Write(capture1); - return x; - } - Action L5(int x) - { - Console.Write(x); - return L1; - } - dynamic val = 2; - Console.WriteLine(); - L1(val); - L2(val); - Console.WriteLine(); - L2(L4(val)); - L5(val)(val); - } -} -"; - VerifyOutput(source, output: @"202 -00222"); - } - - [Fact] - [CompilerTrait(CompilerFeature.Dynamic, CompilerFeature.Params)] - public void DynamicArgsAndParams() - { - var src = @" -int capture1 = 0; -void L1(int x, params int[] ys) -{ - Console.Write(capture1); - Console.Write(x); - foreach (var y in ys) - { - Console.Write(y); + Local(val); } } - -dynamic val = 2; -int val2 = 3; -L1(val, val2); -L1(val); -L1(val, val, val); "; - VerifyOutputInMain(src, "023020222", "System"); - } - - [WorkItem(10708, "https://github.com/dotnet/roslyn/issues/10708")] - [CompilerTrait(CompilerFeature.Dynamic, CompilerFeature.Params)] - [Fact] - public void DynamicArgumentToParams() - { - var src = @" -using System; -class C -{ - static void Main() - { - void L1(int x = 0, params int[] ys) => Console.Write(x); - - dynamic val = 2; - L1(val, val); - L1(ys: val, x: val); - L1(ys: val); - } -}"; - VerifyDiagnostics(src, - // (10,9): error CS8106: Cannot pass argument with dynamic type to params parameter 'ys' of local function 'L1'. - // L1(val, val); - Diagnostic(ErrorCode.ERR_DynamicLocalFunctionParamsParameter, "L1(val, val)").WithArguments("ys", "L1").WithLocation(10, 9), - // (11,9): error CS8106: Cannot pass argument with dynamic type to params parameter 'ys' of local function 'L1'. - // L1(ys: val, x: val); - Diagnostic(ErrorCode.ERR_DynamicLocalFunctionParamsParameter, "L1(ys: val, x: val)").WithArguments("ys", "L1").WithLocation(11, 9), - // (12,9): error CS8106: Cannot pass argument with dynamic type to params parameter 'ys' of local function 'L1'. - // L1(ys: val); - Diagnostic(ErrorCode.ERR_DynamicLocalFunctionParamsParameter, "L1(ys: val)").WithArguments("ys", "L1").WithLocation(12, 9)); + VerifyDiagnostics(source, + // (12,9): error CS8098: Cannot invoke the local function 'Local' with dynamic parameters. + // Local(val); + Diagnostic(ErrorCode.ERR_DynamicLocalFunctionParameter, "Local(val)").WithArguments("Local").WithLocation(12, 9) + ); } [Fact] @@ -2440,24 +2338,12 @@ dynamic Local(dynamic x) { return x; } - dynamic L2(int x) - { - void L2_1(int y) - { - Console.Write(x); - Console.Write(y); - } - dynamic z = x + 1; - void L2_2() => L2_1(z); - return (Action)L2_2; - } dynamic local = (Func)Local; Console.Write(local(2)); - L2(3)(); } } "; - VerifyOutput(source, "234"); + VerifyOutput(source, "2"); } [Fact] @@ -2485,7 +2371,6 @@ Expression> Local(Expression> f) VerifyOutputInMain(source, "x => x", "System", "System.Linq.Expressions"); } - [WorkItem(3923, "https://github.com/dotnet/roslyn/issues/3923")] [Fact] public void ExpressionTreeLocfuncUsage() { @@ -2507,7 +2392,9 @@ Expression> Local(Expression> f) Console.Write(Local(() => Id(2))); Console.Write(Local>(() => Id)); Console.Write(Local(() => new Func(Id))); - Console.Write(Local(() => nameof(Id))); + // Disabled because of https://github.com/dotnet/roslyn/issues/3923 + // Should produce a diagnostic once uncommented. + //Console.Write(Local(() => nameof(Id))); } } ";