/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/CSharp15/LabeledBreakContinueEmitTests.cs
1 780 строк
53 KB
Cyrus Najmabadi
[labeled break/continue] NormalizeWhitespace and IL-vs-goto tests (#84228)
22 июн 2026, 23:43
Не верифицирован
22 июн 2026, 23:43
c90273a
Код
Авторство
О чём код?
// 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. using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics; public sealed class LabeledBreakContinueEmitTests : CSharpTestBase { // Tests that target Net100 can only execute on a runtime that supports it. // On other hosts (e.g. net472 CI) the emitted assembly references Net 10 // assemblies that cannot be loaded for execution, so skip the runtime check // by passing null for the expected output. private static string? IncludeExpectedOutput(string expectedOutput) => ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null; #region while [Fact] public void Break_While_Immediate() { var source = """ outer: while (true) { System.Console.Write("A "); break outer; } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A done").VerifyDiagnostics(); } [Fact] public void Break_While_Nested() { var source = """ outer: while (true) { while (true) { System.Console.Write("A "); break outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A done").VerifyDiagnostics( // (8,5): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(8, 5)); } [Fact] public void Continue_While_Immediate() { var source = """ int i = 0; outer: while (i < 3) { i++; System.Console.Write(i + " "); continue outer; } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "1 2 3 done").VerifyDiagnostics(); } [Fact] public void Continue_While_Nested() { var source = """ int i = 0; outer: while (i < 3) { i++; while (true) { System.Console.Write(i + " "); continue outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "1 2 3 done").VerifyDiagnostics( // (10,5): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(10, 5)); } #endregion #region do/while [Fact] public void Break_DoWhile_Immediate() { var source = """ outer: do { System.Console.Write("A "); break outer; } while (true); System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A done").VerifyDiagnostics(); } [Fact] public void Break_DoWhile_Nested() { var source = """ outer: do { do { System.Console.Write("A "); break outer; } while (true); System.Console.Write("SKIPPED "); } while (true); System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A done").VerifyDiagnostics( // (8,5): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(8, 5)); } [Fact] public void Continue_DoWhile_Immediate() { var source = """ int i = 0; outer: do { i++; System.Console.Write(i + " "); continue outer; } while (i < 3); System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "1 2 3 done").VerifyDiagnostics(); } [Fact] public void Continue_DoWhile_Nested() { var source = """ int i = 0; outer: do { i++; while (true) { System.Console.Write(i + " "); continue outer; } System.Console.Write("SKIPPED "); } while (i < 3); System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "1 2 3 done").VerifyDiagnostics( // (10,5): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(10, 5)); } #endregion #region for [Fact] public void Break_For_Immediate() { var source = """ outer: for (int i = 0; ; i++) { System.Console.Write(i + " "); break outer; } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "0 done").VerifyDiagnostics( // (1,26): warning CS0162: Unreachable code detected // outer: for (int i = 0; ; i++) Diagnostic(ErrorCode.WRN_UnreachableCode, "i").WithLocation(1, 26)); } [Fact] public void Break_For_Nested() { var source = """ outer: for (int i = 0; i < 3; i++) { for (int j = 0; ; j++) { System.Console.Write($"({i},{j}) "); break outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "(0,0) done").VerifyDiagnostics( // (3,23): warning CS0162: Unreachable code detected // for (int j = 0; ; j++) Diagnostic(ErrorCode.WRN_UnreachableCode, "j").WithLocation(3, 23), // (8,5): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(8, 5)); } [Fact] public void Continue_For_Immediate() { var source = """ outer: for (int i = 0; i < 3; i++) { System.Console.Write(i + " "); continue outer; } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "0 1 2 done").VerifyDiagnostics(); } [Fact] public void Continue_For_Nested() { var source = """ outer: for (int i = 0; i < 3; i++) { for (int j = 0; ; j++) { System.Console.Write($"({i},{j}) "); continue outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "(0,0) (1,0) (2,0) done").VerifyDiagnostics( // (3,23): warning CS0162: Unreachable code detected // for (int j = 0; ; j++) Diagnostic(ErrorCode.WRN_UnreachableCode, "j").WithLocation(3, 23), // (8,5): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(8, 5)); } #endregion #region foreach [Fact] public void Break_ForEach_Immediate() { var source = """ outer: foreach (var x in new[] { 1, 2, 3 }) { System.Console.Write(x + " "); break outer; } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "1 done").VerifyDiagnostics(); } [Fact] public void Break_ForEach_Nested() { var source = """ outer: foreach (var x in new[] { 1, 2, 3 }) { foreach (var y in new[] { 10, 20 }) { System.Console.Write($"{x}+{y} "); break outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "1+10 done").VerifyDiagnostics(); } [Fact] public void Continue_ForEach_Immediate() { var source = """ outer: foreach (var x in new[] { 1, 2, 3 }) { System.Console.Write(x + " "); continue outer; } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "1 2 3 done").VerifyDiagnostics(); } [Fact] public void Continue_ForEach_Nested() { var source = """ outer: foreach (var x in new[] { 1, 2, 3 }) { foreach (var y in new[] { 10, 20 }) { System.Console.Write($"{x}+{y} "); continue outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "1+10 2+10 3+10 done").VerifyDiagnostics(); } #endregion #region switch [Fact] public void Break_Switch_Immediate() { var source = """ int x = 1; outer: switch (x) { case 1: System.Console.Write("case1 "); break outer; } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "case1 done").VerifyDiagnostics(); } [Fact] public void Break_Switch_Nested() { var source = """ outer: while (true) { switch (1) { case 1: System.Console.Write("inner "); break outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "inner done").VerifyDiagnostics( // (9,5): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(9, 5)); } [Fact] public void Break_Switch_FromNestedSwitch() { var source = """ outer: switch (1) { case 1: switch (2) { case 2: System.Console.Write("nested "); break outer; } System.Console.Write("SKIPPED "); break; } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "nested done").VerifyDiagnostics( // (10,9): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(10, 9)); } [Fact] public void Continue_For_FromSwitch() { var source = """ outer: for (int i = 0; i < 3; i++) { switch (i) { case 0: System.Console.Write("zero "); continue outer; case 1: System.Console.Write("one "); continue outer; default: System.Console.Write("other "); continue outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "zero one other done").VerifyDiagnostics( // (15,5): warning CS0162: Unreachable code detected // System.Console.Write("SKIPPED "); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(15, 5)); } #endregion #region try/finally [Fact] public void Break_Finally_Single() { var source = """ outer: for (int i = 0; ; i++) { try { System.Console.Write("A "); break outer; } finally { System.Console.Write("F "); } } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A F done").VerifyDiagnostics( // (1,26): warning CS0162: Unreachable code detected // outer: for (int i = 0; ; i++) Diagnostic(ErrorCode.WRN_UnreachableCode, "i").WithLocation(1, 26)); } [Fact] public void Break_Finally_Multiple_InnermostFirst() { var source = """ outer: for (int i = 0; ; i++) { try { try { try { System.Console.Write("A "); break outer; } finally { System.Console.Write("F3 "); } } finally { System.Console.Write("F2 "); } } finally { System.Console.Write("F1 "); } } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A F3 F2 F1 done").VerifyDiagnostics( // (1,26): warning CS0162: Unreachable code detected // outer: for (int i = 0; ; i++) Diagnostic(ErrorCode.WRN_UnreachableCode, "i").WithLocation(1, 26)); } [Fact] public void Break_Finally_AcrossNestedLoops() { var source = """ outer: for (int i = 0; ; i++) { try { for (int j = 0; ; j++) { try { System.Console.Write("A "); break outer; } finally { System.Console.Write("Finner "); } } } finally { System.Console.Write("Fouter "); } } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A Finner Fouter done").VerifyDiagnostics( // (1,26): warning CS0162: Unreachable code detected // outer: for (int i = 0; ; i++) Diagnostic(ErrorCode.WRN_UnreachableCode, "i").WithLocation(1, 26), // (5,27): warning CS0162: Unreachable code detected // for (int j = 0; ; j++) Diagnostic(ErrorCode.WRN_UnreachableCode, "j").WithLocation(5, 27)); } [Fact] public void Break_Finally_OuterTryNotExited() { var source = """ try { outer: for (int i = 0; ; i++) { try { System.Console.Write("A "); break outer; } finally { System.Console.Write("Finner "); } } System.Console.Write("after-loop "); } finally { System.Console.Write("Fouter "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A Finner after-loop Fouter done").VerifyDiagnostics( // (3,30): warning CS0162: Unreachable code detected // outer: for (int i = 0; ; i++) Diagnostic(ErrorCode.WRN_UnreachableCode, "i").WithLocation(3, 30)); } [Fact] public void Continue_Finally_Single() { var source = """ int count = 0; outer: while (count < 3) { count++; try { System.Console.Write($"A{count} "); continue outer; } finally { System.Console.Write("F "); } } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A1 F A2 F A3 F done").VerifyDiagnostics(); } [Fact] public void Continue_Finally_Multiple_InnermostFirst() { var source = """ int count = 0; outer: while (count < 2) { count++; try { try { try { System.Console.Write($"A{count} "); continue outer; } finally { System.Console.Write("F3 "); } } finally { System.Console.Write("F2 "); } } finally { System.Console.Write("F1 "); } } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A1 F3 F2 F1 A2 F3 F2 F1 done").VerifyDiagnostics(); } [Fact] public void Continue_Finally_AcrossNestedLoops() { var source = """ int count = 0; outer: while (count < 2) { count++; try { for (int j = 0; ; j++) { try { System.Console.Write($"A{count} "); continue outer; } finally { System.Console.Write("Finner "); } } } finally { System.Console.Write("Fouter "); } } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A1 Finner Fouter A2 Finner Fouter done").VerifyDiagnostics( // (7,27): warning CS0162: Unreachable code detected // for (int j = 0; ; j++) Diagnostic(ErrorCode.WRN_UnreachableCode, "j").WithLocation(7, 27)); } [Fact] public void Continue_Finally_OuterTryNotExited() { var source = """ int count = 0; try { outer: while (count < 2) { count++; try { System.Console.Write($"A{count} "); continue outer; } finally { System.Console.Write("Finner "); } } System.Console.Write("after-loop "); } finally { System.Console.Write("Fouter "); } System.Console.Write("done"); """; CompileAndVerify(source, expectedOutput: "A1 Finner A2 Finner after-loop Fouter done").VerifyDiagnostics(); } #endregion #region await foreach and async iterator [Fact] public void Break_AwaitForeach_Labeled() { var source = """ using System.Collections.Generic; using System.Threading.Tasks; class C { static async Task Main() { outer: await foreach (var x in Items()) { await foreach (var y in Items()) { System.Console.Write($"{x}{y} "); if (x == 2) break outer; } } System.Console.Write("done"); } static async IAsyncEnumerable<int> Items() { await Task.Yield(); yield return 1; yield return 2; yield return 3; } } """; CompileAndVerify(source, targetFramework: TargetFramework.Net100, expectedOutput: IncludeExpectedOutput("11 12 13 21 done"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void Continue_AwaitForeach_Labeled() { var source = """ using System.Collections.Generic; using System.Threading.Tasks; class C { static async Task Main() { outer: await foreach (var x in Items()) { await foreach (var y in Items()) { System.Console.Write($"{x}{y} "); if (y == 1) continue outer; } System.Console.Write("SKIPPED "); } System.Console.Write("done"); } static async IAsyncEnumerable<int> Items() { await Task.Yield(); yield return 1; yield return 2; } } """; CompileAndVerify(source, targetFramework: TargetFramework.Net100, expectedOutput: IncludeExpectedOutput("11 21 done"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void Break_AsyncIterator_Labeled() { var source = """ using System.Collections.Generic; using System.Threading.Tasks; class C { static async Task Main() { await foreach (var x in Produce()) { System.Console.Write($"{x} "); } System.Console.Write("done"); } static async IAsyncEnumerable<int> Produce() { outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { await Task.Yield(); yield return i * 10 + j; if (i == 1 && j == 1) break outer; } } } } """; CompileAndVerify(source, targetFramework: TargetFramework.Net100, expectedOutput: IncludeExpectedOutput("0 1 2 10 11 done"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void Continue_AsyncIterator_Labeled() { var source = """ using System.Collections.Generic; using System.Threading.Tasks; class C { static async Task Main() { await foreach (var x in Produce()) { System.Console.Write($"{x} "); } System.Console.Write("done"); } static async IAsyncEnumerable<int> Produce() { outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { await Task.Yield(); if (j == 1) continue outer; yield return i * 10 + j; } } } } """; CompileAndVerify(source, targetFramework: TargetFramework.Net100, expectedOutput: IncludeExpectedOutput("0 10 20 done"), verify: Verification.Skipped).VerifyDiagnostics(); } #endregion #region Label name vs local name [Fact] public void Break_LocalWithSameNameAsOuterLabel_ResolvesToLabel() { var source = """ int sum = 0; outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { int outer = i * 3 + j; sum += outer; if (outer == 4) break outer; } } System.Console.Write($"sum={sum}"); """; CompileAndVerify(source, expectedOutput: "sum=10").VerifyDiagnostics(); } [Fact] public void Continue_LocalWithSameNameAsOuterLabel_ResolvesToLabel() { var source = """ var seen = ""; outer: for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { int outer = i; if (outer == 1) continue outer; seen += $"{i}{j} "; } } System.Console.Write(seen); """; CompileAndVerify(source, expectedOutput: "00 01 20 21 ").VerifyDiagnostics(); } #endregion #region IL verification [Fact] public void IL_Break_While_Nested() { var source = """ class C { static void M(bool b1, bool b2) { outer: while (b1) { while (b2) { break outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 9 (0x9) .maxstack 1 IL_0000: br.s IL_0005 IL_0002: ldarg.1 IL_0003: brtrue.s IL_0008 IL_0005: ldarg.0 IL_0006: brtrue.s IL_0002 IL_0008: ret } """); } [Fact] public void IL_Break_While_Nested_Conditional() { var source = """ class C { static void M(bool b1, bool b2, bool b3) { outer: while (b1) { while (b2) { if (b3) break outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: br.s IL_0008 IL_0002: ldarg.2 IL_0003: brtrue.s IL_000b IL_0005: ldarg.1 IL_0006: brtrue.s IL_0002 IL_0008: ldarg.0 IL_0009: brtrue.s IL_0005 IL_000b: ret } """); } [Fact] public void IL_Continue_While_Immediate() { var source = """ class C { static void M(bool b) { outer: while (b) { continue outer; } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 4 (0x4) .maxstack 1 IL_0000: ldarg.0 IL_0001: brtrue.s IL_0000 IL_0003: ret } """); } [Fact] public void IL_Continue_While_Nested() { var source = """ class C { static void M(bool b1, bool b2) { outer: while (b1) { while (b2) { continue outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 8 (0x8) .maxstack 1 IL_0000: br.s IL_0004 IL_0002: ldarg.1 IL_0003: pop IL_0004: ldarg.0 IL_0005: brtrue.s IL_0002 IL_0007: ret } """); } [Fact] public void IL_Continue_While_Nested_Conditional() { var source = """ class C { static void M(bool b1, bool b2, bool b3) { outer: while (b1) { while (b2) { if (b3) continue outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: br.s IL_0008 IL_0002: ldarg.2 IL_0003: brtrue.s IL_0008 IL_0005: ldarg.1 IL_0006: brtrue.s IL_0002 IL_0008: ldarg.0 IL_0009: brtrue.s IL_0005 IL_000b: ret } """); } [Fact] public void IL_Break_DoWhile_Nested() { var source = """ class C { static void M(bool b1, bool b2) { outer: do { do { break outer; } while (b2); } while (b1); } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 1 (0x1) .maxstack 1 IL_0000: ret } """); } [Fact] public void IL_Break_DoWhile_Nested_Conditional() { var source = """ class C { static void M(bool b1, bool b2, bool b3) { outer: do { do { if (b3) break outer; } while (b2); } while (b1); } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 10 (0xa) .maxstack 1 IL_0000: ldarg.2 IL_0001: brtrue.s IL_0009 IL_0003: ldarg.1 IL_0004: brtrue.s IL_0000 IL_0006: ldarg.0 IL_0007: brtrue.s IL_0000 IL_0009: ret } """); } [Fact] public void IL_Continue_DoWhile_Immediate() { var source = """ class C { static void M(bool b) { outer: do { continue outer; } while (b); } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 4 (0x4) .maxstack 1 IL_0000: ldarg.0 IL_0001: brtrue.s IL_0000 IL_0003: ret } """); } [Fact] public void IL_Continue_DoWhile_Nested() { var source = """ class C { static void M(bool b1, bool b2) { outer: do { do { continue outer; } while (b2); } while (b1); } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 4 (0x4) .maxstack 1 IL_0000: ldarg.0 IL_0001: brtrue.s IL_0000 IL_0003: ret } """); } [Fact] public void IL_Continue_DoWhile_Nested_Conditional() { var source = """ class C { static void M(bool b1, bool b2, bool b3) { outer: do { do { if (b3) continue outer; } while (b2); } while (b1); } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 10 (0xa) .maxstack 1 IL_0000: ldarg.2 IL_0001: brtrue.s IL_0006 IL_0003: ldarg.1 IL_0004: brtrue.s IL_0000 IL_0006: ldarg.0 IL_0007: brtrue.s IL_0000 IL_0009: ret } """); } [Fact] public void IL_Break_For_Nested() { var source = """ class C { static void M(bool b) { outer: for (int i = 0; i < 10; i++) { for (int j = 0; b; j++) { break outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics( // (7,32): warning CS0162: Unreachable code detected // for (int j = 0; b; j++) Diagnostic(ErrorCode.WRN_UnreachableCode, "j").WithLocation(7, 32)).VerifyIL("C.M", """ { // Code size 19 (0x13) .maxstack 2 .locals init (int V_0, //i int V_1) //j IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: br.s IL_000d IL_0004: ldc.i4.0 IL_0005: stloc.1 IL_0006: ldarg.0 IL_0007: brtrue.s IL_0012 IL_0009: ldloc.0 IL_000a: ldc.i4.1 IL_000b: add IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: ldc.i4.s 10 IL_0010: blt.s IL_0004 IL_0012: ret } """); } [Fact] public void IL_Break_For_Nested_Conditional() { var source = """ class C { static void M(bool b1, bool b2) { outer: for (int i = 0; i < 10; i++) { for (int j = 0; b1; j++) { if (b2) break outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 28 (0x1c) .maxstack 2 .locals init (int V_0, //i int V_1) //j IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: br.s IL_0016 IL_0004: ldc.i4.0 IL_0005: stloc.1 IL_0006: br.s IL_000f IL_0008: ldarg.1 IL_0009: brtrue.s IL_001b IL_000b: ldloc.1 IL_000c: ldc.i4.1 IL_000d: add IL_000e: stloc.1 IL_000f: ldarg.0 IL_0010: brtrue.s IL_0008 IL_0012: ldloc.0 IL_0013: ldc.i4.1 IL_0014: add IL_0015: stloc.0 IL_0016: ldloc.0 IL_0017: ldc.i4.s 10 IL_0019: blt.s IL_0004 IL_001b: ret } """); } [Fact] public void IL_Continue_For_Immediate() { var source = """ class C { static void M() { outer: for (int i = 0; i < 10; i++) { continue outer; } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 14 (0xe) .maxstack 2 .locals init (int V_0) //i IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: br.s IL_0008 IL_0004: ldloc.0 IL_0005: ldc.i4.1 IL_0006: add IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: ldc.i4.s 10 IL_000b: blt.s IL_0004 IL_000d: ret } """); } [Fact] public void IL_Continue_For_Nested() { var source = """ class C { static void M(bool b) { outer: for (int i = 0; i < 10; i++) { for (int j = 0; b; j++) { continue outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics( // (7,32): warning CS0162: Unreachable code detected // for (int j = 0; b; j++) Diagnostic(ErrorCode.WRN_UnreachableCode, "j").WithLocation(7, 32)).VerifyIL("C.M", """ { // Code size 18 (0x12) .maxstack 2 .locals init (int V_0, //i int V_1) //j IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: br.s IL_000c IL_0004: ldc.i4.0 IL_0005: stloc.1 IL_0006: ldarg.0 IL_0007: pop IL_0008: ldloc.0 IL_0009: ldc.i4.1 IL_000a: add IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: ldc.i4.s 10 IL_000f: blt.s IL_0004 IL_0011: ret } """); } [Fact] public void IL_Continue_For_Nested_Conditional() { var source = """ class C { static void M(bool b1, bool b2) { outer: for (int i = 0; i < 10; i++) { for (int j = 0; b1; j++) { if (b2) continue outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 28 (0x1c) .maxstack 2 .locals init (int V_0, //i int V_1) //j IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: br.s IL_0016 IL_0004: ldc.i4.0 IL_0005: stloc.1 IL_0006: br.s IL_000f IL_0008: ldarg.1 IL_0009: brtrue.s IL_0012 IL_000b: ldloc.1 IL_000c: ldc.i4.1 IL_000d: add IL_000e: stloc.1 IL_000f: ldarg.0 IL_0010: brtrue.s IL_0008 IL_0012: ldloc.0 IL_0013: ldc.i4.1 IL_0014: add IL_0015: stloc.0 IL_0016: ldloc.0 IL_0017: ldc.i4.s 10 IL_0019: blt.s IL_0004 IL_001b: ret } """); } [Fact] public void IL_Break_ForEach_Nested() { var source = """ class C { static void M(int[] a, int[] b) { outer: foreach (var x in a) { foreach (var y in b) { break outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 38 (0x26) .maxstack 2 .locals init (int[] V_0, int V_1, int[] V_2, int V_3) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: br.s IL_001f IL_0006: ldloc.0 IL_0007: ldloc.1 IL_0008: ldelem.i4 IL_0009: pop IL_000a: ldarg.1 IL_000b: stloc.2 IL_000c: ldc.i4.0 IL_000d: stloc.3 IL_000e: br.s IL_0015 IL_0010: ldloc.2 IL_0011: ldloc.3 IL_0012: ldelem.i4 IL_0013: pop IL_0014: ret IL_0015: ldloc.3 IL_0016: ldloc.2 IL_0017: ldlen IL_0018: conv.i4 IL_0019: blt.s IL_0010 IL_001b: ldloc.1 IL_001c: ldc.i4.1 IL_001d: add IL_001e: stloc.1 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldlen IL_0022: conv.i4 IL_0023: blt.s IL_0006 IL_0025: ret } """); } [Fact] public void IL_Continue_ForEach_Nested() { var source = """ class C { static void M(int[] a, int[] b) { outer: foreach (var x in a) { foreach (var y in b) { continue outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 39 (0x27) .maxstack 2 .locals init (int[] V_0, int V_1, int[] V_2, int V_3) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: br.s IL_0020 IL_0006: ldloc.0 IL_0007: ldloc.1 IL_0008: ldelem.i4 IL_0009: pop IL_000a: ldarg.1 IL_000b: stloc.2 IL_000c: ldc.i4.0 IL_000d: stloc.3 IL_000e: br.s IL_0016 IL_0010: ldloc.2 IL_0011: ldloc.3 IL_0012: ldelem.i4 IL_0013: pop IL_0014: br.s IL_001c IL_0016: ldloc.3 IL_0017: ldloc.2 IL_0018: ldlen IL_0019: conv.i4 IL_001a: blt.s IL_0010 IL_001c: ldloc.1 IL_001d: ldc.i4.1 IL_001e: add IL_001f: stloc.1 IL_0020: ldloc.1 IL_0021: ldloc.0 IL_0022: ldlen IL_0023: conv.i4 IL_0024: blt.s IL_0006 IL_0026: ret } """); } [Fact] public void IL_Break_Switch_Immediate() { var source = """ class C { static void M(int x) { outer: switch (x) { case 0: break outer; } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 3 (0x3) .maxstack 1 IL_0000: ldarg.0 IL_0001: pop IL_0002: ret } """); } [Fact] public void IL_Break_Switch_FromWhile() { var source = """ class C { static void M(bool b, int x) { outer: while (b) { switch (x) { case 0: break outer; } } } } """; CompileAndVerify(source).VerifyDiagnostics().VerifyIL("C.M", """ { // Code size 9 (0x9) .maxstack 1 IL_0000: br.s IL_0005 IL_0002: ldarg.1 IL_0003: brfalse.s IL_0008 IL_0005: ldarg.0 IL_0006: brtrue.s IL_0002 IL_0008: ret } """); } [Fact] public void IL_LabeledBreak_SameAsGoto() { // A labeled 'break' out of nested loops must emit the exact same IL as the equivalent 'goto' to a label // placed after the loops, so the JIT's loop recognition treats them identically. var source = """ class C { static void Labeled(bool b1, bool b2) { outer: while (b1) { while (b2) { break outer; } } } static void Goto(bool b1, bool b2) { while (b1) { while (b2) { goto outerEnd; } } outerEnd: ; } } """; var verifier = CompileAndVerify(source).VerifyDiagnostics(); const string expectedIL = """ { // Code size 9 (0x9) .maxstack 1 IL_0000: br.s IL_0005 IL_0002: ldarg.1 IL_0003: brtrue.s IL_0008 IL_0005: ldarg.0 IL_0006: brtrue.s IL_0002 IL_0008: ret } """; verifier.VerifyIL("C.Labeled", expectedIL); verifier.VerifyIL("C.Goto", expectedIL); } [Fact] public void IL_LabeledContinue_SameAsGoto() { // A labeled 'continue' to an outer loop must emit the exact same IL as the equivalent 'goto' to a label at // the end of that loop's body (which falls through to the condition re-check). var source = """ class C { static void Labeled(bool b1, bool b2) { outer: while (b1) { while (b2) { continue outer; } } } static void Goto(bool b1, bool b2) { while (b1) { while (b2) { goto outerContinue; } outerContinue: ; } } } """; var verifier = CompileAndVerify(source).VerifyDiagnostics(); const string expectedIL = """ { // Code size 8 (0x8) .maxstack 1 IL_0000: br.s IL_0004 IL_0002: ldarg.1 IL_0003: pop IL_0004: ldarg.0 IL_0005: brtrue.s IL_0002 IL_0007: ret } """; verifier.VerifyIL("C.Labeled", expectedIL); verifier.VerifyIL("C.Goto", expectedIL); } #endregion #region Sequence points [Fact] public void SequencePoints_LabeledBreak() { var source = """ class C { static void M(bool b) { outer: while (b) { while (b) { if (b) break outer; } } } } """; CompileAndVerify(source, options: TestOptions.DebugDll).VerifyIL("C.M", sequencePointDisplay: SequencePointDisplayMode.Enhanced, expectedIL: """ { // Code size 28 (0x1c) .maxstack 1 .locals init (bool V_0, bool V_1, bool V_2) // sequence point: { IL_0000: nop // sequence point: outer: IL_0001: nop // sequence point: <hidden> IL_0002: br.s IL_0016 // sequence point: { IL_0004: nop // sequence point: <hidden> IL_0005: br.s IL_0010 // sequence point: { IL_0007: nop // sequence point: if (b) IL_0008: ldarg.0 IL_0009: stloc.0 // sequence point: <hidden> IL_000a: ldloc.0 IL_000b: brfalse.s IL_000f // sequence point: break outer; IL_000d: br.s IL_001b // sequence point: } IL_000f: nop // sequence point: while (b) IL_0010: ldarg.0 IL_0011: stloc.1 // sequence point: <hidden> IL_0012: ldloc.1 IL_0013: brtrue.s IL_0007 // sequence point: } IL_0015: nop // sequence point: while (b) IL_0016: ldarg.0 IL_0017: stloc.2 // sequence point: <hidden> IL_0018: ldloc.2 IL_0019: brtrue.s IL_0004 // sequence point: } IL_001b: ret } """); } [Fact] public void SequencePoints_LabeledContinue() { var source = """ class C { static void M(bool b) { outer: while (b) { while (b) { if (b) continue outer; } } } } """; CompileAndVerify(source, options: TestOptions.DebugDll).VerifyIL("C.M", sequencePointDisplay: SequencePointDisplayMode.Enhanced, expectedIL: """ { // Code size 28 (0x1c) .maxstack 1 .locals init (bool V_0, bool V_1, bool V_2) // sequence point: { IL_0000: nop // sequence point: outer: IL_0001: nop // sequence point: <hidden> IL_0002: br.s IL_0016 // sequence point: { IL_0004: nop // sequence point: <hidden> IL_0005: br.s IL_0010 // sequence point: { IL_0007: nop // sequence point: if (b) IL_0008: ldarg.0 IL_0009: stloc.0 // sequence point: <hidden> IL_000a: ldloc.0 IL_000b: brfalse.s IL_000f // sequence point: continue outer; IL_000d: br.s IL_0016 // sequence point: } IL_000f: nop // sequence point: while (b) IL_0010: ldarg.0 IL_0011: stloc.1 // sequence point: <hidden> IL_0012: ldloc.1 IL_0013: brtrue.s IL_0007 // sequence point: } IL_0015: nop // sequence point: while (b) IL_0016: ldarg.0 IL_0017: stloc.2 // sequence point: <hidden> IL_0018: ldloc.2 IL_0019: brtrue.s IL_0004 // sequence point: } IL_001b: ret } """); } #endregion }