Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle ref returning receivers in dynamic calls #70676

Merged
merged 6 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -539,35 +539,20 @@ private MethodSymbol GetArgumentInfoFactory()
// however
// dynamic d = ...;
// GetS().M(d); // becomes Site(GetS(), d) without ref on the target obj arg
internal static RefKind GetReceiverRefKind(BoundExpression loweredReceiver)
internal RefKind GetReceiverRefKind(BoundExpression loweredReceiver)
{
Debug.Assert(loweredReceiver.Type is { });
if (!loweredReceiver.Type.IsValueType)
{
return RefKind.None;
}

switch (loweredReceiver.Kind)
{
case BoundKind.Parameter:
Debug.Assert(!LocalRewriter.IsCapturedPrimaryConstructorParameter(loweredReceiver));
goto case BoundKind.Local;

case BoundKind.Local:
case BoundKind.ArrayAccess:
case BoundKind.ThisReference:
case BoundKind.PointerIndirectionOperator:
case BoundKind.PointerElementAccess:
case BoundKind.RefValueOperator:
return RefKind.Ref;

case BoundKind.BaseReference:
// base dynamic dispatch is not supported, an error has already been reported
case BoundKind.TypeExpression:
throw ExceptionUtilities.UnexpectedValue(loweredReceiver.Kind);
}

return RefKind.None;
var hasHome = Binder.HasHome(loweredReceiver,
Binder.AddressKind.Writeable,
_factory.CurrentFunction,
peVerifyCompatEnabled: false,
stackLocalsOpt: null);
return hasHome ? RefKind.Ref : RefKind.None;
}

internal BoundExpression MakeCallSiteArgumentInfos(
Expand Down
48 changes: 24 additions & 24 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9235,10 +9235,10 @@ public void goo(int a) {}
";
CompileAndVerifyIL(source, "C.M", @"
{
// Code size 97 (0x61)
// Code size 98 (0x62)
.maxstack 9
IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, object>> C.<>o__1.<>p__0""
IL_0005: brtrue.s IL_0045
IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object>> C.<>o__1.<>p__0""
IL_0005: brtrue.s IL_0046
IL_0007: ldc.i4 0x100
IL_000c: ldstr ""goo""
IL_0011: ldnull
Expand All @@ -9248,27 +9248,27 @@ .maxstack 9
IL_001d: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo""
IL_0022: dup
IL_0023: ldc.i4.0
IL_0024: ldc.i4.1
IL_0025: ldnull
IL_0026: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_002b: stelem.ref
IL_002c: dup
IL_002d: ldc.i4.1
IL_002e: ldc.i4.0
IL_002f: ldnull
IL_0030: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_0035: stelem.ref
IL_0036: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_003b: call ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, object>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, object>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0040: stsfld ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, object>> C.<>o__1.<>p__0""
IL_0045: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, object>> C.<>o__1.<>p__0""
IL_004a: ldfld ""System.Action<System.Runtime.CompilerServices.CallSite, S, object> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, object>>.Target""
IL_004f: ldsfld ""System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, object>> C.<>o__1.<>p__0""
IL_0054: ldarg.0
IL_0055: ldfld ""S C.s""
IL_005a: ldarg.1
IL_005b: callvirt ""void System.Action<System.Runtime.CompilerServices.CallSite, S, object>.Invoke(System.Runtime.CompilerServices.CallSite, S, object)""
IL_0060: ret
IL_0024: ldc.i4.s 9
IL_0026: ldnull
IL_0027: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_002c: stelem.ref
IL_002d: dup
IL_002e: ldc.i4.1
IL_002f: ldc.i4.0
IL_0030: ldnull
IL_0031: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)""
IL_0036: stelem.ref
IL_0037: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)""
IL_003c: call ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object>> System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object>>.Create(System.Runtime.CompilerServices.CallSiteBinder)""
IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object>> C.<>o__1.<>p__0""
IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object>> C.<>o__1.<>p__0""
IL_004b: ldfld ""<>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object> System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object>>.Target""
IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object>> C.<>o__1.<>p__0""
IL_0055: ldarg.0
IL_0056: ldflda ""S C.s""
IL_005b: ldarg.1
IL_005c: callvirt ""void <>A{00000008}<System.Runtime.CompilerServices.CallSite, S, object>.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)""
IL_0061: ret
}
");
}
Expand Down
204 changes: 204 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,210 @@ public void Goo()
VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}

[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/68063")]
public void RefReturn_Method([CombinatorialValues("int", "dynamic")] string type)
{
var source = $$"""
{{type}} d = 1;
S s = default;
GetS(ref s).M(d);
System.Console.Write(s.X);

static ref S GetS(ref S s) => ref s;

struct S
{
public int X { get; private set; }

public void M(int x) => X = x;
}
""";
CompileAndVerify(source, new[] { CSharpRef },
expectedOutput: "1", verify: Verification.Fails).VerifyDiagnostics();
}

[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/68063")]
public void RefReturn_Property([CombinatorialValues("int", "dynamic")] string type)
{
var source = $$"""
{{type}} d = 1;
S s = default;
s.Self.M(d);
System.Console.Write(s.X);

struct S
{
[System.Diagnostics.CodeAnalysis.UnscopedRef]
public ref S Self => ref this;
public int X { get; private set; }

public void M(int x) => X = x;
}
""";
CompileAndVerify(new[] { source, UnscopedRefAttributeDefinition }, new[] { CSharpRef },
expectedOutput: "1", verify: Verification.Fails).VerifyDiagnostics();
}

[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/68063")]
public void RefReturn_Indexer([CombinatorialValues("int", "dynamic")] string type)
{
var source = $$"""
{{type}} d = 1;
S s = default;
s[0].M(d);
System.Console.Write(s.X);

struct S
{
[System.Diagnostics.CodeAnalysis.UnscopedRef]
public ref S this[int _] => ref this;
public int X { get; private set; }

public void M(int x) => X = x;
}
""";
CompileAndVerify(new[] { source, UnscopedRefAttributeDefinition }, new[] { CSharpRef },
expectedOutput: "1", verify: Verification.Fails).VerifyDiagnostics();
}

[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/68063")]
public void RefReturn_Local([CombinatorialValues("int", "dynamic")] string type)
{
var source = $$"""
{{type}} d = 1;
S s = default;
ref S r = ref s;
r.M(d);
System.Console.Write(s.X);

struct S
{
public int X { get; private set; }
public void M(int x) => X = x;
}
""";
CompileAndVerify(new[] { source, UnscopedRefAttributeDefinition }, new[] { CSharpRef },
expectedOutput: "1").VerifyDiagnostics();
}

[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/68063")]
public void RefReturn_Field([CombinatorialValues("int", "dynamic")] string type)
{
var source = $$"""
{{type}} d = 1;
S s = default;
P p = new P(ref s);
p.S.M(d);
System.Console.Write(s.X);

ref struct P(ref S s)
{
public ref S S = ref s;
}

struct S
{
public int X { get; private set; }
public void M(int x) => X = x;
}
""";
CompileAndVerify(new[] { source, UnscopedRefAttributeDefinition }, targetFramework: TargetFramework.Net70,
expectedOutput: RefFieldTests.IncludeExpectedOutput("1"), verify: Verification.FailsPEVerify).VerifyDiagnostics();
}

[Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/68063")]
public void StructReceiver_Field([CombinatorialValues("int", "dynamic")] string type, bool ro)
{
var source = $$"""
new C(default).Run();

class C(S s)
{
private {{(ro ? "readonly" : "")}} S s = s;

public void Run()
{
{{type}} d = 1;
s.M(d);
System.Console.Write(s.X);
}
}

struct S
{
public int X { get; private set; }
public void M(int x) => X = x;
}
""";
CompileAndVerify(new[] { source, UnscopedRefAttributeDefinition }, new[] { CSharpRef },
expectedOutput: ro ? "0" : "1", verify: ro ? Verification.FailsPEVerify : default).VerifyDiagnostics();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68063")]
public void StructReceiver_Rvalue()
{
var source = """
class Program
{
static void Main()
{
dynamic d = 1;
GetS().M(d);
}

static S GetS() => default;
}

struct S
{
public int X { get; private set; }
public void M(int x) => X = x;
}
""";
var verifier = CompileAndVerify(source, new[] { CSharpRef }).VerifyDiagnostics();
verifier.VerifyIL("Program.Main", """
{
// Code size 103 (0x67)
.maxstack 9
.locals init (object V_0) //d
IL_0000: ldc.i4.1
IL_0001: box "int"
IL_0006: stloc.0
IL_0007: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic>> Program.<>o__0.<>p__0"
IL_000c: brtrue.s IL_004c
IL_000e: ldc.i4 0x100
IL_0013: ldstr "M"
IL_0018: ldnull
IL_0019: ldtoken "Program"
IL_001e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"
IL_0023: ldc.i4.2
IL_0024: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo"
IL_0029: dup
IL_002a: ldc.i4.0
IL_002b: ldc.i4.1
IL_002c: ldnull
IL_002d: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"
IL_0032: stelem.ref
IL_0033: dup
IL_0034: ldc.i4.1
IL_0035: ldc.i4.0
IL_0036: ldnull
IL_0037: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"
IL_003c: stelem.ref
IL_003d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)"
IL_0042: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"
IL_0047: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic>> Program.<>o__0.<>p__0"
IL_004c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic>> Program.<>o__0.<>p__0"
IL_0051: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic>>.Target"
IL_0056: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic>> Program.<>o__0.<>p__0"
IL_005b: call "S Program.GetS()"
IL_0060: ldloc.0
IL_0061: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, S, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, S, dynamic)"
IL_0066: ret
}
""");
}

#endregion

#region Type Inference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class RefFieldTests : CSharpTestBase
{
private static string IncludeExpectedOutput(string expectedOutput) => ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null;
internal static string IncludeExpectedOutput(string expectedOutput) => ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null;

[CombinatorialData]
[Theory]
Expand Down