diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs index 84cfae1c3b2c0..66fae04bf79cc 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs @@ -1130,7 +1130,7 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node return conditionBuilder.Update(_F.Default(node.Type)); } - else + else if (!node.IsRef) { var tmp = _F.SynthesizedLocal(node.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax); @@ -1142,6 +1142,33 @@ public override BoundNode VisitConditionalOperator(BoundConditionalOperator node return conditionBuilder.Update(_F.Local(tmp)); } + else + { + Debug.Assert(condition.Type.SpecialType == SpecialType.System_Boolean); + + // 1. Capture the boolean value (the condition) in a temp + var tmp = _F.SynthesizedLocal(condition.Type, kind: SynthesizedLocalKind.Spill, syntax: _F.Syntax); + + conditionBuilder.AddLocal(tmp); + conditionBuilder.AddStatement(_F.Assignment(_F.Local(tmp), condition)); + condition = _F.Local(tmp); + + // 2. Conditionally execute side-effects from the builders based on the temp + conditionBuilder.AddLocals(consequenceBuilder.GetLocals()); + conditionBuilder.AddLocals(alternativeBuilder.GetLocals()); + + conditionBuilder.AddStatement( + _F.If(condition, + _F.StatementList(consequenceBuilder.GetStatements()), + _F.StatementList(alternativeBuilder.GetStatements()))); + + consequenceBuilder.Free(); + alternativeBuilder.Free(); + + // 3. Use updated conditional operator as the result. Note, we are using the captured temp as its condition, + // plus rewritten consequence and alternative. + return conditionBuilder.Update(node.Update(node.IsRef, condition, consequence, alternative, node.ConstantValueOpt, node.NaturalTypeOpt, node.WasTargetTyped, node.Type)); + } } public override BoundNode VisitConversion(BoundConversion node) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs index 92b6ac0126c69..60ca1c1996c02 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs @@ -1356,5 +1356,1121 @@ void verify(CSharpCompilationOptions options, string expectedIL) verifier.VerifyIL("", expectedIL); } } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void AwaitInRefConditional_01() + { + var source = @" +using System.Threading.Tasks; + +class C +{ + int F; + + static async Task Main() + { + var c1 = new C(); + var c2 = new C(); + await Test(false, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + + System.Console.Write('-'); + + c1 = new C(); + c2 = new C(); + await Test(true, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + } + + static async Task Test(bool b, C c1, C c2) + { + ((await GetBool(b)) ? ref GetC(c1).F: ref GetC(c2).F) = 123; + } + + static async Task GetBool(bool b) + { + await Task.Yield(); + return b; + } + + static C GetC(C c) => c; +} +"; + + var comp = CompileAndVerify(source, expectedOutput: "0123-1230", options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: "0123-1230", options: TestOptions.DebugExe); + + comp.VerifyIL("C.d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", +@" +{ + // Code size 187 (0xbb) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.TaskAwaiter V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__2.<>1__state"" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0044 + IL_000a: ldarg.0 + IL_000b: ldfld ""bool C.d__2.b"" + IL_0010: call ""System.Threading.Tasks.Task C.GetBool(bool)"" + IL_0015: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_001a: stloc.1 + IL_001b: ldloca.s V_1 + IL_001d: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_0022: brtrue.s IL_0060 + IL_0024: ldarg.0 + IL_0025: ldc.i4.0 + IL_0026: dup + IL_0027: stloc.0 + IL_0028: stfld ""int C.d__2.<>1__state"" + IL_002d: ldarg.0 + IL_002e: ldloc.1 + IL_002f: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0034: ldarg.0 + IL_0035: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_003a: ldloca.s V_1 + IL_003c: ldarg.0 + IL_003d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_0042: leave.s IL_00ba + IL_0044: ldarg.0 + IL_0045: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_004a: stloc.1 + IL_004b: ldarg.0 + IL_004c: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0051: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_0057: ldarg.0 + IL_0058: ldc.i4.m1 + IL_0059: dup + IL_005a: stloc.0 + IL_005b: stfld ""int C.d__2.<>1__state"" + IL_0060: ldloca.s V_1 + IL_0062: call ""bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_0067: brtrue.s IL_007b + IL_0069: ldarg.0 + IL_006a: ldfld ""C C.d__2.c2"" + IL_006f: call ""C C.GetC(C)"" + IL_0074: ldflda ""int C.F"" + IL_0079: br.s IL_008b + IL_007b: ldarg.0 + IL_007c: ldfld ""C C.d__2.c1"" + IL_0081: call ""C C.GetC(C)"" + IL_0086: ldflda ""int C.F"" + IL_008b: ldc.i4.s 123 + IL_008d: stind.i4 + IL_008e: leave.s IL_00a7 + } + catch System.Exception + { + IL_0090: stloc.2 + IL_0091: ldarg.0 + IL_0092: ldc.i4.s -2 + IL_0094: stfld ""int C.d__2.<>1__state"" + IL_0099: ldarg.0 + IL_009a: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_009f: ldloc.2 + IL_00a0: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_00a5: leave.s IL_00ba + } + IL_00a7: ldarg.0 + IL_00a8: ldc.i4.s -2 + IL_00aa: stfld ""int C.d__2.<>1__state"" + IL_00af: ldarg.0 + IL_00b0: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_00b5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00ba: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void AwaitInRefConditional_02() + { + var source = @" +using System.Threading.Tasks; + +class C +{ + int F; + + static async Task Main() + { + var c1 = new C(); + var c2 = new C(); + await Test(false, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + + System.Console.Write('-'); + + c1 = new C(); + c2 = new C(); + await Test(true, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + } + + static async Task Test(bool b, C c1, C c2) + { + (GetBool(b) ? ref (await GetC1(c1)).F: ref GetC2(c2).F) = 123; + } + + static bool GetBool(bool b) => b; + + static async Task GetC1(C c) + { + await Task.Yield(); + return c; + } + + static C GetC2(C c) => c; +} +"; + + var comp = CompileAndVerify(source, expectedOutput: "0123-1230", options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: "0123-1230", options: TestOptions.DebugExe); + + comp.VerifyIL("C.d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", +@" +{ + // Code size 209 (0xd1) + .maxstack 3 + .locals init (int V_0, + C V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__2.<>1__state"" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_005d + IL_000a: ldarg.0 + IL_000b: ldarg.0 + IL_000c: ldfld ""bool C.d__2.b"" + IL_0011: call ""bool C.GetBool(bool)"" + IL_0016: stfld ""bool C.d__2.<>7__wrap1"" + IL_001b: ldarg.0 + IL_001c: ldfld ""bool C.d__2.<>7__wrap1"" + IL_0021: brfalse.s IL_0081 + IL_0023: ldarg.0 + IL_0024: ldfld ""C C.d__2.c1"" + IL_0029: call ""System.Threading.Tasks.Task C.GetC1(C)"" + IL_002e: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0033: stloc.2 + IL_0034: ldloca.s V_2 + IL_0036: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_003b: brtrue.s IL_0079 + IL_003d: ldarg.0 + IL_003e: ldc.i4.0 + IL_003f: dup + IL_0040: stloc.0 + IL_0041: stfld ""int C.d__2.<>1__state"" + IL_0046: ldarg.0 + IL_0047: ldloc.2 + IL_0048: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_004d: ldarg.0 + IL_004e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_0053: ldloca.s V_2 + IL_0055: ldarg.0 + IL_0056: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_005b: leave.s IL_00d0 + IL_005d: ldarg.0 + IL_005e: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0063: stloc.2 + IL_0064: ldarg.0 + IL_0065: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_006a: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_0070: ldarg.0 + IL_0071: ldc.i4.m1 + IL_0072: dup + IL_0073: stloc.0 + IL_0074: stfld ""int C.d__2.<>1__state"" + IL_0079: ldloca.s V_2 + IL_007b: call ""C System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_0080: stloc.1 + IL_0081: ldarg.0 + IL_0082: ldfld ""bool C.d__2.<>7__wrap1"" + IL_0087: brtrue.s IL_009b + IL_0089: ldarg.0 + IL_008a: ldfld ""C C.d__2.c2"" + IL_008f: call ""C C.GetC2(C)"" + IL_0094: ldflda ""int C.F"" + IL_0099: br.s IL_00a1 + IL_009b: ldloc.1 + IL_009c: ldflda ""int C.F"" + IL_00a1: ldc.i4.s 123 + IL_00a3: stind.i4 + IL_00a4: leave.s IL_00bd + } + catch System.Exception + { + IL_00a6: stloc.3 + IL_00a7: ldarg.0 + IL_00a8: ldc.i4.s -2 + IL_00aa: stfld ""int C.d__2.<>1__state"" + IL_00af: ldarg.0 + IL_00b0: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_00b5: ldloc.3 + IL_00b6: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_00bb: leave.s IL_00d0 + } + IL_00bd: ldarg.0 + IL_00be: ldc.i4.s -2 + IL_00c0: stfld ""int C.d__2.<>1__state"" + IL_00c5: ldarg.0 + IL_00c6: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_00cb: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00d0: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void AwaitInRefConditional_03() + { + var source = @" +using System.Threading.Tasks; + +class C +{ + int F; + + static async Task Main() + { + var c1 = new C(); + var c2 = new C(); + await Test(false, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + + System.Console.Write('-'); + + c1 = new C(); + c2 = new C(); + await Test(true, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + } + + static async Task Test(bool b, C c1, C c2) + { + (GetBool(b) ? ref GetC1(c1).F: ref (await GetC2(c2)).F) = 123; + } + + static bool GetBool(bool b) => b; + + static C GetC1(C c) => c; + + static async Task GetC2(C c) + { + await Task.Yield(); + return c; + } +} +"; + + var comp = CompileAndVerify(source, expectedOutput: "0123-1230", options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: "0123-1230", options: TestOptions.DebugExe); + + comp.VerifyIL("C.d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", +@" +{ + // Code size 209 (0xd1) + .maxstack 3 + .locals init (int V_0, + C V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__2.<>1__state"" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_005d + IL_000a: ldarg.0 + IL_000b: ldarg.0 + IL_000c: ldfld ""bool C.d__2.b"" + IL_0011: call ""bool C.GetBool(bool)"" + IL_0016: stfld ""bool C.d__2.<>7__wrap1"" + IL_001b: ldarg.0 + IL_001c: ldfld ""bool C.d__2.<>7__wrap1"" + IL_0021: brtrue.s IL_0081 + IL_0023: ldarg.0 + IL_0024: ldfld ""C C.d__2.c2"" + IL_0029: call ""System.Threading.Tasks.Task C.GetC2(C)"" + IL_002e: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0033: stloc.2 + IL_0034: ldloca.s V_2 + IL_0036: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_003b: brtrue.s IL_0079 + IL_003d: ldarg.0 + IL_003e: ldc.i4.0 + IL_003f: dup + IL_0040: stloc.0 + IL_0041: stfld ""int C.d__2.<>1__state"" + IL_0046: ldarg.0 + IL_0047: ldloc.2 + IL_0048: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_004d: ldarg.0 + IL_004e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_0053: ldloca.s V_2 + IL_0055: ldarg.0 + IL_0056: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_005b: leave.s IL_00d0 + IL_005d: ldarg.0 + IL_005e: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0063: stloc.2 + IL_0064: ldarg.0 + IL_0065: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_006a: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_0070: ldarg.0 + IL_0071: ldc.i4.m1 + IL_0072: dup + IL_0073: stloc.0 + IL_0074: stfld ""int C.d__2.<>1__state"" + IL_0079: ldloca.s V_2 + IL_007b: call ""C System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_0080: stloc.1 + IL_0081: ldarg.0 + IL_0082: ldfld ""bool C.d__2.<>7__wrap1"" + IL_0087: brtrue.s IL_0091 + IL_0089: ldloc.1 + IL_008a: ldflda ""int C.F"" + IL_008f: br.s IL_00a1 + IL_0091: ldarg.0 + IL_0092: ldfld ""C C.d__2.c1"" + IL_0097: call ""C C.GetC1(C)"" + IL_009c: ldflda ""int C.F"" + IL_00a1: ldc.i4.s 123 + IL_00a3: stind.i4 + IL_00a4: leave.s IL_00bd + } + catch System.Exception + { + IL_00a6: stloc.3 + IL_00a7: ldarg.0 + IL_00a8: ldc.i4.s -2 + IL_00aa: stfld ""int C.d__2.<>1__state"" + IL_00af: ldarg.0 + IL_00b0: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_00b5: ldloc.3 + IL_00b6: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_00bb: leave.s IL_00d0 + } + IL_00bd: ldarg.0 + IL_00be: ldc.i4.s -2 + IL_00c0: stfld ""int C.d__2.<>1__state"" + IL_00c5: ldarg.0 + IL_00c6: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_00cb: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00d0: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void AwaitInRefConditional_04() + { + var source = @" +using System.Threading.Tasks; + +class C +{ + int F; + + static async Task Main() + { + var c1 = new C(); + var c2 = new C(); + await Test(false, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + + System.Console.Write('-'); + + c1 = new C(); + c2 = new C(); + await Test(true, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + } + + static async Task Test(bool b, C c1, C c2) + { + ((await GetBool(b)) ? ref (await GetC1(c1)).F: ref (await GetC2(c2)).F) = 123; + } + + static async Task GetBool(bool b) + { + await Task.Yield(); + return b; + } + + static async Task GetC1(C c) + { + await Task.Yield(); + return c; + } + + static async Task GetC2(C c) + { + await Task.Yield(); + return c; + } +} +"; + + var comp = CompileAndVerify(source, expectedOutput: "0123-1230", options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: "0123-1230", options: TestOptions.DebugExe); + + comp.VerifyIL("C.d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", +@" +{ + // Code size 439 (0x1b7) + .maxstack 3 + .locals init (int V_0, + bool V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Runtime.CompilerServices.TaskAwaiter V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__2.<>1__state"" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: switch ( + IL_0056, + IL_00c6, + IL_012e) + IL_0019: ldarg.0 + IL_001a: ldfld ""bool C.d__2.b"" + IL_001f: call ""System.Threading.Tasks.Task C.GetBool(bool)"" + IL_0024: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0029: stloc.2 + IL_002a: ldloca.s V_2 + IL_002c: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_0031: brtrue.s IL_0072 + IL_0033: ldarg.0 + IL_0034: ldc.i4.0 + IL_0035: dup + IL_0036: stloc.0 + IL_0037: stfld ""int C.d__2.<>1__state"" + IL_003c: ldarg.0 + IL_003d: ldloc.2 + IL_003e: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0043: ldarg.0 + IL_0044: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_0049: ldloca.s V_2 + IL_004b: ldarg.0 + IL_004c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_0051: leave IL_01b6 + IL_0056: ldarg.0 + IL_0057: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_005c: stloc.2 + IL_005d: ldarg.0 + IL_005e: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0063: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_0069: ldarg.0 + IL_006a: ldc.i4.m1 + IL_006b: dup + IL_006c: stloc.0 + IL_006d: stfld ""int C.d__2.<>1__state"" + IL_0072: ldloca.s V_2 + IL_0074: call ""bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_0079: stloc.1 + IL_007a: ldarg.0 + IL_007b: ldloc.1 + IL_007c: stfld ""bool C.d__2.<>7__wrap1"" + IL_0081: ldarg.0 + IL_0082: ldfld ""bool C.d__2.<>7__wrap1"" + IL_0087: brfalse.s IL_00f1 + IL_0089: ldarg.0 + IL_008a: ldfld ""C C.d__2.c1"" + IL_008f: call ""System.Threading.Tasks.Task C.GetC1(C)"" + IL_0094: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0099: stloc.3 + IL_009a: ldloca.s V_3 + IL_009c: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_00a1: brtrue.s IL_00e2 + IL_00a3: ldarg.0 + IL_00a4: ldc.i4.1 + IL_00a5: dup + IL_00a6: stloc.0 + IL_00a7: stfld ""int C.d__2.<>1__state"" + IL_00ac: ldarg.0 + IL_00ad: ldloc.3 + IL_00ae: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__2"" + IL_00b3: ldarg.0 + IL_00b4: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_00b9: ldloca.s V_3 + IL_00bb: ldarg.0 + IL_00bc: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_00c1: leave IL_01b6 + IL_00c6: ldarg.0 + IL_00c7: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__2"" + IL_00cc: stloc.3 + IL_00cd: ldarg.0 + IL_00ce: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__2"" + IL_00d3: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_00d9: ldarg.0 + IL_00da: ldc.i4.m1 + IL_00db: dup + IL_00dc: stloc.0 + IL_00dd: stfld ""int C.d__2.<>1__state"" + IL_00e2: ldarg.0 + IL_00e3: ldloca.s V_3 + IL_00e5: call ""C System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_00ea: stfld ""C C.d__2.<>7__wrap2"" + IL_00ef: br.s IL_0157 + IL_00f1: ldarg.0 + IL_00f2: ldfld ""C C.d__2.c2"" + IL_00f7: call ""System.Threading.Tasks.Task C.GetC2(C)"" + IL_00fc: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0101: stloc.3 + IL_0102: ldloca.s V_3 + IL_0104: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_0109: brtrue.s IL_014a + IL_010b: ldarg.0 + IL_010c: ldc.i4.2 + IL_010d: dup + IL_010e: stloc.0 + IL_010f: stfld ""int C.d__2.<>1__state"" + IL_0114: ldarg.0 + IL_0115: ldloc.3 + IL_0116: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__2"" + IL_011b: ldarg.0 + IL_011c: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_0121: ldloca.s V_3 + IL_0123: ldarg.0 + IL_0124: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_0129: leave IL_01b6 + IL_012e: ldarg.0 + IL_012f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__2"" + IL_0134: stloc.3 + IL_0135: ldarg.0 + IL_0136: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__2"" + IL_013b: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_0141: ldarg.0 + IL_0142: ldc.i4.m1 + IL_0143: dup + IL_0144: stloc.0 + IL_0145: stfld ""int C.d__2.<>1__state"" + IL_014a: ldarg.0 + IL_014b: ldloca.s V_3 + IL_014d: call ""C System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_0152: stfld ""C C.d__2.<>7__wrap3"" + IL_0157: ldarg.0 + IL_0158: ldfld ""bool C.d__2.<>7__wrap1"" + IL_015d: brtrue.s IL_016c + IL_015f: ldarg.0 + IL_0160: ldfld ""C C.d__2.<>7__wrap3"" + IL_0165: ldflda ""int C.F"" + IL_016a: br.s IL_0177 + IL_016c: ldarg.0 + IL_016d: ldfld ""C C.d__2.<>7__wrap2"" + IL_0172: ldflda ""int C.F"" + IL_0177: ldc.i4.s 123 + IL_0179: stind.i4 + IL_017a: ldarg.0 + IL_017b: ldnull + IL_017c: stfld ""C C.d__2.<>7__wrap2"" + IL_0181: ldarg.0 + IL_0182: ldnull + IL_0183: stfld ""C C.d__2.<>7__wrap3"" + IL_0188: leave.s IL_01a3 + } + catch System.Exception + { + IL_018a: stloc.s V_4 + IL_018c: ldarg.0 + IL_018d: ldc.i4.s -2 + IL_018f: stfld ""int C.d__2.<>1__state"" + IL_0194: ldarg.0 + IL_0195: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_019a: ldloc.s V_4 + IL_019c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_01a1: leave.s IL_01b6 + } + IL_01a3: ldarg.0 + IL_01a4: ldc.i4.s -2 + IL_01a6: stfld ""int C.d__2.<>1__state"" + IL_01ab: ldarg.0 + IL_01ac: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_01b1: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_01b6: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void AwaitInRefConditional_05() + { + var source = @" +using System.Threading.Tasks; + +class C +{ + int F; + + static async Task Main() + { + var c1 = new C() { F = 123}; + var c2 = new C() { F = 124}; + await Test(false, c1, c2); + + System.Console.Write('-'); + + await Test(true, c1, c2); + } + + static async Task Test(bool b, C c1, C c2) + { + Test(b ? ref (await GetC(c1)).F : ref (await GetC(c2)).F, await GetC(new C())); + } + + static void Test(int x, C y) + { + System.Console.Write(x); + } + + static async Task GetC(C c) + { + await Task.Yield(); + return c; + } +} +"; + + var comp = CompileAndVerify(source, expectedOutput: "124-123", options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: "124-123", options: TestOptions.DebugExe); + + comp.VerifyIL("C.d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", +@" +{ + // Code size 453 (0x1c5) + .maxstack 3 + .locals init (int V_0, + C V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__2.<>1__state"" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: switch ( + IL_006a, + IL_00d2, + IL_015a) + IL_0019: ldarg.0 + IL_001a: ldarg.0 + IL_001b: ldfld ""bool C.d__2.b"" + IL_0020: stfld ""bool C.d__2.<>7__wrap1"" + IL_0025: ldarg.0 + IL_0026: ldfld ""bool C.d__2.<>7__wrap1"" + IL_002b: brfalse.s IL_0095 + IL_002d: ldarg.0 + IL_002e: ldfld ""C C.d__2.c1"" + IL_0033: call ""System.Threading.Tasks.Task C.GetC(C)"" + IL_0038: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_003d: stloc.2 + IL_003e: ldloca.s V_2 + IL_0040: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_0045: brtrue.s IL_0086 + IL_0047: ldarg.0 + IL_0048: ldc.i4.0 + IL_0049: dup + IL_004a: stloc.0 + IL_004b: stfld ""int C.d__2.<>1__state"" + IL_0050: ldarg.0 + IL_0051: ldloc.2 + IL_0052: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0057: ldarg.0 + IL_0058: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_005d: ldloca.s V_2 + IL_005f: ldarg.0 + IL_0060: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_0065: leave IL_01c4 + IL_006a: ldarg.0 + IL_006b: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0070: stloc.2 + IL_0071: ldarg.0 + IL_0072: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0077: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_007d: ldarg.0 + IL_007e: ldc.i4.m1 + IL_007f: dup + IL_0080: stloc.0 + IL_0081: stfld ""int C.d__2.<>1__state"" + IL_0086: ldarg.0 + IL_0087: ldloca.s V_2 + IL_0089: call ""C System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_008e: stfld ""C C.d__2.<>7__wrap2"" + IL_0093: br.s IL_00fb + IL_0095: ldarg.0 + IL_0096: ldfld ""C C.d__2.c2"" + IL_009b: call ""System.Threading.Tasks.Task C.GetC(C)"" + IL_00a0: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_00a5: stloc.2 + IL_00a6: ldloca.s V_2 + IL_00a8: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_00ad: brtrue.s IL_00ee + IL_00af: ldarg.0 + IL_00b0: ldc.i4.1 + IL_00b1: dup + IL_00b2: stloc.0 + IL_00b3: stfld ""int C.d__2.<>1__state"" + IL_00b8: ldarg.0 + IL_00b9: ldloc.2 + IL_00ba: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_00bf: ldarg.0 + IL_00c0: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_00c5: ldloca.s V_2 + IL_00c7: ldarg.0 + IL_00c8: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_00cd: leave IL_01c4 + IL_00d2: ldarg.0 + IL_00d3: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_00d8: stloc.2 + IL_00d9: ldarg.0 + IL_00da: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_00df: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_00e5: ldarg.0 + IL_00e6: ldc.i4.m1 + IL_00e7: dup + IL_00e8: stloc.0 + IL_00e9: stfld ""int C.d__2.<>1__state"" + IL_00ee: ldarg.0 + IL_00ef: ldloca.s V_2 + IL_00f1: call ""C System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_00f6: stfld ""C C.d__2.<>7__wrap3"" + IL_00fb: ldarg.0 + IL_00fc: ldarg.0 + IL_00fd: ldfld ""bool C.d__2.<>7__wrap1"" + IL_0102: brtrue.s IL_0111 + IL_0104: ldarg.0 + IL_0105: ldfld ""C C.d__2.<>7__wrap3"" + IL_010a: ldfld ""int C.F"" + IL_010f: br.s IL_011c + IL_0111: ldarg.0 + IL_0112: ldfld ""C C.d__2.<>7__wrap2"" + IL_0117: ldfld ""int C.F"" + IL_011c: stfld ""int C.d__2.<>7__wrap4"" + IL_0121: newobj ""C..ctor()"" + IL_0126: call ""System.Threading.Tasks.Task C.GetC(C)"" + IL_012b: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0130: stloc.2 + IL_0131: ldloca.s V_2 + IL_0133: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_0138: brtrue.s IL_0176 + IL_013a: ldarg.0 + IL_013b: ldc.i4.2 + IL_013c: dup + IL_013d: stloc.0 + IL_013e: stfld ""int C.d__2.<>1__state"" + IL_0143: ldarg.0 + IL_0144: ldloc.2 + IL_0145: stfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_014a: ldarg.0 + IL_014b: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_0150: ldloca.s V_2 + IL_0152: ldarg.0 + IL_0153: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.d__2>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.d__2)"" + IL_0158: leave.s IL_01c4 + IL_015a: ldarg.0 + IL_015b: ldfld ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0160: stloc.2 + IL_0161: ldarg.0 + IL_0162: ldflda ""System.Runtime.CompilerServices.TaskAwaiter C.d__2.<>u__1"" + IL_0167: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_016d: ldarg.0 + IL_016e: ldc.i4.m1 + IL_016f: dup + IL_0170: stloc.0 + IL_0171: stfld ""int C.d__2.<>1__state"" + IL_0176: ldloca.s V_2 + IL_0178: call ""C System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_017d: stloc.1 + IL_017e: ldarg.0 + IL_017f: ldfld ""int C.d__2.<>7__wrap4"" + IL_0184: ldloc.1 + IL_0185: call ""void C.Test(int, C)"" + IL_018a: ldarg.0 + IL_018b: ldnull + IL_018c: stfld ""C C.d__2.<>7__wrap2"" + IL_0191: ldarg.0 + IL_0192: ldnull + IL_0193: stfld ""C C.d__2.<>7__wrap3"" + IL_0198: leave.s IL_01b1 + } + catch System.Exception + { + IL_019a: stloc.3 + IL_019b: ldarg.0 + IL_019c: ldc.i4.s -2 + IL_019e: stfld ""int C.d__2.<>1__state"" + IL_01a3: ldarg.0 + IL_01a4: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_01a9: ldloc.3 + IL_01aa: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_01af: leave.s IL_01c4 + } + IL_01b1: ldarg.0 + IL_01b2: ldc.i4.s -2 + IL_01b4: stfld ""int C.d__2.<>1__state"" + IL_01b9: ldarg.0 + IL_01ba: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.d__2.<>t__builder"" + IL_01bf: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_01c4: ret +} +"); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void AwaitInRefConditional_06() + { + var source = @" +using System.Threading.Tasks; + +class C +{ + int F; + + static async Task Main() + { + var c1 = new C(); + var c2 = new C(); + await Test(false, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + + System.Console.Write('-'); + + c1 = new C(); + c2 = new C(); + await Test(true, c1, c2); + System.Console.Write(c1.F); + System.Console.Write(c2.F); + } + + static async Task Test(bool b, C c1, C c2) + { + Test(ref b ? ref (await GetC(c1)).F : ref (await GetC(c2)).F, await GetC(new C())); + Test(ref b ? ref c1.F : ref c2.F, await GetC(new C())); + } + + static void Test(ref int x, C y) + { + x = 123; + } + + static async Task GetC(C c) + { + await Task.Yield(); + return c; + } +} +"; + + var comp = CreateCompilation(source); + + comp.VerifyEmitDiagnostics( + // (27,18): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator + // Test(ref b ? ref (await GetC(c1)).F : ref (await GetC(c2)).F, await GetC(new C())); + Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b ? ref (await GetC(c1)).F : ref (await GetC(c2)).F").WithLocation(27, 18), + // (28,18): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator + // Test(ref b ? ref c1.F : ref c2.F, await GetC(new C())); + Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b ? ref c1.F : ref c2.F").WithLocation(28, 18) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void AwaitInRefConditional_07() + { + var source = @" +using System.Threading.Tasks; +using System; + +class C +{ + static void Main() + { + _ = NumberBuggy().Result; + Console.WriteLine(""--------""); + _ = NumberNotBuggy(); + } + + public static async Task NumberBuggy() + { + string str = ""a2""; + int x = 1; + int y = 2; + + ref int r = + ref await EvalAsync(str, 1) is ""a1"" ? ref x + : ref await EvalAsync(str, 2) is ""a2"" ? ref y + : ref System.Runtime.CompilerServices.Unsafe.NullRef(); + + r++; + r++; + r++; + int xxx = r; + System.Console.WriteLine(xxx); + System.Console.WriteLine(x); + System.Console.WriteLine(y); //should be 5 now! + return xxx; + } + + public static ValueTask NumberNotBuggy() + { + string str = ""a2""; + int x = 1; + int y = 2; + + ref int r = + ref EvalAsync(str, 1).Result is ""a1"" ? ref x + : ref EvalAsync(str, 2).Result is ""a2"" ? ref y + : ref System.Runtime.CompilerServices.Unsafe.NullRef(); + + r++; + r++; + r++; + int xxx = r; + System.Console.WriteLine(xxx); + System.Console.WriteLine(x); + System.Console.WriteLine(y); + return new(xxx); + } + static ValueTask EvalAsync(string s, int i) + { + System.Console.WriteLine($""{s} {i}""); + return ValueTask.FromResult(s); + } +} +"; + var expectedOutput = @" +a2 1 +a2 2 +5 +1 +5 +-------- +a2 1 +a2 2 +5 +1 +5 +"; + CompileAndVerify(source, targetFramework: TargetFramework.NetCoreApp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify); + CompileAndVerify(source, targetFramework: TargetFramework.NetCoreApp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null, options: TestOptions.DebugExe, verify: Verification.FailsPEVerify); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void SpillingInRefConditional_01() + { + var source = @" +using System; + +class C +{ + static void Main() + { + string str = ""a2""; + int x = 1; + int y = 2; + + ref int r = + ref str is ""Hallo"" ? ref x + : ref str is { Length: >= 2 and <= 10 or 22 } ? ref y + : ref System.Runtime.CompilerServices.Unsafe.NullRef(); + + r++; + r++; + r++; + int xxx = r; + System.Console.WriteLine(xxx); + System.Console.WriteLine(x); + System.Console.WriteLine(y); + } +} +"; + var expectedOutput = @" +5 +1 +5 +"; + CompileAndVerify(source, targetFramework: TargetFramework.NetCoreApp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify); + CompileAndVerify(source, targetFramework: TargetFramework.NetCoreApp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null, options: TestOptions.DebugExe, verify: Verification.FailsPEVerify); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/74115")] + public void SpillingInRefConditional_02() + { + var source = @" +using System; + +class C +{ + static void Main() + { + string str = ""a2""; + int x = 1; + int y = 2; + + ref readonly var xx = ref x; + + ref readonly int r = + ref Eval(str, 1) is ""Hallo"" ? ref xx + : ref Eval(str, 2) is { Length: >= 2 and <= 10 or 22 } ? ref y + : ref System.Runtime.CompilerServices.Unsafe.NullRef(); + + ref var rx = ref System.Runtime.CompilerServices.Unsafe.AsRef(in r); + + rx++; + rx++; + rx++; + int xxx = r; + System.Console.WriteLine(xxx); + System.Console.WriteLine(x); + System.Console.WriteLine(y); + } + + static T Eval(T s, int i) + { + System.Console.WriteLine($""{s} {i}""); + return s; + } +} +"; + var expectedOutput = @" +a2 1 +a2 2 +5 +1 +5 +"; + CompileAndVerify(source, targetFramework: TargetFramework.NetCoreApp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify); + CompileAndVerify(source, targetFramework: TargetFramework.NetCoreApp, expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null, options: TestOptions.DebugExe, verify: Verification.FailsPEVerify); + } } }