diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 13a8b4caa586..a573c7e32787 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -292,7 +292,7 @@ private void PerformMemberOverloadResolution( if (isFunctionPointerResolution) { RemoveCallingConventionMismatches(results, callingConvention); - RemoveStaticInstanceMismatches(results, requireStatic: true); + RemoveMethodsNotDeclaredStatic(results); } // NB: As in dev12, we do this AFTER removing less derived members. @@ -387,6 +387,22 @@ private static void RemoveStaticInstanceMismatches(ArrayBuilder(ArrayBuilder> results) where TMember : Symbol + { + // RemoveStaticInstanceMistmatches allows methods that do not need a reciever but are not declared static, + // such as a local function that is not declared static. This eliminates methods that are not actually + // declared as static + for (int f = 0; f < results.Count; f++) + { + var result = results[f]; + TMember member = result.Member; + if (result.Result.IsValid && !member.IsStatic) + { + results[f] = new MemberResolutionResult(member, result.LeastOverriddenMember, MemberAnalysisResult.StaticInstanceMismatch()); + } + } + } + private void RemoveConstraintViolations(ArrayBuilder> results) where TMember : Symbol { // When the feature 'ImprovedOverloadCandidates' is enabled, we do not include methods for which the type arguments diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs index 9faf91fc9887..b48deb8669ca 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs @@ -1303,6 +1303,30 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE return base.VisitDelegateCreationExpression(node); } + public override BoundNode VisitFunctionPointerLoad(BoundFunctionPointerLoad node) + { + if (node.TargetMethod.MethodKind == MethodKind.LocalFunction) + { + Debug.Assert(node.TargetMethod is { RequiresInstanceReceiver: false, IsStatic: true }); + ImmutableArray arguments = default; + ImmutableArray argRefKinds = default; + + RemapLocalFunction( + node.Syntax, + node.TargetMethod, + out var receiver, + out var remappedMethod, + ref arguments, + ref argRefKinds); + + Debug.Assert(arguments.IsDefault && argRefKinds.IsDefault && receiver.Kind == BoundKind.TypeExpression); + + return node.Update(remappedMethod, node.Type); + } + + return base.VisitFunctionPointerLoad(node); + } + public override BoundNode VisitConversion(BoundConversion conversion) { // a conversion with a method should have been rewritten, e.g. to an invocation diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index 88a2dc60e21e..8be389ca89d7 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -7101,6 +7101,62 @@ .locals init (delegate*, System.Span> V_0) "); } + [Fact, WorkItem(45447, "https://github.com/dotnet/roslyn/issues/45447")] + public void LocalFunction_ValidStatic() + { + var verifier = CompileAndVerifyFunctionPointers(@" +unsafe class FunctionPointer +{ + public static void Main() + { + delegate* a = &local; + a(); + + static void local() => System.Console.Write(""local""); + } +} +", expectedOutput: "local"); + + + verifier.VerifyIL("FunctionPointer.Main", @" +{ + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldftn ""void FunctionPointer.
g__local|0_0()"" + IL_0006: calli ""delegate*"" + IL_000b: ret +} +"); + } + + [Fact, WorkItem(45447, "https://github.com/dotnet/roslyn/issues/45447")] + public void LocalFunction_InvalidNonStatic() + { + var comp = CreateCompilationWithFunctionPointers(@" +unsafe class FunctionPointer +{ + public static void M() + { + int local = 1; + + delegate* first = &noCaptures; + delegate* second = &capturesLocal; + + void noCaptures() { } + void capturesLocal() { local++; } + } +}"); + + comp.VerifyDiagnostics( + // (8,34): error CS8759: Cannot create a function pointer for 'noCaptures()' because it is not a static method + // delegate* first = &noCaptures; + Diagnostic(ErrorCode.ERR_FuncPtrMethMustBeStatic, "noCaptures").WithArguments("noCaptures()").WithLocation(8, 34), + // (9,35): error CS8759: Cannot create a function pointer for 'capturesLocal()' because it is not a static method + // delegate* second = &capturesLocal; + Diagnostic(ErrorCode.ERR_FuncPtrMethMustBeStatic, "capturesLocal").WithArguments("capturesLocal()").WithLocation(9, 35) + ); + } + private static readonly Guid s_guid = new Guid("97F4DBD4-F6D1-4FAD-91B3-1001F92068E5"); private static readonly BlobContentId s_contentId = new BlobContentId(s_guid, 0x04030201);