From e4b7d60efbda60d0f763556725f9cb5c546ac248 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 25 Sep 2020 15:15:04 -0700 Subject: [PATCH] Rewrite function pointer arguments during local rewrite We weren't rewriting arguments during local rewrite, meaning that steps like out var discards and in refkind adjustment weren't occuring. We now do that. Fixes https://github.com/dotnet/roslyn/issues/47487. --- ...LocalRewriter_FunctionPointerInvocation.cs | 43 +++++++++ .../CodeGen/CodeGenFunctionPointersTests.cs | 90 +++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FunctionPointerInvocation.cs diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FunctionPointerInvocation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FunctionPointerInvocation.cs new file mode 100644 index 0000000000000..b6aa20ce1bc91 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FunctionPointerInvocation.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CSharp.Symbols; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal sealed partial class LocalRewriter + { + public override BoundNode? VisitFunctionPointerInvocation(BoundFunctionPointerInvocation node) + { + var rewrittenExpression = VisitExpression(node.InvokedExpression); + var rewrittenArgs = VisitList(node.Arguments); + + MethodSymbol functionPointer = node.FunctionPointer.Signature; + var argumentRefKindsOpt = node.ArgumentRefKindsOpt; + rewrittenArgs = MakeArguments( + node.Syntax, + rewrittenArgs, + functionPointer, + functionPointer, + expanded: false, + argsToParamsOpt: default, + ref argumentRefKindsOpt, + out ImmutableArray temps, + invokedAsExtensionMethod: false, + enableCallerInfo: ThreeState.False); + + node = node.Update(rewrittenExpression, rewrittenArgs, argumentRefKindsOpt, node.ResultKind, node.Type); + + if (temps.IsDefaultOrEmpty) + { + return node; + } + + return new BoundSequence(node.Syntax, temps, sideEffects: ImmutableArray.Empty, node, node.Type); + } + } +} diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index 10fb5fa016426..850ff0537bcd0 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -10778,6 +10778,96 @@ public static void M2() Assert.Equal(ConversionKind.AnonymousFunction, conversion.Kind); } + [Fact, WorkItem(47487, "https://github.com/dotnet/roslyn/issues/47487")] + public void InAndRefParameter() + { + var verifier = CompileAndVerifyFunctionPointers(@" +using System; +unsafe +{ + delegate* F = &Test; + char c = 'a'; + F(int.MaxValue, ref c); +} + +static void Test(in int b, ref char c) +{ + Console.WriteLine($""b = {b}, c = {c}""); +} +", expectedOutput: "b = 2147483647, c = a"); + + verifier.VerifyIL("", @" +{ + // Code size 27 (0x1b) + .maxstack 3 + .locals init (char V_0, //c + delegate* V_1, + int V_2) + IL_0000: ldftn ""void $.<
$>g__Test|0_0(in int, ref char)"" + IL_0006: ldc.i4.s 97 + IL_0008: stloc.0 + IL_0009: stloc.1 + IL_000a: ldc.i4 0x7fffffff + IL_000f: stloc.2 + IL_0010: ldloca.s V_2 + IL_0012: ldloca.s V_0 + IL_0014: ldloc.1 + IL_0015: calli ""delegate*"" + IL_001a: ret +} +"); + } + + [Fact, WorkItem(47487, "https://github.com/dotnet/roslyn/issues/47487")] + public void OutDiscard() + { + var verifier = CompileAndVerifyFunctionPointers(@" +using System; +unsafe +{ + delegate* F = &Test; + F(out var i1, out _); + F(out _, out var i2); + Console.Write(i1); + Console.Write(i2); +} + +static void Test(out int i1, out int i2) +{ + i1 = 1; + i2 = 2; +} +", expectedOutput: "12"); + + verifier.VerifyIL("", @" +{ + // Code size 42 (0x2a) + .maxstack 4 + .locals init (int V_0, //i1 + int V_1, //i2 + int V_2, + delegate* V_3) + IL_0000: ldftn ""void $.<
$>g__Test|0_0(out int, out int)"" + IL_0006: dup + IL_0007: stloc.3 + IL_0008: ldloca.s V_0 + IL_000a: ldloca.s V_2 + IL_000c: ldloc.3 + IL_000d: calli ""delegate*"" + IL_0012: stloc.3 + IL_0013: ldloca.s V_2 + IL_0015: ldloca.s V_1 + IL_0017: ldloc.3 + IL_0018: calli ""delegate*"" + IL_001d: ldloc.0 + IL_001e: call ""void System.Console.Write(int)"" + IL_0023: ldloc.1 + IL_0024: call ""void System.Console.Write(int)"" + IL_0029: ret +} +"); + } + private static readonly Guid s_guid = new Guid("97F4DBD4-F6D1-4FAD-91B3-1001F92068E5"); private static readonly BlobContentId s_contentId = new BlobContentId(s_guid, 0x04030201);