diff --git a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs index dabbfdf34e3a8..7aecf65e54e34 100644 --- a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorMethodToStateMachineRewriter.cs @@ -463,9 +463,10 @@ private IteratorFinallyFrame PushFrame(BoundTryStatement statement) // these nodes have to be tracked by the IDE EnC analyzer Debug.Assert( syntax.IsKind(SyntaxKind.TryStatement) || + syntax.IsKind(SyntaxKind.UsingStatement) || + syntax.IsKind(SyntaxKind.LocalDeclarationStatement) || syntax.IsKind(SyntaxKind.ForEachStatement) || - syntax.IsKind(SyntaxKind.LockStatement) || - syntax.IsKind(SyntaxKind.UsingStatement)); + syntax.IsKind(SyntaxKind.LockStatement)); if (slotAllocatorOpt?.TryGetPreviousStateMachineState(syntax, out var finalizeState) != true) { diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index 6b77a64d7e09a..c6e8925951cbe 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -232,12 +232,13 @@ protected void AddResumableState(SyntaxNode awaitOrYieldReturnSyntax, out int st protected void AddStateDebugInfo(SyntaxNode node, int stateNumber) { Debug.Assert( + node.IsKind(SyntaxKind.AwaitExpression) || + node.IsKind(SyntaxKind.YieldReturnStatement) || node.IsKind(SyntaxKind.TryStatement) || - node.IsKind(SyntaxKind.LockStatement) || node.IsKind(SyntaxKind.UsingStatement) || - node.IsKind(SyntaxKind.ForEachStatement) || - node.IsKind(SyntaxKind.AwaitExpression) || - node.IsKind(SyntaxKind.YieldReturnStatement)); + node.IsKind(SyntaxKind.LocalDeclarationStatement) || + node.IsKind(SyntaxKind.LockStatement) || + node.IsKind(SyntaxKind.ForEachStatement)); int syntaxOffset = CurrentMethod.CalculateLocalSyntaxOffset(node.SpanStart, node.SyntaxTree); _stateDebugInfoBuilder.Add(new StateMachineStateDebugInfo(syntaxOffset, stateNumber)); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index 931088a187389..98c8071c667ea 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -3569,7 +3569,7 @@ .locals init (int V_0) } [Fact] - public void UpdateIterator_AddYieldReturn_Finally() + public void UpdateIterator_AddYieldReturn_Finally_Try() { var source0 = MarkedSource(@" using System.Collections.Generic; @@ -3707,22 +3707,6 @@ static void Finally3(int gen) {} "System.Collections.IEnumerator.Current" }) + "}"); - v0.VerifyIL("C.d__0.<>m__Finally1", @" -{ - // Code size 17 (0x11) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldc.i4.m1 - IL_0002: stfld ""int C.d__0.<>1__state"" - IL_0007: nop - IL_0008: ldc.i4.0 - IL_0009: call ""void C.Finally1(int)"" - IL_000e: nop - IL_000f: nop - IL_0010: ret -} -"); - diff1.VerifyIL("C.d__0.<>m__Finally1", @" { // Code size 17 (0x11) @@ -3871,7 +3855,6 @@ .locals init (int V_0) } "); - v0.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" { // Code size 179 (0xb3) @@ -4084,6 +4067,332 @@ .locals init (bool V_0, }"); } + [Fact] + public void UpdateIterator_AddYieldReturn_Finally_UsingDeclaration() + { + var source0 = MarkedSource(@" +using System; +using System.Collections.Generic; + +class C +{ + static IEnumerable F() + { + using var x = M1(); + yield return M2(); + End(); + } + + static IDisposable M1() => null; + static int M2() => 0; + static int M3() => 0; + static void End() {} +}"); + var source1 = MarkedSource(@" +using System; +using System.Collections.Generic; + +class C +{ + static IEnumerable F() + { + using var x = M1(); + yield return M2(); + yield return M3(); + End(); + } + + static IDisposable M1() => null; + static int M2() => 0; + static int M3() => 0; + static void End() {} +}"); + var compilation0 = CreateCompilationWithMscorlib45(new[] { source0.Tree }, new[] { SystemCoreRef, CSharpRef }, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1.Tree); + + var v0 = CompileAndVerify(compilation0); + v0.VerifyDiagnostics(); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); + + v0.VerifyPdb("C.F", @" + + + + + + + + + + + + + + + + + + +"); + + diff1.VerifySynthesizedMembers( + "C: {d__0}", + "C.d__0: {" + string.Join(", ", new[] + { + "<>1__state", + "<>2__current", + "<>l__initialThreadId", + "5__1", + "System.IDisposable.Dispose", + "MoveNext", + "<>m__Finally1", + "System.Collections.Generic.IEnumerator.get_Current", + "System.Collections.IEnumerator.Reset, System.Collections.IEnumerator.get_Current", + "System.Collections.Generic.IEnumerable.GetEnumerator", + "System.Collections.IEnumerable.GetEnumerator", + "System.Collections.Generic.IEnumerator.Current", + "System.Collections.IEnumerator.Current" + }) + "}"); + + diff1.VerifyIL("C.d__0.<>m__Finally1", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.m1 + IL_0002: stfld ""int C.d__0.<>1__state"" + IL_0007: ldarg.0 + IL_0008: ldfld ""System.IDisposable C.d__0.5__1"" + IL_000d: brfalse.s IL_001b + IL_000f: ldarg.0 + IL_0010: ldfld ""System.IDisposable C.d__0.5__1"" + IL_0015: callvirt ""void System.IDisposable.Dispose()"" + IL_001a: nop + IL_001b: ret +} +"); + + v0.VerifyIL("C.d__0.System.IDisposable.Dispose", @" +{ + // Code size 33 (0x21) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__0.<>1__state"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -3 + IL_000a: beq.s IL_0014 + IL_000c: br.s IL_000e + IL_000e: ldloc.0 + IL_000f: ldc.i4.1 + IL_0010: beq.s IL_0014 + IL_0012: br.s IL_0020 + IL_0014: nop + .try + { + IL_0015: leave.s IL_001e + } + finally + { + IL_0017: ldarg.0 + IL_0018: call ""void C.d__0.<>m__Finally1()"" + IL_001d: endfinally + } + IL_001e: br.s IL_0020 + IL_0020: ret +} +"); + diff1.VerifyIL("C.d__0.System.IDisposable.Dispose", @" +{ + // Code size 35 (0x23) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__0.<>1__state"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -3 + IL_000a: beq.s IL_0016 + IL_000c: br.s IL_000e + IL_000e: ldloc.0 + IL_000f: ldc.i4.1 + IL_0010: sub + IL_0011: ldc.i4.1 + IL_0012: ble.un.s IL_0016 + IL_0014: br.s IL_0022 + IL_0016: nop + .try + { + IL_0017: leave.s IL_0020 + } + finally + { + IL_0019: ldarg.0 + IL_001a: call ""void C.d__0.<>m__Finally1()"" + IL_001f: endfinally + } + IL_0020: br.s IL_0022 + IL_0022: ret +} +"); + + v0.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" + { + // Code size 112 (0x70) + .maxstack 2 + .locals init (bool V_0, + int V_1) + .try + { + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__0.<>1__state"" + IL_0006: stloc.1 + IL_0007: ldloc.1 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.1 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0016 + IL_0012: br.s IL_001a + IL_0014: br.s IL_004b + IL_0016: ldc.i4.0 + IL_0017: stloc.0 + IL_0018: leave.s IL_006e + IL_001a: ldarg.0 + IL_001b: ldc.i4.m1 + IL_001c: stfld ""int C.d__0.<>1__state"" + IL_0021: nop + IL_0022: ldarg.0 + IL_0023: call ""System.IDisposable C.M1()"" + IL_0028: stfld ""System.IDisposable C.d__0.5__1"" + IL_002d: ldarg.0 + IL_002e: ldc.i4.s -3 + IL_0030: stfld ""int C.d__0.<>1__state"" + IL_0035: ldarg.0 + IL_0036: call ""int C.M2()"" + IL_003b: stfld ""int C.d__0.<>2__current"" + IL_0040: ldarg.0 + IL_0041: ldc.i4.1 + IL_0042: stfld ""int C.d__0.<>1__state"" + IL_0047: ldc.i4.1 + IL_0048: stloc.0 + IL_0049: leave.s IL_006e + IL_004b: ldarg.0 + IL_004c: ldc.i4.s -3 + IL_004e: stfld ""int C.d__0.<>1__state"" + IL_0053: call ""void C.End()"" + IL_0058: nop + IL_0059: ldc.i4.0 + IL_005a: stloc.0 + IL_005b: br.s IL_005d + IL_005d: ldarg.0 + IL_005e: call ""void C.d__0.<>m__Finally1()"" + IL_0063: nop + IL_0064: leave.s IL_006e + } + fault + { + IL_0066: ldarg.0 + IL_0067: call ""void C.d__0.Dispose()"" + IL_006c: nop + IL_006d: endfinally + } + IL_006e: ldloc.0 + IL_006f: ret +} +"); + + diff1.VerifyIL("C.d__0.System.Collections.IEnumerator.MoveNext", @" +{ + // Code size 153 (0x99) + .maxstack 2 + .locals init (bool V_0, + int V_1) + .try + { + IL_0000: ldarg.0 + IL_0001: ldfld ""int C.d__0.<>1__state"" + IL_0006: stloc.1 + IL_0007: ldloc.1 + IL_0008: switch ( + IL_001b, + IL_001d, + IL_001f) + IL_0019: br.s IL_0021 + IL_001b: br.s IL_0025 + IL_001d: br.s IL_0056 + IL_001f: br.s IL_0074 + IL_0021: ldc.i4.0 + IL_0022: stloc.0 + IL_0023: leave.s IL_0097 + IL_0025: ldarg.0 + IL_0026: ldc.i4.m1 + IL_0027: stfld ""int C.d__0.<>1__state"" + IL_002c: nop + IL_002d: ldarg.0 + IL_002e: call ""System.IDisposable C.M1()"" + IL_0033: stfld ""System.IDisposable C.d__0.5__1"" + IL_0038: ldarg.0 + IL_0039: ldc.i4.s -3 + IL_003b: stfld ""int C.d__0.<>1__state"" + IL_0040: ldarg.0 + IL_0041: call ""int C.M2()"" + IL_0046: stfld ""int C.d__0.<>2__current"" + IL_004b: ldarg.0 + IL_004c: ldc.i4.1 + IL_004d: stfld ""int C.d__0.<>1__state"" + IL_0052: ldc.i4.1 + IL_0053: stloc.0 + IL_0054: leave.s IL_0097 + IL_0056: ldarg.0 + IL_0057: ldc.i4.s -3 + IL_0059: stfld ""int C.d__0.<>1__state"" + IL_005e: ldarg.0 + IL_005f: call ""int C.M3()"" + IL_0064: stfld ""int C.d__0.<>2__current"" + IL_0069: ldarg.0 + IL_006a: ldc.i4.2 + IL_006b: stfld ""int C.d__0.<>1__state"" + IL_0070: ldc.i4.1 + IL_0071: stloc.0 + IL_0072: leave.s IL_0097 + IL_0074: ldarg.0 + IL_0075: ldc.i4.s -3 + IL_0077: stfld ""int C.d__0.<>1__state"" + IL_007c: call ""void C.End()"" + IL_0081: nop + IL_0082: ldc.i4.0 + IL_0083: stloc.0 + IL_0084: br.s IL_0086 + IL_0086: ldarg.0 + IL_0087: call ""void C.d__0.<>m__Finally1()"" + IL_008c: nop + IL_008d: leave.s IL_0097 + } + fault + { + IL_008f: ldarg.0 + IL_0090: call ""void C.d__0.Dispose()"" + IL_0095: nop + IL_0096: endfinally + } + IL_0097: ldloc.0 + IL_0098: ret +}"); + } + [Fact] public void HoistedVariables_MultipleGenerations() {