diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs index 99ac4ec17e483..8f5d5b0b4e9db 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs @@ -539,7 +539,7 @@ 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) @@ -547,27 +547,12 @@ internal static RefKind GetReceiverRefKind(BoundExpression loweredReceiver) 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( diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs index 8c49b627be8ae..407a1feb30ece 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs @@ -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> C.<>o__1.<>p__0"" - IL_0005: brtrue.s IL_0045 + IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}> C.<>o__1.<>p__0"" + IL_0005: brtrue.s IL_0046 IL_0007: ldc.i4 0x100 IL_000c: ldstr ""goo"" IL_0011: ldnull @@ -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.Collections.Generic.IEnumerable)"" - IL_003b: call ""System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0040: stsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__1.<>p__0"" - IL_0045: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__1.<>p__0"" - IL_004a: ldfld ""System.Action System.Runtime.CompilerServices.CallSite>.Target"" - IL_004f: ldsfld ""System.Runtime.CompilerServices.CallSite> 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.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.Collections.Generic.IEnumerable)"" + IL_003c: call ""System.Runtime.CompilerServices.CallSite<<>A{00000008}> System.Runtime.CompilerServices.CallSite<<>A{00000008}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}> C.<>o__1.<>p__0"" + IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}> C.<>o__1.<>p__0"" + IL_004b: ldfld ""<>A{00000008} System.Runtime.CompilerServices.CallSite<<>A{00000008}>.Target"" + IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}> 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}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" + IL_0061: ret } "); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs index 4f60110bfc116..e79dc23b94fd1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs @@ -1510,6 +1510,210 @@ public void Goo() VerifyOperationTreeAndDiagnosticsForTest(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> 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.Collections.Generic.IEnumerable)" + IL_0042: call "System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)" + IL_0047: stsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_004c: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_0051: ldfld "System.Action System.Runtime.CompilerServices.CallSite>.Target" + IL_0056: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_005b: call "S Program.GetS()" + IL_0060: ldloc.0 + IL_0061: callvirt "void System.Action.Invoke(System.Runtime.CompilerServices.CallSite, S, dynamic)" + IL_0066: ret + } + """); + } + #endregion #region Type Inference diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index b47147cd7c62f..a787c577b41e6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -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]