/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenIterators.cs
5 104 строки
142 KB
Julien Couvreur
Set state of enumerator object to "after" during disposal (#76090)
03 дек 2024, 00:10
Не верифицирован
03 дек 2024, 00:10
c4598d2
Код
Авторство
О чём код?
// 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. #nullable disable using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using System; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { [CompilerTrait(CompilerFeature.Iterator)] public class CodeGenIterators : CSharpTestBase { [Fact] public void TestSimpleIterator01() { var source = @"using System; using System.Collections; using System.Collections.Generic; class Program { static IEnumerator Ints0() { yield return 1; yield return 2; } static IEnumerator<int> Ints1() { yield return 3; yield return 4; } static IEnumerable Ints2() { yield return 5; yield return 6; } static IEnumerable<int> Ints3() { yield return 7; yield return 8; } static IEnumerable<int> Ints4() { yield return 9; throw new Exception(); } static void Main() { var e0 = Ints0(); { while (e0.MoveNext()) { Console.Write(e0.Current); } } using (var e1 = Ints1()) { while (e1.MoveNext()) { Console.Write(e1.Current); } } foreach (var i in Ints2()) { Console.Write(i); } foreach (var i in Ints3()) { Console.Write(i); } try { foreach (var i in Ints4()) { Console.Write(i); } } catch (Exception) { Console.Write('X'); } } }"; var compilation = CompileAndVerify(source, expectedOutput: "123456789X"); } [Fact] public void TestSimpleIterator02() { var source = @"using System.Collections.Generic; using System; class C { static IEnumerable<int> IE() { { int x = 0; yield return x++; yield return x++; } { int x = 2; yield return x++; yield return x++; { int y = 4; yield return y++; yield return y++; } } for (int i = 6; i < 10; i++) yield return i; } static void Main(string[] args) { foreach (var i in IE()) Console.Write(i); } }"; var compilation = CompileAndVerify(source, expectedOutput: "0123456789"); } [Fact] public void TestSimpleIterator03() { var source = @"using System.Collections.Generic; using System; struct S { int x; public S(int x) { this.x = x; } public IEnumerable<T> IE<T>(T i0, T i1) { Console.Write(x); yield return i0; yield return i1; yield return i0; } } class C { int x; public C(int x) { this.x = x; } public IEnumerable<T> IE<T>(T i0, T i1) { Console.Write(x); yield return i0; yield return i1; yield return i0; } } class Program { static void Main(string[] args) { foreach (var i in new S(1).IE(2, 3)) Console.Write(i); foreach (var i in new C(4).IE(5, 6)) Console.Write(i); } }"; var compilation = CompileAndVerifyWithMscorlib40(source, expectedOutput: "12324565"); compilation.VerifyIL("C.<IE>d__2<T>.System.Collections.Generic.IEnumerable<T>.GetEnumerator()", @" { // Code size 84 (0x54) .maxstack 2 .locals init (C.<IE>d__2<T> V_0) IL_0000: ldarg.0 IL_0001: ldfld ""int C.<IE>d__2<T>.<>1__state"" IL_0006: ldc.i4.s -2 IL_0008: bne.un.s IL_0027 IL_000a: ldarg.0 IL_000b: ldfld ""int C.<IE>d__2<T>.<>l__initialThreadId"" IL_0010: call ""System.Threading.Thread System.Threading.Thread.CurrentThread.get"" IL_0015: callvirt ""int System.Threading.Thread.ManagedThreadId.get"" IL_001a: bne.un.s IL_0027 IL_001c: ldarg.0 IL_001d: ldc.i4.0 IL_001e: stfld ""int C.<IE>d__2<T>.<>1__state"" IL_0023: ldarg.0 IL_0024: stloc.0 IL_0025: br.s IL_003a IL_0027: ldc.i4.0 IL_0028: newobj ""C.<IE>d__2<T>..ctor(int)"" IL_002d: stloc.0 IL_002e: ldloc.0 IL_002f: ldarg.0 IL_0030: ldfld ""C C.<IE>d__2<T>.<>4__this"" IL_0035: stfld ""C C.<IE>d__2<T>.<>4__this"" IL_003a: ldloc.0 IL_003b: ldarg.0 IL_003c: ldfld ""T C.<IE>d__2<T>.<>3__i0"" IL_0041: stfld ""T C.<IE>d__2<T>.i0"" IL_0046: ldloc.0 IL_0047: ldarg.0 IL_0048: ldfld ""T C.<IE>d__2<T>.<>3__i1"" IL_004d: stfld ""T C.<IE>d__2<T>.i1"" IL_0052: ldloc.0 IL_0053: ret }"); compilation.VerifyIL("S.<IE>d__2<T>.System.Collections.Generic.IEnumerable<T>.GetEnumerator()", @" { // Code size 84 (0x54) .maxstack 2 .locals init (S.<IE>d__2<T> V_0) IL_0000: ldarg.0 IL_0001: ldfld ""int S.<IE>d__2<T>.<>1__state"" IL_0006: ldc.i4.s -2 IL_0008: bne.un.s IL_0027 IL_000a: ldarg.0 IL_000b: ldfld ""int S.<IE>d__2<T>.<>l__initialThreadId"" IL_0010: call ""System.Threading.Thread System.Threading.Thread.CurrentThread.get"" IL_0015: callvirt ""int System.Threading.Thread.ManagedThreadId.get"" IL_001a: bne.un.s IL_0027 IL_001c: ldarg.0 IL_001d: ldc.i4.0 IL_001e: stfld ""int S.<IE>d__2<T>.<>1__state"" IL_0023: ldarg.0 IL_0024: stloc.0 IL_0025: br.s IL_002e IL_0027: ldc.i4.0 IL_0028: newobj ""S.<IE>d__2<T>..ctor(int)"" IL_002d: stloc.0 IL_002e: ldloc.0 IL_002f: ldarg.0 IL_0030: ldfld ""S S.<IE>d__2<T>.<>3__<>4__this"" IL_0035: stfld ""S S.<IE>d__2<T>.<>4__this"" IL_003a: ldloc.0 IL_003b: ldarg.0 IL_003c: ldfld ""T S.<IE>d__2<T>.<>3__i0"" IL_0041: stfld ""T S.<IE>d__2<T>.i0"" IL_0046: ldloc.0 IL_0047: ldarg.0 IL_0048: ldfld ""T S.<IE>d__2<T>.<>3__i1"" IL_004d: stfld ""T S.<IE>d__2<T>.i1"" IL_0052: ldloc.0 IL_0053: ret }"); } [Fact] public void TestSimpleIterator04() { var source = @"using System; using System.Collections.Generic; class Program { static IEnumerable<int> Int0() { yield return 0; try { yield return 1; try { yield return 2; } finally { Console.Write('X'); } yield return 3; try { yield return 4; } finally { Console.Write('Y'); } yield return 5; } finally { Console.Write('Z'); } yield return 6; } public static void Main(string[] args) { for (int i = 0; i < 7; i++) { if (i != 0) Console.Write('|'); foreach (var j in Int0()) { Console.Write(j); if (i == j) break; } } } }"; var compilation = CompileAndVerify(source, expectedOutput: "0|01Z|012XZ|012X3Z|012X34YZ|012X34Y5Z|012X34Y5Z6"); compilation.VerifyIL("Program.<Int0>d__0.System.Collections.IEnumerator.MoveNext()", @" { // Code size 303 (0x12f) .maxstack 2 .locals init (bool V_0, int V_1) .try { IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Int0>d__0.<>1__state"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: switch ( IL_0034, IL_0050, IL_0074, IL_0099, IL_00b9, IL_00db, IL_00fb, IL_011b) IL_002d: ldc.i4.0 IL_002e: stloc.0 IL_002f: leave IL_012d IL_0034: ldarg.0 IL_0035: ldc.i4.m1 IL_0036: stfld ""int Program.<Int0>d__0.<>1__state"" IL_003b: ldarg.0 IL_003c: ldc.i4.0 IL_003d: stfld ""int Program.<Int0>d__0.<>2__current"" IL_0042: ldarg.0 IL_0043: ldc.i4.1 IL_0044: stfld ""int Program.<Int0>d__0.<>1__state"" IL_0049: ldc.i4.1 IL_004a: stloc.0 IL_004b: leave IL_012d IL_0050: ldarg.0 IL_0051: ldc.i4.m1 IL_0052: stfld ""int Program.<Int0>d__0.<>1__state"" IL_0057: ldarg.0 IL_0058: ldc.i4.s -3 IL_005a: stfld ""int Program.<Int0>d__0.<>1__state"" IL_005f: ldarg.0 IL_0060: ldc.i4.1 IL_0061: stfld ""int Program.<Int0>d__0.<>2__current"" IL_0066: ldarg.0 IL_0067: ldc.i4.2 IL_0068: stfld ""int Program.<Int0>d__0.<>1__state"" IL_006d: ldc.i4.1 IL_006e: stloc.0 IL_006f: leave IL_012d IL_0074: ldarg.0 IL_0075: ldc.i4.s -3 IL_0077: stfld ""int Program.<Int0>d__0.<>1__state"" IL_007c: ldarg.0 IL_007d: ldc.i4.s -4 IL_007f: stfld ""int Program.<Int0>d__0.<>1__state"" IL_0084: ldarg.0 IL_0085: ldc.i4.2 IL_0086: stfld ""int Program.<Int0>d__0.<>2__current"" IL_008b: ldarg.0 IL_008c: ldc.i4.3 IL_008d: stfld ""int Program.<Int0>d__0.<>1__state"" IL_0092: ldc.i4.1 IL_0093: stloc.0 IL_0094: leave IL_012d IL_0099: ldarg.0 IL_009a: ldc.i4.s -4 IL_009c: stfld ""int Program.<Int0>d__0.<>1__state"" IL_00a1: ldarg.0 IL_00a2: call ""void Program.<Int0>d__0.<>m__Finally2()"" IL_00a7: ldarg.0 IL_00a8: ldc.i4.3 IL_00a9: stfld ""int Program.<Int0>d__0.<>2__current"" IL_00ae: ldarg.0 IL_00af: ldc.i4.4 IL_00b0: stfld ""int Program.<Int0>d__0.<>1__state"" IL_00b5: ldc.i4.1 IL_00b6: stloc.0 IL_00b7: leave.s IL_012d IL_00b9: ldarg.0 IL_00ba: ldc.i4.s -3 IL_00bc: stfld ""int Program.<Int0>d__0.<>1__state"" IL_00c1: ldarg.0 IL_00c2: ldc.i4.s -5 IL_00c4: stfld ""int Program.<Int0>d__0.<>1__state"" IL_00c9: ldarg.0 IL_00ca: ldc.i4.4 IL_00cb: stfld ""int Program.<Int0>d__0.<>2__current"" IL_00d0: ldarg.0 IL_00d1: ldc.i4.5 IL_00d2: stfld ""int Program.<Int0>d__0.<>1__state"" IL_00d7: ldc.i4.1 IL_00d8: stloc.0 IL_00d9: leave.s IL_012d IL_00db: ldarg.0 IL_00dc: ldc.i4.s -5 IL_00de: stfld ""int Program.<Int0>d__0.<>1__state"" IL_00e3: ldarg.0 IL_00e4: call ""void Program.<Int0>d__0.<>m__Finally3()"" IL_00e9: ldarg.0 IL_00ea: ldc.i4.5 IL_00eb: stfld ""int Program.<Int0>d__0.<>2__current"" IL_00f0: ldarg.0 IL_00f1: ldc.i4.6 IL_00f2: stfld ""int Program.<Int0>d__0.<>1__state"" IL_00f7: ldc.i4.1 IL_00f8: stloc.0 IL_00f9: leave.s IL_012d IL_00fb: ldarg.0 IL_00fc: ldc.i4.s -3 IL_00fe: stfld ""int Program.<Int0>d__0.<>1__state"" IL_0103: ldarg.0 IL_0104: call ""void Program.<Int0>d__0.<>m__Finally1()"" IL_0109: ldarg.0 IL_010a: ldc.i4.6 IL_010b: stfld ""int Program.<Int0>d__0.<>2__current"" IL_0110: ldarg.0 IL_0111: ldc.i4.7 IL_0112: stfld ""int Program.<Int0>d__0.<>1__state"" IL_0117: ldc.i4.1 IL_0118: stloc.0 IL_0119: leave.s IL_012d IL_011b: ldarg.0 IL_011c: ldc.i4.m1 IL_011d: stfld ""int Program.<Int0>d__0.<>1__state"" IL_0122: ldc.i4.0 IL_0123: stloc.0 IL_0124: leave.s IL_012d } fault { IL_0126: ldarg.0 IL_0127: call ""void Program.<Int0>d__0.Dispose()"" IL_012c: endfinally } IL_012d: ldloc.0 IL_012e: ret } "); compilation.VerifyIL("Program.<Int0>d__0.System.IDisposable.Dispose()", @" { // Code size 84 (0x54) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Int0>d__0.<>1__state"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s -5 IL_000a: sub IL_000b: ldc.i4.2 IL_000c: ble.un.s IL_0014 IL_000e: ldloc.0 IL_000f: ldc.i4.2 IL_0010: sub IL_0011: ldc.i4.4 IL_0012: bgt.un.s IL_004b IL_0014: nop .try { IL_0015: ldloc.0 IL_0016: ldc.i4.s -4 IL_0018: bgt.s IL_0026 IL_001a: ldloc.0 IL_001b: ldc.i4.s -5 IL_001d: beq.s IL_003a IL_001f: ldloc.0 IL_0020: ldc.i4.s -4 IL_0022: beq.s IL_0030 IL_0024: leave.s IL_004b IL_0026: ldloc.0 IL_0027: ldc.i4.3 IL_0028: beq.s IL_0030 IL_002a: ldloc.0 IL_002b: ldc.i4.5 IL_002c: beq.s IL_003a IL_002e: leave.s IL_004b IL_0030: nop .try { IL_0031: leave.s IL_004b } finally { IL_0033: ldarg.0 IL_0034: call ""void Program.<Int0>d__0.<>m__Finally2()"" IL_0039: endfinally } IL_003a: nop .try { IL_003b: leave.s IL_004b } finally { IL_003d: ldarg.0 IL_003e: call ""void Program.<Int0>d__0.<>m__Finally3()"" IL_0043: endfinally } } finally { IL_0044: ldarg.0 IL_0045: call ""void Program.<Int0>d__0.<>m__Finally1()"" IL_004a: endfinally } IL_004b: ldarg.0 IL_004c: ldc.i4.s -2 IL_004e: stfld ""int Program.<Int0>d__0.<>1__state"" IL_0053: ret } "); } [Fact] public void TestIteratorWithBaseAccess() { var source = @" using System; using System.Collections.Generic; class Test { static void Main() { foreach (string i in new Derived().Iter()) { Console.Write(i); } } } class Base { public virtual string Func() { return ""Base.Func;""; } } class Derived: Base { public override string Func() { return ""Derived.Func;""; } public IEnumerable<string> Iter() { yield return base.Func(); yield return this.Func(); } }"; CompileAndVerify(source, expectedOutput: "Base.Func;Derived.Func;"); } [Fact] public void TestIteratorWithBaseAccessInLambda() { var source = @" using System; using System.Collections.Generic; static class M1 { class B1<T> { public virtual string F<U>(T t, U u) { return ""B1::F;""; } } class Outer<V> { public class B2 : B1<V> { public override string F<U>(V t, U u) { return ""B2::F;""; } public void Test() { Action m = () => { foreach (string i in this.Iter()) { Console.Write(i); } }; m(); } public IEnumerable<string> Iter() { V v = default(V); int i = 0; string s = null; Func<string> f = () => { Func<Func<V, int, string>> ff = () => base.F<int>; return ff()(v, i); }; yield return f(); f = () => { Func<Func<V, string, string>> ff = () => this.F<string>; return ff()(v, s); }; yield return f(); f = () => { Func<Func<V, int, string>> ff = () => { i++; return base.F<int>; }; return ff()(v, i); }; yield return f(); } } } class D<X> : Outer<X>.B2 { public override string F<U>(X t, U u) { return ""D::F;""; } } static void Main() { (new D<int>()).Test(); } }"; CompileAndVerify(source, expectedOutput: "B1::F;D::F;B1::F;"); } [Fact] public void TestIteratorWithBaseAccessInLambda2() { var source = @" using System; using System.Collections.Generic; class Test { static void Main() { foreach (int i in new C().Iter()) { Console.Write(i); } } } class Base { public virtual int Func(int p) { return p * 444; } } class C: Base { public override int Func(int p) { return p * 222; } public IEnumerable<int> Iter() { int local = 0; Action<int> d = delegate(int jj2) { local = base.Func(jj2); }; d(1); yield return local; Func<int> dd = () => { local++; return base.Func(2); }; yield return dd(); } }"; CompileAndVerify(source, expectedOutput: "444888"); } [WorkItem(543165, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543165")] [Fact] public void TestIteratorWithLambda() { var source = @" using System; using System.Collections.Generic; class Test { static void Main() { foreach (int i in new C().Iter()) { Console.Write(i); } } } class C { public IEnumerable<int> Iter() { int local = 0; Action<int> d = delegate(int jj2) { local = jj2; }; d(3); yield return local; Func<int> dd = () => { return 6; }; yield return dd(); } } "; CompileAndVerify(source, expectedOutput: "36"); } [Fact] public void Legacy_basic_itr_block010() { var source = @"using System.Collections.Generic; using System.Collections; using System; class Ien<T> : IEnumerable<T> { List<T> items; public Ien(T t0, T t1) { items = new List<T>(); items.Add(t0); items.Add(t1); } public IEnumerator<T> GetEnumerator() { IEnumerator<T> ie = items.GetEnumerator(); foreach (T t in items) yield return t; } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } class Program { public static void Main(string[] args) { var x = new Ien<char>('a', 'b'); foreach (var c in x) Console.Write(c); var y = new Ien<int>(0, 1); foreach (var i in y) Console.Write(i); } }"; CompileAndVerify(source, expectedOutput: "ab01"); } [WorkItem(543178, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543178")] [Fact] public void TestIteratorWithLambda02() { var source = @" using System; using System.Collections; using System.Collections.Generic; public class A { static IEnumerable<Func<U>> Iter<T, U>(T t) where T : IEnumerable { foreach (U c in t) { yield return (Func<U>)delegate { return c; }; } } static void Main() { foreach (var item in Iter<string, char>(""abc"")) { Console.Write(item()); } } } "; CompileAndVerify(source, expectedOutput: "abc"); } [WorkItem(543373, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543373")] [Fact] public void TestIteratorWithNestedForEachAndThrow() { var source = @" using System.Collections.Generic; using System; public class Test { static IEnumerable<int> GetInts() { foreach (int i in new MyEnumerable(""Outer"")) { foreach (int j in new MyEnumerable(""Inner"")) { yield return 1; } throw new Exception(""ExInner""); } } public static string result; public static void Main() { try { foreach (int i in GetInts()) { } } catch (Exception e) { result += e.Message; } // Expect: inner, outer, ExInner Console.WriteLine(result); } } public class MyEnumerable : IEnumerable<int> { string name; public MyEnumerable(string name) { this.name = name; } public IEnumerator<int> GetEnumerator() { return new MyEnumerator(name); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new MyEnumerator(name); } } public class MyEnumerator : IEnumerator<int> { int[] collection = new int[] { 1 }; int index = -1; string name; public MyEnumerator(string name) { this.name = name; } public void Dispose() { Test.result += name; } bool System.Collections.IEnumerator.MoveNext() { if (index == 0) { index = -1; } else { index++; } return index != -1; } int IEnumerator<int>.Current { get { return collection[index]; } } object System.Collections.IEnumerator.Current { get { return collection[index]; } } void System.Collections.IEnumerator.Reset() { index = -1; } } "; CompileAndVerify(source, expectedOutput: "InnerOuterExInner"); } [WorkItem(543542, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543542")] [Fact] public void TestIteratorWithSwitchBreak() { var source = @" using System; using System.Collections.Generic; class Test { static void Main() { foreach (int i in Iter2(1)) { Console.Write(i); } } static public IEnumerable<int> Iter2(int x) { foreach (int i in new int[] { 1, 2, 3 }) { switch (x) { default: { yield return x + i; } break; } } } // Iter2 } "; CompileAndVerify(source, expectedOutput: "234"); } [WorkItem(546128, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546128")] [Fact] public void TestIteratorWithCapturedStruct() { var source = @"using System; using System.Collections.Generic; class Program { public static void Main(string[] args) { foreach (var b in Blend(3)) { Console.Write(b.N); } } static private IEnumerable<B> Blend(int limit) { int n = limit; B result; do { result = new B(n); yield return result; n = n - 1; } while (result.N > 0); } } struct B { public int N; public B(int n) { this.N = n; } }"; CompileAndVerify(source, expectedOutput: "3210"); } [Fact, WorkItem(544908, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544908")] public void TestIteratorWithNullableAsCollectionVariable_NonNull() { var source = @" using System; using System.Collections; class Program { static void Main() { S? s = new S(); int i = 0; foreach (object x in s) { i++; } Console.Write(i); } } struct S : IEnumerable { IEnumerator IEnumerable.GetEnumerator() { yield return """"; } } "; CompileAndVerify(source, expectedOutput: "1").VerifyIL("Program.Main", @" { // Code size 90 (0x5a) .maxstack 2 .locals init (S? V_0, //s int V_1, //i S V_2, System.Collections.IEnumerator V_3, System.IDisposable V_4) IL_0000: ldloca.s V_0 IL_0002: ldloca.s V_2 IL_0004: initobj ""S"" IL_000a: ldloc.2 IL_000b: call ""S?..ctor(S)"" IL_0010: ldc.i4.0 IL_0011: stloc.1 IL_0012: ldloca.s V_0 IL_0014: call ""S S?.Value.get"" IL_0019: stloc.2 IL_001a: ldloca.s V_2 IL_001c: constrained. ""S"" IL_0022: callvirt ""System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()"" IL_0027: stloc.3 .try { IL_0028: br.s IL_0035 IL_002a: ldloc.3 IL_002b: callvirt ""object System.Collections.IEnumerator.Current.get"" IL_0030: pop IL_0031: ldloc.1 IL_0032: ldc.i4.1 IL_0033: add IL_0034: stloc.1 IL_0035: ldloc.3 IL_0036: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" IL_003b: brtrue.s IL_002a IL_003d: leave.s IL_0053 } finally { IL_003f: ldloc.3 IL_0040: isinst ""System.IDisposable"" IL_0045: stloc.s V_4 IL_0047: ldloc.s V_4 IL_0049: brfalse.s IL_0052 IL_004b: ldloc.s V_4 IL_004d: callvirt ""void System.IDisposable.Dispose()"" IL_0052: endfinally } IL_0053: ldloc.1 IL_0054: call ""void System.Console.Write(int)"" IL_0059: ret }"); } [ConditionalFact(typeof(DesktopOnly))] [WorkItem(544908, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544908")] public void TestIteratorWithNullableAsCollectionVariable_Null() { var source = @" using System; using System.Collections; class Program { static void Main() { System.Globalization.CultureInfo saveUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; try { Test(); } finally { System.Threading.Thread.CurrentThread.CurrentUICulture = saveUICulture; } } static void Test() { S? s = null; int i = 0; foreach (object x in s) { i++; } Console.Write(i); } } struct S : IEnumerable { IEnumerator IEnumerable.GetEnumerator() { yield return """"; } } "; CompileAndVerifyException<InvalidOperationException>(source, expectedMessage: "Nullable object must have a value."). VerifyIL("Program.Test", @" { // Code size 82 (0x52) .maxstack 2 .locals init (S? V_0, //s int V_1, //i System.Collections.IEnumerator V_2, S V_3, System.IDisposable V_4) IL_0000: ldloca.s V_0 IL_0002: initobj ""S?"" IL_0008: ldc.i4.0 IL_0009: stloc.1 IL_000a: ldloca.s V_0 IL_000c: call ""S S?.Value.get"" IL_0011: stloc.3 IL_0012: ldloca.s V_3 IL_0014: constrained. ""S"" IL_001a: callvirt ""System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()"" IL_001f: stloc.2 .try { IL_0020: br.s IL_002d IL_0022: ldloc.2 IL_0023: callvirt ""object System.Collections.IEnumerator.Current.get"" IL_0028: pop IL_0029: ldloc.1 IL_002a: ldc.i4.1 IL_002b: add IL_002c: stloc.1 IL_002d: ldloc.2 IL_002e: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" IL_0033: brtrue.s IL_0022 IL_0035: leave.s IL_004b } finally { IL_0037: ldloc.2 IL_0038: isinst ""System.IDisposable"" IL_003d: stloc.s V_4 IL_003f: ldloc.s V_4 IL_0041: brfalse.s IL_004a IL_0043: ldloc.s V_4 IL_0045: callvirt ""void System.IDisposable.Dispose()"" IL_004a: endfinally } IL_004b: ldloc.1 IL_004c: call ""void System.Console.Write(int)"" IL_0051: ret }"); } [Fact] [WorkItem(545650, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545650")] public void TestIteratorWithUsing() { var source = @"using System; using System.Collections.Generic; class Test<T> { public static IEnumerator<T> M<U>(IEnumerable<T> items) where U : IDisposable, new() { T val = default(T); U u = default(U); using (u = new U()) { } yield return val; } } class T { static void Main() { } }"; CompileAndVerify(source, expectedOutput: ""); } [Fact, WorkItem(545767, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545767")] public void DoNotCaptureUnusedParameters_Release() { var source = @" using System; using System.Collections; using System.Collections.Generic; class Program { static void Main() { } public static IEnumerator<int> M(IEnumerable<int> items) { int x = 0; yield return x; } }"; var rel = CompileAndVerify(source, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => { AssertEx.Equal(new[] { "<>1__state", "<>2__current" }, module.GetFieldNames("Program.<M>d__1")); }); rel.VerifyIL("Program.M", @" { // Code size 7 (0x7) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: newobj ""Program.<M>d__1..ctor(int)"" IL_0006: ret }"); var dbg = CompileAndVerify(source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => { AssertEx.Equal(new[] { "<>1__state", "<>2__current", "items", "<x>5__1", }, module.GetFieldNames("Program.<M>d__1")); }); dbg.VerifyIL("Program.M", @" { // Code size 14 (0xe) .maxstack 3 IL_0000: ldc.i4.0 IL_0001: newobj ""Program.<M>d__1..ctor(int)"" IL_0006: dup IL_0007: ldarg.0 IL_0008: stfld ""System.Collections.Generic.IEnumerable<int> Program.<M>d__1.items"" IL_000d: ret }"); } [Fact] public void HoistedParameters_Enumerable() { var source = @" using System.Collections.Generic; struct Test { public static IEnumerable<int> F(int x, int y, int z) { x = z; yield return 1; y = 1; } }"; CompileAndVerify(source, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => { // consider: we don't really need to hoist "x" and "z", we could store the values of "<>3__x" and "<>3__z" to locals at the beginning of MoveNext. AssertEx.Equal(new[] { "<>1__state", "<>2__current", "<>l__initialThreadId", "x", "<>3__x", "z", "<>3__z", "y", "<>3__y", }, module.GetFieldNames("Test.<F>d__0")); }); CompileAndVerify(source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => { AssertEx.Equal(new[] { "<>1__state", "<>2__current", "<>l__initialThreadId", "x", "<>3__x", "y", "<>3__y", "z", "<>3__z", }, module.GetFieldNames("Test.<F>d__0")); }); } [Fact] public void HoistedParameters_Enumerator() { var source = @" using System.Collections.Generic; struct Test { public static IEnumerator<int> F(int x, int y, int z) { x = z; yield return 1; y = 1; } }"; CompileAndVerify(source, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => { AssertEx.Equal(new[] { "<>1__state", "<>2__current", "x", "z", "y", }, module.GetFieldNames("Test.<F>d__0")); }); CompileAndVerify(source, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => { AssertEx.Equal(new[] { "<>1__state", "<>2__current", "x", "y", "z", }, module.GetFieldNames("Test.<F>d__0")); }); } [Fact] public void IteratorForEach() { var source = @" using System; using System.Collections.Generic; class Test { public static IEnumerable<T> M<T>(IEnumerable<T> col) { foreach (var r in col) { yield return r; } } static void Main() { foreach (var rr in M(""abcdef"")) { System.Console.Write(rr); } } }"; CompileAndVerify(source, expectedOutput: "abcdef"). VerifyIL("Test.<M>d__0<T>.System.Collections.IEnumerator.MoveNext()", @"{ // Code size 129 (0x81) .maxstack 2 .locals init (bool V_0, int V_1, T V_2) //r .try { IL_0000: ldarg.0 IL_0001: ldfld ""int Test.<M>d__0<T>.<>1__state"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: brfalse.s IL_0012 IL_000a: ldloc.1 IL_000b: ldc.i4.1 IL_000c: beq.s IL_0052 IL_000e: ldc.i4.0 IL_000f: stloc.0 IL_0010: leave.s IL_007f IL_0012: ldarg.0 IL_0013: ldc.i4.m1 IL_0014: stfld ""int Test.<M>d__0<T>.<>1__state"" IL_0019: ldarg.0 IL_001a: ldarg.0 IL_001b: ldfld ""System.Collections.Generic.IEnumerable<T> Test.<M>d__0<T>.col"" IL_0020: callvirt ""System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()"" IL_0025: stfld ""System.Collections.Generic.IEnumerator<T> Test.<M>d__0<T>.<>7__wrap1"" IL_002a: ldarg.0 IL_002b: ldc.i4.s -3 IL_002d: stfld ""int Test.<M>d__0<T>.<>1__state"" IL_0032: br.s IL_005a IL_0034: ldarg.0 IL_0035: ldfld ""System.Collections.Generic.IEnumerator<T> Test.<M>d__0<T>.<>7__wrap1"" IL_003a: callvirt ""T System.Collections.Generic.IEnumerator<T>.Current.get"" IL_003f: stloc.2 IL_0040: ldarg.0 IL_0041: ldloc.2 IL_0042: stfld ""T Test.<M>d__0<T>.<>2__current"" IL_0047: ldarg.0 IL_0048: ldc.i4.1 IL_0049: stfld ""int Test.<M>d__0<T>.<>1__state"" IL_004e: ldc.i4.1 IL_004f: stloc.0 IL_0050: leave.s IL_007f IL_0052: ldarg.0 IL_0053: ldc.i4.s -3 IL_0055: stfld ""int Test.<M>d__0<T>.<>1__state"" IL_005a: ldarg.0 IL_005b: ldfld ""System.Collections.Generic.IEnumerator<T> Test.<M>d__0<T>.<>7__wrap1"" IL_0060: callvirt ""bool System.Collections.IEnumerator.MoveNext()"" IL_0065: brtrue.s IL_0034 IL_0067: ldarg.0 IL_0068: call ""void Test.<M>d__0<T>.<>m__Finally1()"" IL_006d: ldarg.0 IL_006e: ldnull IL_006f: stfld ""System.Collections.Generic.IEnumerator<T> Test.<M>d__0<T>.<>7__wrap1"" IL_0074: ldc.i4.0 IL_0075: stloc.0 IL_0076: leave.s IL_007f } fault { IL_0078: ldarg.0 IL_0079: call ""void Test.<M>d__0<T>.Dispose()"" IL_007e: endfinally } IL_007f: ldloc.0 IL_0080: ret }"); } [Fact] [WorkItem(563925, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/563925")] public void CaptureRefLocalNoParts() { var source = @"using System; using System.Collections.Generic; struct S { public int X; } class Program { public static IEnumerable<int> M() { S s = new S(); yield return s.X; s.X += 12; yield return s.X; } public static void Main(string[] args) { foreach (var i in M()) { Console.Write(i); } } }"; CompileAndVerify(source, expectedOutput: "012"); } [Fact] [WorkItem(590712, "DevDiv")] public void MultipassAnalysisWithRefLocal() { var source = @"using System; using System.Collections.Generic; struct S { public int I; } class Program { public static IEnumerable<int> M() { S s = new S(); for (int i = 0; i < 3; i++) { yield return i; s.I += 1; } } public static void Main(string[] args) { foreach (var i in M()) { Console.Write(i); } } }"; CompileAndVerify(source, expectedOutput: "012"); } [Fact] [WorkItem(620862, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/620862")] public void DelegateCreationInIterator() { var source = @"using System; using System.Collections.Generic; delegate int Delegate(int x); class Program1 { static int F(int x) { return x; } int F(double y) { return (int)y; } static IEnumerable<int> M() { Delegate d = new Delegate(F); yield return d(12); yield break; } static void Main() { foreach (int i in M()) { Console.WriteLine(i); } } }"; CompileAndVerify(source, expectedOutput: "12"); } [Fact] public void ForwardBranchInFinally() { var source = @" using System; using System.Collections.Generic; class Program { private static bool flag = true; public static IEnumerable<int> M() { try { yield return 1; while (true) { if (flag) { Console.Write(""Try""); } try { if (flag) { Console.Write(""NestedTry""); } break; } catch (Exception) { if (flag) { Console.Write(""NestedCatch""); } break; } finally { if (flag) { Console.Write(""NestedFinally""); } } } yield return 2; } finally { if (flag) { Console.Write(""Finally""); } } } public static void Main(string[] args) { foreach (var i in M()) { Console.Write(i); } } } "; CompileAndVerify(source, expectedOutput: "1TryNestedTryNestedFinally2Finally"); } [Fact] public void MultiLevelGoto() { var source = @" using System; using System.Collections.Generic; class Program { private static bool flag = true; public static IEnumerable<int> M() { try { yield return 1; try { yield return 2; try { yield return 3; try { yield return 4; try { yield return 5; goto L1; } finally { if (flag) { Console.Write(""Finally5""); } } } finally { if (flag) { Console.Write(""Finally4""); } } } finally { if (flag) { Console.Write(""Finally3""); } } } finally { if (flag) { Console.Write(""Finally2""); } } L1: Console.Write(""L1""); } finally { if (flag) { Console.Write(""Finally1""); } } } public static void Main(string[] args) { foreach (var i in M()) { Console.Write(i); } } } "; CompileAndVerify(source, expectedOutput: "12345Finally5Finally4Finally3Finally2L1Finally1"); } [Fact] public void MultiLevelGoto001() { var source = @" using System; using System.Collections.Generic; class Program { private static int i = 0; public static IEnumerable<int> M() { tryAgain: try { if (i == 0) goto L1; yield return 0; try { if (i == 1) goto L1; yield return 1; try { if (i == 2) goto L1; yield return 2; try { if (i == 3) goto L1; yield return 3; try { if (i == 4) goto L1; yield return 4; } finally { Console.Write(""Finally5""); } } finally { Console.Write(""Finally4""); } } finally { Console.Write(""Finally3""); } } finally { Console.Write(""Finally2""); } L1: Console.WriteLine(""L1""); if (i++ < 5) goto tryAgain; } finally { Console.Write(""Finally1""); } } public static void Main(string[] args) { foreach (var i in M()) { Console.Write(i); } } } "; CompileAndVerify(source, expectedOutput: @" L1 Finally10Finally2L1 Finally101Finally3Finally2L1 Finally1012Finally4Finally3Finally2L1 Finally10123Finally5Finally4Finally3Finally2L1 Finally101234Finally5Finally4Finally3Finally2L1 Finally1"); } [Fact] public void Switch001() { var source = @" using System; using System.Diagnostics; using System.Collections; using System.Collections.Generic; delegate T Del<T, R>(int a, string s, T t, R r); class Test { static void Main() { Console.WriteLine(""M""); } } class S<T1, T2> { T1 t1_field = default(T1); public IEnumerable<int> Iter2<M1, M2>(int x, string str) { try { using (DisposeImpl<M2> d = new DisposeImpl<M2>()) { foreach (T1 t in new T1[] { }) { Console.WriteLine(); } foreach (int ii in new int[] { 1, 2, 3, 4, 3, 3, 4, 5 }) { switch (str) { case ""blah"": yield return 1; break; case ""hoge"": yield break; break; case ""age"": yield return 1; break; case ""hogehoge"": yield return 1; break; } } } } finally { using (DisposeImpl<M2> d = new DisposeImpl<M2>()) { foreach (T1 t in new T1[] { }) { Del<M2, T1> del; foreach (int ii in new int[] { 1, 2, 3, 4, 3, 3, 4, 5 }) { switch (str) { case ""abcdef"": goto case ""blah""; case ""blah"": del = delegate { Console.WriteLine(d.ToString() + t.ToString() + ii + x + str + t1_field); return default(M2); }; break; } } } } } } class DisposeImpl<T> : IDisposable { public void Dispose() { Console.WriteLine(""D""); } } } "; CompileAndVerify(source, expectedOutput: @"M"); } [Fact] public void AssignedInFinally() { var source = @" using System; using System.Collections.Generic; class A { public static IEnumerable<int> Iterator() { int aa; int dd; try { yield return 5; } finally { aa = 42; dd = 42; } yield return dd; } static void Main() { foreach(int i in Iterator()) { Console.WriteLine(i); } } } "; CompileAndVerify(source, expectedOutput: @"5 42"); } [Fact] [WorkItem(703361, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/703361")] public void VerifyHelpers() { var source = @" using System.Collections.Generic; class Program { public static IEnumerable<int> Goo() { yield return 1; } } "; //EDMAURER ensure that we use System.Environment.CurrentManagedThreadId when compiling against 4.5 var parsed = new[] { Parse(source) }; var comp = CreateCompilationWithMscorlib461(parsed); var verifier = this.CompileAndVerify(comp); var il = verifier.VisualizeIL("Program.<Goo>d__0.System.Collections.Generic.IEnumerable<int>.GetEnumerator()"); Assert.Contains("System.Environment.CurrentManagedThreadId.get", il, StringComparison.Ordinal); } [Fact] [WorkItem(703361, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/703361")] public void VerifyHelpers001() { var source = @" using System.Collections.Generic; class Program { public static IEnumerable<int> Goo() { yield return 1; } } namespace System { class Environment { public static int CurrentManagedThreadId{get{return 1;}} } } "; var parsed = new[] { Parse(source) }; var comp = CreateCompilation(parsed); comp.MakeMemberMissing(WellKnownMember.System_Threading_Thread__ManagedThreadId); var verifier = this.CompileAndVerify(comp); var il = verifier.VisualizeIL("Program.<Goo>d__0.System.Collections.Generic.IEnumerable<int>.GetEnumerator()"); Assert.Contains("System.Environment.CurrentManagedThreadId.get", il, StringComparison.Ordinal); } [Fact] public void UnreachableExit() { var source = @" using System.Collections.Generic; using System; class Program { public static IEnumerable<T> GetOnce<T>(T item) { yield break; yield return item; } static void Main(string[] args) { int i = 0; foreach (int ii in GetOnce(1)) { i++; } Console.WriteLine(""DONE""); } } "; CompileAndVerify(source, expectedOutput: @"DONE"); } [Fact] public void Regress709127() { var source = @" using System.Collections; using System.Collections.Generic; using System; // Enumerator is an empty struct - why not? struct Enumerator : IEnumerator<int> { public int Current { get { throw new NotImplementedException(); } } public void Dispose() { } object IEnumerator.Current { get { throw new NotImplementedException(); } } public bool MoveNext() { return false; } public void Reset() { throw new NotImplementedException(); } } class C : IEnumerable<int> { public Enumerator GetEnumerator() { return new Enumerator(); } IEnumerator<int> IEnumerable<int>.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } class Program { private C moduleCatalog = new C(); static void Main(string[] args) { var x = new Program(); foreach (var v in x.GetModules()) { } } public IEnumerable<int> GetModules() { foreach (var catalog in moduleCatalog) yield return catalog; } }"; CompileAndVerify(source, expectedOutput: @""); } [WorkItem(718498, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/718498")] [Fact] public void Regress718498a() { var source = @"using System.Collections; using System.Collections.Generic; using System; struct Empty { } class Program { private static void M(Empty s) { } public static IEnumerable<int> M() { yield return 1; yield return 2; try { Empty s; // a variable of an empty struct type M(s); // is used before being assigned } finally // in a try with a finally { } } static void Main(string[] args) { foreach (var i in M()) { Console.Write(i); } } }"; CompileAndVerify(source, expectedOutput: @"12"); } [WorkItem(718498, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/718498")] [Fact] public void Regress718498b() { var source = @"using System.Collections; using System.Collections.Generic; using System; class Program { static void M(int x) { } public static IEnumerable<int> M() { try // a try-finally statement { yield return 1; // with a yield in its try block } finally { try // and a try block in its finally block { // containing a local variable declared and definitely assigned int x = 12; M(x); } finally { } } yield return 2; } static void Main(string[] args) { foreach (var i in M()) { Console.Write(i); } } }"; CompileAndVerify(source, expectedOutput: @"12"); } [WorkItem(718498, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/718498")] [Fact] public void Regress718498c() { var source = @"using System.Collections; using System.Collections.Generic; using System; class Program { public static IEnumerable<int> M() { int y; try { yield return 1; y = 1; } finally // since the finally is moved to a separate method, any outside variable it uses must be stored in the frame { if (1.ToString() == 1.ToString()) y = 2; } yield return 2; } static void Main(string[] args) { foreach (var i in M()) { Console.Write(i); } } }"; CompileAndVerify(source, expectedOutput: @"12"); } /// <summary> /// Name of public fields for spill temps must start with /// "<>[c]__" so the fields are hidden in the debugger. /// </summary> [WorkItem(808600, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/808600")] [Fact] public void SpillFieldName() { var source = @"class C<T> { static System.Collections.Generic.IEnumerable<T> F(System.IDisposable x, T[] y) { using (x) { foreach (var o in y) { yield return o; } } } }"; string expectedIL; CompileAndVerify(source).VerifyIL("C<T>.<F>d__0.System.Collections.IEnumerator.MoveNext()", expectedIL = @"{ // Code size 176 (0xb0) .maxstack 3 .locals init (bool V_0, int V_1, T V_2) //o .try { IL_0000: ldarg.0 IL_0001: ldfld ""int C<T>.<F>d__0.<>1__state"" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: brfalse.s IL_0015 IL_000a: ldloc.1 IL_000b: ldc.i4.1 IL_000c: beq.s IL_0069 IL_000e: ldc.i4.0 IL_000f: stloc.0 IL_0010: leave IL_00ae IL_0015: ldarg.0 IL_0016: ldc.i4.m1 IL_0017: stfld ""int C<T>.<F>d__0.<>1__state"" IL_001c: ldarg.0 IL_001d: ldarg.0 IL_001e: ldfld ""System.IDisposable C<T>.<F>d__0.x"" IL_0023: stfld ""System.IDisposable C<T>.<F>d__0.<>7__wrap1"" IL_0028: ldarg.0 IL_0029: ldc.i4.s -3 IL_002b: stfld ""int C<T>.<F>d__0.<>1__state"" IL_0030: ldarg.0 IL_0031: ldarg.0 IL_0032: ldfld ""T[] C<T>.<F>d__0.y"" IL_0037: stfld ""T[] C<T>.<F>d__0.<>7__wrap2"" IL_003c: ldarg.0 IL_003d: ldc.i4.0 IL_003e: stfld ""int C<T>.<F>d__0.<>7__wrap3"" IL_0043: br.s IL_007f IL_0045: ldarg.0 IL_0046: ldfld ""T[] C<T>.<F>d__0.<>7__wrap2"" IL_004b: ldarg.0 IL_004c: ldfld ""int C<T>.<F>d__0.<>7__wrap3"" IL_0051: ldelem ""T"" IL_0056: stloc.2 IL_0057: ldarg.0 IL_0058: ldloc.2 IL_0059: stfld ""T C<T>.<F>d__0.<>2__current"" IL_005e: ldarg.0 IL_005f: ldc.i4.1 IL_0060: stfld ""int C<T>.<F>d__0.<>1__state"" IL_0065: ldc.i4.1 IL_0066: stloc.0 IL_0067: leave.s IL_00ae IL_0069: ldarg.0 IL_006a: ldc.i4.s -3 IL_006c: stfld ""int C<T>.<F>d__0.<>1__state"" IL_0071: ldarg.0 IL_0072: ldarg.0 IL_0073: ldfld ""int C<T>.<F>d__0.<>7__wrap3"" IL_0078: ldc.i4.1 IL_0079: add IL_007a: stfld ""int C<T>.<F>d__0.<>7__wrap3"" IL_007f: ldarg.0 IL_0080: ldfld ""int C<T>.<F>d__0.<>7__wrap3"" IL_0085: ldarg.0 IL_0086: ldfld ""T[] C<T>.<F>d__0.<>7__wrap2"" IL_008b: ldlen IL_008c: conv.i4 IL_008d: blt.s IL_0045 IL_008f: ldarg.0 IL_0090: ldnull IL_0091: stfld ""T[] C<T>.<F>d__0.<>7__wrap2"" IL_0096: ldarg.0 IL_0097: call ""void C<T>.<F>d__0.<>m__Finally1()"" IL_009c: ldarg.0 IL_009d: ldnull IL_009e: stfld ""System.IDisposable C<T>.<F>d__0.<>7__wrap1"" IL_00a3: ldc.i4.0 IL_00a4: stloc.0 IL_00a5: leave.s IL_00ae } fault { IL_00a7: ldarg.0 IL_00a8: call ""void C<T>.<F>d__0.Dispose()"" IL_00ad: endfinally } IL_00ae: ldloc.0 IL_00af: ret }"); Assert.True(expectedIL.IndexOf("<>_", StringComparison.Ordinal) < 0); } [Fact, WorkItem(9167, "https://github.com/dotnet/roslyn/issues/9167")] public void IteratorShouldCompileWithoutOptionalAttributes() { #region IL for corlib without CompilerGeneratedAttribute or DebuggerNonUserCodeAttribute var corlib = @" namespace System { public class Object { } public struct Int32 { } public struct Boolean { } public class String { } public class Exception { } public class NotSupportedException : Exception { } public class ValueType { } public class Enum { } public struct Void { } public interface IDisposable { void Dispose(); } } namespace System.Collections { public interface IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerator { bool MoveNext(); object Current { get; } void Reset(); } namespace Generic { public interface IEnumerable<T> : IEnumerable { new IEnumerator<T> GetEnumerator(); } public interface IEnumerator<T> : IEnumerator { new T Current { get; } } } }"; #endregion var source = @" public class C { public System.Collections.IEnumerable SomeNumbers() { yield return 42; } }"; // The compilation succeeds even though CompilerGeneratedAttribute and DebuggerNonUserCodeAttribute are not available. var parseOptions = TestOptions.Regular.WithNoRefSafetyRulesAttribute(); var compilation = CreateEmptyCompilation(new[] { Parse(source, options: parseOptions), Parse(corlib, options: parseOptions) }); // PEVerify: System.Enum must extend System.ValueType. var verifier = CompileAndVerify(compilation, verify: Verification.FailsPEVerify); verifier.VerifyDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1)); } [Fact, WorkItem(9463, "https://github.com/dotnet/roslyn/issues/9463")] public void IEnumerableIteratorReportsDiagnosticsWhenCoreTypesAreMissing() { // Note that IDisposable.Dispose, IEnumerator.Current and other types are missing // Also, IEnumerator<T> doesn't have a get accessor var source = @" namespace System { public class Object { } public struct Int32 { } public struct Boolean { } public class String { } public class Exception { } public class ValueType { } public class Enum { } public struct Void { } public interface IDisposable { } } namespace System.Collections { public interface IEnumerable { } public interface IEnumerator { } } namespace System.Collections.Generic { public interface IEnumerator<T> { T Current { set; } } } public class C { public System.Collections.IEnumerable SomeNumbers() { yield return 42; } }"; var compilation = CreateEmptyCompilation(new[] { Parse(source, options: TestOptions.Regular.WithNoRefSafetyRulesAttribute()) }); compilation.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // (31,57): error CS0656: Missing compiler required member 'System.IDisposable.Dispose' // public System.Collections.IEnumerable SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.IDisposable", "Dispose").WithLocation(31, 57), // (31,57): error CS0154: The property or indexer 'IEnumerator<T>.Current' cannot be used in this context because it lacks the get accessor // public System.Collections.IEnumerable SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_PropertyLacksGet, "{ yield return 42; }").WithArguments("System.Collections.Generic.IEnumerator<T>.Current").WithLocation(31, 57), // (31,57): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.Current' // public System.Collections.IEnumerable SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.Collections.IEnumerator", "Current").WithLocation(31, 57), // (31,57): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.MoveNext' // public System.Collections.IEnumerable SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.Collections.IEnumerator", "MoveNext").WithLocation(31, 57), // (31,57): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.Reset' // public System.Collections.IEnumerable SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.Collections.IEnumerator", "Reset").WithLocation(31, 57), // (31,57): error CS0656: Missing compiler required member 'System.Collections.IEnumerable.GetEnumerator' // public System.Collections.IEnumerable SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.Collections.IEnumerable", "GetEnumerator").WithLocation(31, 57), // (31,57): error CS0518: Predefined type 'System.Collections.Generic.IEnumerable`1' is not defined or imported // public System.Collections.IEnumerable SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "{ yield return 42; }").WithArguments("System.Collections.Generic.IEnumerable`1").WithLocation(31, 57), // (31,57): error CS0656: Missing compiler required member 'System.Collections.Generic.IEnumerable`1.GetEnumerator' // public System.Collections.IEnumerable SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.Collections.Generic.IEnumerable`1", "GetEnumerator").WithLocation(31, 57)); } [Fact, WorkItem(9463, "https://github.com/dotnet/roslyn/issues/9463")] public void IEnumeratorIteratorReportsDiagnosticsWhenCoreTypesAreMissing() { // Note that IDisposable.Dispose and other types are missing // Also IEnumerator.Current lacks a get accessor var source = @" namespace System { public class Object { } public struct Int32 { } public struct Boolean { } public class String { } public class Exception { } public class ValueType { } public class Enum { } public struct Void { } public interface IDisposable { } } namespace System.Collections { public interface IEnumerable { } public interface IEnumerator { Object Current { set; } } } public class C { public System.Collections.IEnumerator SomeNumbers() { yield return 42; } }"; var compilation = CreateEmptyCompilation(new[] { Parse(source, options: TestOptions.Regular.WithNoRefSafetyRulesAttribute()) }); // No error about IEnumerable compilation.VerifyEmitDiagnostics( // (27,57): error CS0656: Missing compiler required member 'System.IDisposable.Dispose' // public System.Collections.IEnumerator SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.IDisposable", "Dispose").WithLocation(27, 57), // (27,57): error CS0518: Predefined type 'System.Collections.Generic.IEnumerator`1' is not defined or imported // public System.Collections.IEnumerator SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "{ yield return 42; }").WithArguments("System.Collections.Generic.IEnumerator`1").WithLocation(27, 57), // (27,57): error CS0656: Missing compiler required member 'System.Collections.Generic.IEnumerator`1.Current' // public System.Collections.IEnumerator SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.Collections.Generic.IEnumerator`1", "Current").WithLocation(27, 57), // (27,57): error CS0154: The property or indexer 'IEnumerator.Current' cannot be used in this context because it lacks the get accessor // public System.Collections.IEnumerator SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_PropertyLacksGet, "{ yield return 42; }").WithArguments("System.Collections.IEnumerator.Current").WithLocation(27, 57), // (27,57): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.MoveNext' // public System.Collections.IEnumerator SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.Collections.IEnumerator", "MoveNext").WithLocation(27, 57), // (27,57): error CS0656: Missing compiler required member 'System.Collections.IEnumerator.Reset' // public System.Collections.IEnumerator SomeNumbers() { yield return 42; } Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ yield return 42; }").WithArguments("System.Collections.IEnumerator", "Reset").WithLocation(27, 57), // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1)); } [Fact, WorkItem(21077, "https://github.com/dotnet/roslyn/issues/21077")] public void NoExtraCapturing_01() { // Note that the variable i is not captured in any of these three async methods. var source = @"using System.Threading.Tasks; class Program { static async Task Method1(bool value) { int i = 42; int j = i; await Task.Yield(); } static async Task Method2(bool value) { int i = 42; if (value) { int j = i; await Task.Yield(); } else { await Task.Yield(); } } static async Task Method3(bool value) { int i = 42; if (value) { await Task.Yield(); } else { int j = i; await Task.Yield(); } } }"; var v = CompileAndVerify(source, options: TestOptions.ReleaseDll); v.VerifyIL("Program.<Method1>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"{ // Code size 148 (0x94) .maxstack 3 .locals init (int V_0, System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, System.Runtime.CompilerServices.YieldAwaitable V_2, System.Exception V_3) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Method1>d__0.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_0044 IL_000a: ldc.i4.s 42 IL_000c: pop IL_000d: call ""System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()"" IL_0012: stloc.2 IL_0013: ldloca.s V_2 IL_0015: call ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()"" IL_001a: stloc.1 IL_001b: ldloca.s V_1 IL_001d: call ""bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.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 Program.<Method1>d__0.<>1__state"" IL_002d: ldarg.0 IL_002e: ldloc.1 IL_002f: stfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method1>d__0.<>u__1"" IL_0034: ldarg.0 IL_0035: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method1>d__0.<>t__builder"" IL_003a: ldloca.s V_1 IL_003c: ldarg.0 IL_003d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, Program.<Method1>d__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<Method1>d__0)"" IL_0042: leave.s IL_0093 IL_0044: ldarg.0 IL_0045: ldfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method1>d__0.<>u__1"" IL_004a: stloc.1 IL_004b: ldarg.0 IL_004c: ldflda ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method1>d__0.<>u__1"" IL_0051: initobj ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"" IL_0057: ldarg.0 IL_0058: ldc.i4.m1 IL_0059: dup IL_005a: stloc.0 IL_005b: stfld ""int Program.<Method1>d__0.<>1__state"" IL_0060: ldloca.s V_1 IL_0062: call ""void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()"" IL_0067: leave.s IL_0080 } catch System.Exception { IL_0069: stloc.3 IL_006a: ldarg.0 IL_006b: ldc.i4.s -2 IL_006d: stfld ""int Program.<Method1>d__0.<>1__state"" IL_0072: ldarg.0 IL_0073: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method1>d__0.<>t__builder"" IL_0078: ldloc.3 IL_0079: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_007e: leave.s IL_0093 } IL_0080: ldarg.0 IL_0081: ldc.i4.s -2 IL_0083: stfld ""int Program.<Method1>d__0.<>1__state"" IL_0088: ldarg.0 IL_0089: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method1>d__0.<>t__builder"" IL_008e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_0093: ret }"); v.VerifyIL("Program.<Method2>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"{ // Code size 260 (0x104) .maxstack 3 .locals init (int V_0, int V_1, //i System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, System.Runtime.CompilerServices.YieldAwaitable V_3, System.Exception V_4) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Method2>d__1.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_0056 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq IL_00b2 IL_0011: ldc.i4.s 42 IL_0013: stloc.1 IL_0014: ldarg.0 IL_0015: ldfld ""bool Program.<Method2>d__1.value"" IL_001a: brfalse.s IL_007b IL_001c: call ""System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()"" IL_0021: stloc.3 IL_0022: ldloca.s V_3 IL_0024: call ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()"" IL_0029: stloc.2 IL_002a: ldloca.s V_2 IL_002c: call ""bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.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 Program.<Method2>d__1.<>1__state"" IL_003c: ldarg.0 IL_003d: ldloc.2 IL_003e: stfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method2>d__1.<>u__1"" IL_0043: ldarg.0 IL_0044: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method2>d__1.<>t__builder"" IL_0049: ldloca.s V_2 IL_004b: ldarg.0 IL_004c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, Program.<Method2>d__1>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<Method2>d__1)"" IL_0051: leave IL_0103 IL_0056: ldarg.0 IL_0057: ldfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method2>d__1.<>u__1"" IL_005c: stloc.2 IL_005d: ldarg.0 IL_005e: ldflda ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method2>d__1.<>u__1"" IL_0063: initobj ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"" IL_0069: ldarg.0 IL_006a: ldc.i4.m1 IL_006b: dup IL_006c: stloc.0 IL_006d: stfld ""int Program.<Method2>d__1.<>1__state"" IL_0072: ldloca.s V_2 IL_0074: call ""void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()"" IL_0079: br.s IL_00d5 IL_007b: call ""System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()"" IL_0080: stloc.3 IL_0081: ldloca.s V_3 IL_0083: call ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()"" IL_0088: stloc.2 IL_0089: ldloca.s V_2 IL_008b: call ""bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get"" IL_0090: brtrue.s IL_00ce IL_0092: ldarg.0 IL_0093: ldc.i4.1 IL_0094: dup IL_0095: stloc.0 IL_0096: stfld ""int Program.<Method2>d__1.<>1__state"" IL_009b: ldarg.0 IL_009c: ldloc.2 IL_009d: stfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method2>d__1.<>u__1"" IL_00a2: ldarg.0 IL_00a3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method2>d__1.<>t__builder"" IL_00a8: ldloca.s V_2 IL_00aa: ldarg.0 IL_00ab: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, Program.<Method2>d__1>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<Method2>d__1)"" IL_00b0: leave.s IL_0103 IL_00b2: ldarg.0 IL_00b3: ldfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method2>d__1.<>u__1"" IL_00b8: stloc.2 IL_00b9: ldarg.0 IL_00ba: ldflda ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method2>d__1.<>u__1"" IL_00bf: initobj ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"" IL_00c5: ldarg.0 IL_00c6: ldc.i4.m1 IL_00c7: dup IL_00c8: stloc.0 IL_00c9: stfld ""int Program.<Method2>d__1.<>1__state"" IL_00ce: ldloca.s V_2 IL_00d0: call ""void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()"" IL_00d5: leave.s IL_00f0 } catch System.Exception { IL_00d7: stloc.s V_4 IL_00d9: ldarg.0 IL_00da: ldc.i4.s -2 IL_00dc: stfld ""int Program.<Method2>d__1.<>1__state"" IL_00e1: ldarg.0 IL_00e2: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method2>d__1.<>t__builder"" IL_00e7: ldloc.s V_4 IL_00e9: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_00ee: leave.s IL_0103 } IL_00f0: ldarg.0 IL_00f1: ldc.i4.s -2 IL_00f3: stfld ""int Program.<Method2>d__1.<>1__state"" IL_00f8: ldarg.0 IL_00f9: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method2>d__1.<>t__builder"" IL_00fe: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_0103: ret }"); v.VerifyIL("Program.<Method3>d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @"{ // Code size 260 (0x104) .maxstack 3 .locals init (int V_0, int V_1, //i System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, System.Runtime.CompilerServices.YieldAwaitable V_3, System.Exception V_4) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Method3>d__2.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_0056 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq IL_00b2 IL_0011: ldc.i4.s 42 IL_0013: stloc.1 IL_0014: ldarg.0 IL_0015: ldfld ""bool Program.<Method3>d__2.value"" IL_001a: brfalse.s IL_007b IL_001c: call ""System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()"" IL_0021: stloc.3 IL_0022: ldloca.s V_3 IL_0024: call ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()"" IL_0029: stloc.2 IL_002a: ldloca.s V_2 IL_002c: call ""bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.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 Program.<Method3>d__2.<>1__state"" IL_003c: ldarg.0 IL_003d: ldloc.2 IL_003e: stfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method3>d__2.<>u__1"" IL_0043: ldarg.0 IL_0044: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method3>d__2.<>t__builder"" IL_0049: ldloca.s V_2 IL_004b: ldarg.0 IL_004c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, Program.<Method3>d__2>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<Method3>d__2)"" IL_0051: leave IL_0103 IL_0056: ldarg.0 IL_0057: ldfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method3>d__2.<>u__1"" IL_005c: stloc.2 IL_005d: ldarg.0 IL_005e: ldflda ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method3>d__2.<>u__1"" IL_0063: initobj ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"" IL_0069: ldarg.0 IL_006a: ldc.i4.m1 IL_006b: dup IL_006c: stloc.0 IL_006d: stfld ""int Program.<Method3>d__2.<>1__state"" IL_0072: ldloca.s V_2 IL_0074: call ""void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()"" IL_0079: br.s IL_00d5 IL_007b: call ""System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()"" IL_0080: stloc.3 IL_0081: ldloca.s V_3 IL_0083: call ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()"" IL_0088: stloc.2 IL_0089: ldloca.s V_2 IL_008b: call ""bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get"" IL_0090: brtrue.s IL_00ce IL_0092: ldarg.0 IL_0093: ldc.i4.1 IL_0094: dup IL_0095: stloc.0 IL_0096: stfld ""int Program.<Method3>d__2.<>1__state"" IL_009b: ldarg.0 IL_009c: ldloc.2 IL_009d: stfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method3>d__2.<>u__1"" IL_00a2: ldarg.0 IL_00a3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method3>d__2.<>t__builder"" IL_00a8: ldloca.s V_2 IL_00aa: ldarg.0 IL_00ab: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, Program.<Method3>d__2>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<Method3>d__2)"" IL_00b0: leave.s IL_0103 IL_00b2: ldarg.0 IL_00b3: ldfld ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method3>d__2.<>u__1"" IL_00b8: stloc.2 IL_00b9: ldarg.0 IL_00ba: ldflda ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<Method3>d__2.<>u__1"" IL_00bf: initobj ""System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"" IL_00c5: ldarg.0 IL_00c6: ldc.i4.m1 IL_00c7: dup IL_00c8: stloc.0 IL_00c9: stfld ""int Program.<Method3>d__2.<>1__state"" IL_00ce: ldloca.s V_2 IL_00d0: call ""void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()"" IL_00d5: leave.s IL_00f0 } catch System.Exception { IL_00d7: stloc.s V_4 IL_00d9: ldarg.0 IL_00da: ldc.i4.s -2 IL_00dc: stfld ""int Program.<Method3>d__2.<>1__state"" IL_00e1: ldarg.0 IL_00e2: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method3>d__2.<>t__builder"" IL_00e7: ldloc.s V_4 IL_00e9: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_00ee: leave.s IL_0103 } IL_00f0: ldarg.0 IL_00f1: ldc.i4.s -2 IL_00f3: stfld ""int Program.<Method3>d__2.<>1__state"" IL_00f8: ldarg.0 IL_00f9: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Method3>d__2.<>t__builder"" IL_00fe: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_0103: ret }"); } [Fact, WorkItem(5062, "https://github.com/dotnet/roslyn/issues/5062")] public void LocalLiftingVsSwitch() { var source = @"using System; using System.Collections.Generic; class Program { static void Main() { foreach (var s in Iter1(0)) Console.Write(s); foreach (var s in Iter1(1)) Console.Write(s); foreach (var s in Iter2(0)) Console.Write(s); foreach (var s in Iter2(1)) Console.Write(s); } static IEnumerable<string> Iter1(int i) { bool result; switch (i) { case 1: result = true; break; default: result = false; break; } yield return result.ToString(); } static IEnumerable<string> Iter2(int i) { bool result; if (i == 1) result = true; else result = false; yield return result.ToString(); } }"; var compilation = CompileAndVerify(source, expectedOutput: "FalseTrueFalseTrue", options: TestOptions.ReleaseExe); compilation.VerifyIL("Program.<Iter1>d__1.System.Collections.IEnumerator.MoveNext()", @"{ // Code size 69 (0x45) .maxstack 2 .locals init (int V_0, bool V_1) //result IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Iter1>d__1.<>1__state"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0010 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq.s IL_003c IL_000e: ldc.i4.0 IL_000f: ret IL_0010: ldarg.0 IL_0011: ldc.i4.m1 IL_0012: stfld ""int Program.<Iter1>d__1.<>1__state"" IL_0017: ldarg.0 IL_0018: ldfld ""int Program.<Iter1>d__1.i"" IL_001d: ldc.i4.1 IL_001e: bne.un.s IL_0024 IL_0020: ldc.i4.1 IL_0021: stloc.1 IL_0022: br.s IL_0026 IL_0024: ldc.i4.0 IL_0025: stloc.1 IL_0026: ldarg.0 IL_0027: ldloca.s V_1 IL_0029: call ""string bool.ToString()"" IL_002e: stfld ""string Program.<Iter1>d__1.<>2__current"" IL_0033: ldarg.0 IL_0034: ldc.i4.1 IL_0035: stfld ""int Program.<Iter1>d__1.<>1__state"" IL_003a: ldc.i4.1 IL_003b: ret IL_003c: ldarg.0 IL_003d: ldc.i4.m1 IL_003e: stfld ""int Program.<Iter1>d__1.<>1__state"" IL_0043: ldc.i4.0 IL_0044: ret }"); compilation.VerifyIL("Program.<Iter2>d__2.System.Collections.IEnumerator.MoveNext()", @"{ // Code size 69 (0x45) .maxstack 2 .locals init (int V_0, bool V_1) //result IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Iter2>d__2.<>1__state"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0010 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq.s IL_003c IL_000e: ldc.i4.0 IL_000f: ret IL_0010: ldarg.0 IL_0011: ldc.i4.m1 IL_0012: stfld ""int Program.<Iter2>d__2.<>1__state"" IL_0017: ldarg.0 IL_0018: ldfld ""int Program.<Iter2>d__2.i"" IL_001d: ldc.i4.1 IL_001e: bne.un.s IL_0024 IL_0020: ldc.i4.1 IL_0021: stloc.1 IL_0022: br.s IL_0026 IL_0024: ldc.i4.0 IL_0025: stloc.1 IL_0026: ldarg.0 IL_0027: ldloca.s V_1 IL_0029: call ""string bool.ToString()"" IL_002e: stfld ""string Program.<Iter2>d__2.<>2__current"" IL_0033: ldarg.0 IL_0034: ldc.i4.1 IL_0035: stfld ""int Program.<Iter2>d__2.<>1__state"" IL_003a: ldc.i4.1 IL_003b: ret IL_003c: ldarg.0 IL_003d: ldc.i4.m1 IL_003e: stfld ""int Program.<Iter2>d__2.<>1__state"" IL_0043: ldc.i4.0 IL_0044: ret }"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_IntLocal() { string src = """ using System.Reflection; var values = C.Produce(); foreach (int value in values) { } System.Console.Write(((int)values.GetType().GetField("<values2>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { int values2 = 42; yield return values2; System.Console.Write($"{values2} "); } } """; var verifier = CompileAndVerify(src, expectedOutput: "42 42").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<Produce>d__0.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_StringLocal() { string src = """ using System.Reflection; var values = C.Produce(); foreach (int value in values) { System.Console.Write(((string)values.GetType().GetField("<values2>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); } System.Console.Write(((string)values.GetType().GetField("<values2>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { string values2 = "ran "; yield return 42; System.Console.Write(values2); } } """; var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "string C.<Produce>d__0.<values2>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C.<Produce>d__0.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_StringLocal_YieldBreak() { string src = """ using System.Reflection; var values = C.Produce(); foreach (int value in values) { System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); } System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { string s = "ran "; yield return 42; System.Console.Write(s); yield break; } } """; var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "string C.<Produce>d__0.<s>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C.<Produce>d__0.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_IntArrayLocal() { string source = """ using System.Linq; using System.Reflection; var values = C.Produce(); foreach (int value in values) { System.Console.Write(((int[])values.GetType().GetField("<values2>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)).Length); } System.Console.Write(((int[])values.GetType().GetField("<values2>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { int[] values2 = Enumerable.Range(0, 100).ToArray(); yield return 42; System.Console.Write($" {values2.Length} "); } } """; var comp = CreateCompilation(source); var verifier = CompileAndVerify(comp, expectedOutput: "100 100 True").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "int[] C.<Produce>d__0.<values2>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C.<Produce>d__0.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedStringLocal() { var src = """ using System.Reflection; var enumerable = C.M(true); var enumerator = enumerable.GetEnumerator(); try { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); System.Console.Write(((string)enumerator.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); assert(!enumerator.MoveNext()); System.Console.Write(((string)enumerator.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)) is null); } finally { enumerator.Dispose(); } void assert(bool b) { if (!b) throw new System.Exception(); } public class C { public static System.Collections.Generic.IEnumerable<int> M(bool b) { while (b) { string s = "value "; yield return 42; b = false; System.Console.Write(s); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "42 value value True").VerifyDiagnostics(); verifier.VerifyIL("C.<M>d__0.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "string C.<M>d__0.<s>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C.<M>d__0.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedStringLocal_YieldBreak() { var src = """ using System.Reflection; var enumerable = C.M(true); var enumerator = enumerable.GetEnumerator(); try { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); assert(!enumerator.MoveNext()); System.Console.Write(((string)enumerator.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); } finally { enumerator.Dispose(); } System.Console.Write(((string)enumerator.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)) is null); void assert(bool b) { if (!b) throw new System.Exception(); } public class C { public static System.Collections.Generic.IEnumerable<int> M(bool b) { while (b) { string s = "value "; yield return 42; System.Console.Write(s); yield break; } } } """; var verifier = CompileAndVerify(src, expectedOutput: "42 value value True").VerifyDiagnostics(); verifier.VerifyIL("C.<M>d__0.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "string C.<M>d__0.<s>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C.<M>d__0.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedStringLocal_ThrownException() { var src = """ using System.Reflection; var enumerable = C.M(true); var enumerator = enumerable.GetEnumerator(); try { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); } finally { System.Console.Write(((string)enumerable.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerable))); enumerator.Dispose(); } System.Console.Write(((string)enumerable.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerable)) is null); void assert(bool b) { if (!b) throw new System.Exception(); } public class C { public static System.Collections.Generic.IEnumerable<int> M(bool b) { while (b) { string s = "value "; yield return 42; System.Console.Write(s); throw new System.Exception(); } } } """; CompileAndVerify(src, expectedOutput: "42 value True").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_IntParameter() { string src = """ using System.Reflection; var values = C.Produce(42); foreach (int value in values) { } System.Console.Write(((int)values.GetType().GetField("s", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); System.Console.Write(((int)values.GetType().GetField("<>3__s", BindingFlags.Public | BindingFlags.Instance).GetValue(values))); class C { public static System.Collections.Generic.IEnumerable<int> Produce(int s) { yield return 0; System.Console.Write($"{s} "); } } """; var verifier = CompileAndVerify(src, expectedOutput: "42 4242").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<Produce>d__0.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_StringParameter() { string src = """ using System.Reflection; var values = C.Produce("value "); foreach (int value in values) { } System.Console.Write(((string)values.GetType().GetField("s", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); System.Console.Write(((string)values.GetType().GetField("<>3__s", BindingFlags.Public | BindingFlags.Instance).GetValue(values))); class C { public static System.Collections.Generic.IEnumerable<int> Produce(string s) { yield return 0; System.Console.Write(s); } } """; var verifier = CompileAndVerify(src, expectedOutput: "value value value").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<Produce>d__0.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_ClosureOverLocal() { string src = """ using System.Reflection; var values = C.Produce(); foreach (int value in values) { } var closure = values.GetType().GetField("<>8__1", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values); System.Console.Write((int)(closure.GetType().GetField("s", BindingFlags.Public | BindingFlags.Instance).GetValue(closure))); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { int s = 41; local(); yield return 0; System.Console.Write($"{s} "); void local() { s++; } } } """; var verifier = CompileAndVerify(src, expectedOutput: "42 42").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<Produce>d__0.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_ClosureOverThis() { string src = """ using System.Reflection; class C { int field = 41; public static void Main() { var values = new C().Produce(); foreach (int value in values) { } System.Console.Write(((C)values.GetType().GetField("<>4__this", BindingFlags.Public | BindingFlags.Instance).GetValue(values)).field); } private System.Collections.Generic.IEnumerable<int> Produce() { local(); yield return this.field; void local() { this.field++; } } } """; var verifier = CompileAndVerify(src, expectedOutput: "42").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__2.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<Produce>d__2.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedStringLocal_InTryFinally() { string src = """ using System.Reflection; var values = C.Produce(); foreach (int value in values) { System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); } System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { try { var s = "value "; yield return 0; System.Console.Write(s); } finally { System.Console.Write("ran "); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "value value ran True").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 42 (0x2a) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.0 IL_0001: ldfld "int C.<Produce>d__0.<>1__state" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s -3 IL_000a: beq.s IL_0010 IL_000c: ldloc.0 IL_000d: ldc.i4.1 IL_000e: bne.un.s IL_001a IL_0010: nop .try { IL_0011: leave.s IL_001a } finally { IL_0013: ldarg.0 IL_0014: call "void C.<Produce>d__0.<>m__Finally1()" IL_0019: endfinally } IL_001a: ldarg.0 IL_001b: ldnull IL_001c: stfld "string C.<Produce>d__0.<s>5__2" IL_0021: ldarg.0 IL_0022: ldc.i4.s -2 IL_0024: stfld "int C.<Produce>d__0.<>1__state" IL_0029: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedStringLocal_InTryFinally_WithThrow() { string src = """ using System.Reflection; var values = C.Produce(); try { foreach (int value in values) { System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); } } catch (System.Exception e) { System.Console.Write(e.Message); } System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { try { string s = "value "; yield return 0; System.Console.Write(s); } finally { throw new System.Exception("exception "); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "value value exception True").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 42 (0x2a) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.0 IL_0001: ldfld "int C.<Produce>d__0.<>1__state" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s -3 IL_000a: beq.s IL_0010 IL_000c: ldloc.0 IL_000d: ldc.i4.1 IL_000e: bne.un.s IL_001a IL_0010: nop .try { IL_0011: leave.s IL_001a } finally { IL_0013: ldarg.0 IL_0014: call "void C.<Produce>d__0.<>m__Finally1()" IL_0019: endfinally } IL_001a: ldarg.0 IL_001b: ldnull IL_001c: stfld "string C.<Produce>d__0.<s>5__2" IL_0021: ldarg.0 IL_0022: ldc.i4.s -2 IL_0024: stfld "int C.<Produce>d__0.<>1__state" IL_0029: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedStringLocal_InTryFinally_WithThrow_EarlyIterationExit() { string src = """ using System.Reflection; var values = C.Produce(); try { foreach (int value in values) { break; } // we interrupt the iteration early } catch (System.Exception e) { System.Console.Write(e.Message); } System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { try { string s = "value"; yield return 0; s.ToString(); throw null; } finally { throw new System.Exception("exception "); } } } """; // Note: nested hoisted local does not get cleared when an exception is thrown during disposal CompileAndVerify(src, expectedOutput: "exception value").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_StringLocal_WithThrownException() { string src = """ using System.Reflection; var values = C.Produce(true); try { foreach (int value in values) { } } catch (System.Exception e) { System.Console.Write(e.Message); } System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce(bool b) { string s = "value "; if (b) throw new System.Exception("exception "); yield return 0; System.Console.Write(s); } } """; CompileAndVerify(src, expectedOutput: "exception True").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedUnmanagedTypeParameterLocal() { var src = """ using System.Reflection; var enumerable = C.M(true, 42); var enumerator = enumerable.GetEnumerator(); try { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); assert(!enumerator.MoveNext()); System.Console.Write(" "); System.Console.Write(((int)enumerator.GetType().GetField("<local>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); } finally { enumerator.Dispose(); } void assert(bool b) { if (!b) throw new System.Exception(); } public class C { public static System.Collections.Generic.IEnumerable<int> M<T>(bool b, T t) where T : unmanaged { while (b) { T local = t; yield return 10; b = false; System.Console.Write(local); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "10 42 42").VerifyDiagnostics(); verifier.VerifyIL("C.<M>d__0<T>.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<M>d__0<T>.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedLocalWithStructFromAnotherCompilation() { var libSrc = """ public struct S { public int field; public override string ToString() => field.ToString(); } """; var libComp = CreateCompilation(libSrc); var src = """ using System.Reflection; var enumerable = C.M(true, new S { field = 42 }); var enumerator = enumerable.GetEnumerator(); try { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); assert(!enumerator.MoveNext()); System.Console.Write(" "); System.Console.Write(((S)enumerator.GetType().GetField("<local>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator))); } finally { enumerator.Dispose(); } void assert(bool b) { if (!b) throw new System.Exception(); } public class C { public static System.Collections.Generic.IEnumerable<int> M(bool b, S s) { while (b) { S local = s; yield return 10; b = false; System.Console.Write(local.ToString()); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "10 42 42", references: [libComp.EmitToImageReference()]).VerifyDiagnostics(); verifier.VerifyIL("C.<M>d__0.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<M>d__0.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedUnmanagedWithGenericsLocal() { var src = """ using System.Reflection; var enumerable = C.M(true, 42); var enumerator = enumerable.GetEnumerator(); try { assert(enumerator.MoveNext()); System.Console.Write($"{enumerator.Current} "); assert(!enumerator.MoveNext()); System.Console.Write(((S<int>)enumerator.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(enumerator)).field); } finally { enumerator.Dispose(); } void assert(bool b) { if (!b) throw new System.Exception(); } public struct S<T> where T : unmanaged // UnmanagedWithGenerics { public T field; } public class C { public static System.Collections.Generic.IEnumerable<int> M<T>(bool b, T t) where T : unmanaged { while (b) { S<T> s = new S<T> { field = t }; yield return 42; b = false; System.Console.Write(s.field); } } } """; CompileAndVerify(src, expectedOutput: "42 4242").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_StringLocal_EarlyIterationExit() { string src = """ using System.Reflection; var values = C.Produce(); foreach (int value in values) { System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); break; } System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { string s = "value "; yield return 0; s.ToString(); throw null; } } """; CompileAndVerify(src, expectedOutput: "value True").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NestedStringLocal_Reused() { string src = """ using System.Reflection; var values = C.Produce(); foreach (int value in values) { System.Console.Write(((string)values.GetType().GetField("<values2>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); } System.Console.Write(((string)values.GetType().GetField("<values2>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { { string values2 = "values2 "; yield return 42; System.Console.Write(values2); } { string values3 = "values3 "; yield return 43; System.Console.Write(values3); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "values2 values2 values3 values3 True").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "string C.<Produce>d__0.<values2>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C.<Produce>d__0.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_Parameters() { string src = """ string s = "ran "; var values = C.Produce(s); foreach (int value in values) { } foreach (int value in values) { } class C { public static System.Collections.Generic.IEnumerable<int> Produce(string s) { yield return 42; System.Console.Write(s); } } """; var verifier = CompileAndVerify(src, expectedOutput: "ran ran").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<Produce>d__0.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_NotCleanedTooSoon() { string src = """ using System.Reflection; var values = C.Produce(); foreach (int value in values) { System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); break; } System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { try { string s = "value "; try { yield return 42; } finally { System.Console.Write(s); } } finally { System.Console.Write("outer "); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "value value outer True").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 63 (0x3f) .maxstack 2 .locals init (int V_0) IL_0000: ldarg.0 IL_0001: ldfld "int C.<Produce>d__0.<>1__state" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s -4 IL_000a: sub IL_000b: ldc.i4.1 IL_000c: ble.un.s IL_0012 IL_000e: ldloc.0 IL_000f: ldc.i4.1 IL_0010: bne.un.s IL_002f IL_0012: nop .try { IL_0013: ldloc.0 IL_0014: ldc.i4.s -4 IL_0016: beq.s IL_001e IL_0018: ldloc.0 IL_0019: ldc.i4.1 IL_001a: beq.s IL_001e IL_001c: leave.s IL_002f IL_001e: nop .try { IL_001f: leave.s IL_002f } finally { IL_0021: ldarg.0 IL_0022: call "void C.<Produce>d__0.<>m__Finally2()" IL_0027: endfinally } } finally { IL_0028: ldarg.0 IL_0029: call "void C.<Produce>d__0.<>m__Finally1()" IL_002e: endfinally } IL_002f: ldarg.0 IL_0030: ldnull IL_0031: stfld "string C.<Produce>d__0.<s>5__2" IL_0036: ldarg.0 IL_0037: ldc.i4.s -2 IL_0039: stfld "int C.<Produce>d__0.<>1__state" IL_003e: ret } """); } [ConditionalFact(typeof(CoreClrOnly))] public void AddVariableCleanup_HoistedFromRefExpression() { var src = """ using System.Reflection; C c = new C(); var coll = Test(c); var enumerator = coll.GetEnumerator(); try { enumerator.MoveNext(); System.Console.Write(((C)coll.GetType().GetField("<>7__wrap2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(coll)) is null); } finally { enumerator.Dispose(); } System.Console.Write(((C)coll.GetType().GetField("<>7__wrap2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(coll)) is null); class C { public Buffer4<int> F = default; } partial class Program { static System.Collections.Generic.IEnumerable<int> Test(C x) { foreach (var y in x.F) { yield return -1; } } } [System.Runtime.CompilerServices.InlineArray(4)] public struct Buffer4<T> { private T _element0; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); var verifier = CompileAndVerify(comp, expectedOutput: "FalseTrue", verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.<Test>d__1.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "C Program.<Test>d__1.<>7__wrap2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int Program.<Test>d__1.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_StringLocal_IEnumerator() { string src = """ using System.Reflection; var values = C.Produce(); assert(values.MoveNext()); assert(values.Current == 42); assert(!values.MoveNext()); System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values))); values.Dispose(); System.Console.Write(((string)values.GetType().GetField("<s>5__2", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(values)) is null); static void assert(bool b) { if (!b) throw new System.Exception(); } class C { public static System.Collections.Generic.IEnumerator<int> Produce() { string s = "ran "; yield return 42; System.Console.Write(s); } } """; var verifier = CompileAndVerify(src, expectedOutput: "ran ran True").VerifyDiagnostics(); verifier.VerifyIL("C.<Produce>d__0.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "string C.<Produce>d__0.<s>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C.<Produce>d__0.<>1__state" IL_000f: ret } """); } [Fact] public void AddVariableCleanup_Unmanaged_UseSiteError() { var missingLibS1 = CreateCompilation(@" public struct S1 { public int i; } ", assemblyName: "libS1").ToMetadataReference(); var libS2 = CreateCompilation(@" public struct S2 { public S1 s1; } ", references: [missingLibS1], assemblyName: "libS2").ToMetadataReference(); var source = @" class C { System.Collections.Generic.IEnumerable<int> M1() { S2 s2 = M2(); yield return 42; System.Console.Write(s2); } S2 M2() => default; } "; var comp = CreateCompilation(source, references: [libS2]); comp.VerifyEmitDiagnostics( // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), // error CS0012: The type 'S1' is defined in an assembly that is not referenced. You must add a reference to assembly 'libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("S1", "libS1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1)); comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll, references: [libS2, missingLibS1]); comp.VerifyEmitDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75666")] public void AddVariableCleanup_StringLocal_MoveNextAfterDispose() { string src = """ var values = new C<string>([" one ", " two "]); var enumerator = values.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write("disposing "); enumerator.Dispose(); System.Console.Write("disposed "); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C<T> { private sealed class Node { internal readonly T _value; internal Node _next; internal Node(T value) { _value = value; _next = null; } } private Node _head; public C(System.Collections.Generic.IEnumerable<T> collection) { Node lastNode = null; foreach (T element in collection) { Node newNode = new Node(element); newNode._next = lastNode; lastNode = newNode; } _head = lastNode; } public System.Collections.Generic.IEnumerator<T> GetEnumerator() { return GetEnumerator(_head); } private static System.Collections.Generic.IEnumerator<T> GetEnumerator(Node head) { Node current = head; while (current != null) { yield return current._value; current = current._next; System.Console.Write("AFTER"); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "True two disposing disposed False two").VerifyDiagnostics(); verifier.VerifyIL("C<T>.<GetEnumerator>d__4.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "C<T>.Node C<T>.<GetEnumerator>d__4.<current>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C<T>.<GetEnumerator>d__4.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_YieldReturn() { // When a yield return statement is encountered ... The state of the enumerator object is changed to suspended. // If the state of the enumerator object is suspended, invoking Dispose: ... Changes the state to after. string src = """ var enumerator = C.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); enumerator.Dispose(); System.Console.Write("disposed "); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator() { yield return " one "; yield return " two "; } } """; var verifier = CompileAndVerify(src, expectedOutput: "True one disposed False one").VerifyDiagnostics(); verifier.VerifyIL("C.<GetEnumerator>d__0.System.IDisposable.Dispose()", """ { // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s -2 IL_0003: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_0008: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_YieldReturn_IEnumerable() { // When a yield return statement is encountered ... The state of the enumerator object is changed to suspended. // If the state of the enumerator object is suspended, invoking Dispose: ... Changes the state to after. string src = """ var enumerator = C.Produce().GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); enumerator.Dispose(); System.Console.Write("disposed "); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerable<string> Produce() { yield return " one "; yield return " two "; } } """; CompileAndVerify(src, expectedOutput: "True one disposed False one").VerifyDiagnostics(); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(); var enumerator = enumerable.GetEnumerator(); System.Console.Write(object.ReferenceEquals(enumerable, enumerator)); System.Console.Write(!object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); System.Console.Write(enumerator.MoveNext()); System.Console.Write(!object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); enumerator.Dispose(); System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); enumerator.Dispose(); enumerator.Dispose(); System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { yield return 42; yield return 43; } } """; CompileAndVerify(src2, expectedOutput: "TrueTrueTrueTrueTrueTrue").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_DisposeTwice() { // If the state of the enumerator object is after, invoking Dispose has no affect. string src = """ var enumerator = C.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); enumerator.Dispose(); System.Console.Write("disposed "); enumerator.Dispose(); System.Console.Write("disposed2 "); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator() { string local = ""; yield return " one "; local.ToString(); } } """; // Note: we actually set the state to "after"/"finished" and cleanup hoisted locals again var verifier = CompileAndVerify(src, expectedOutput: "True one disposed disposed2 False one").VerifyDiagnostics(); verifier.VerifyIL("C.<GetEnumerator>d__0.System.IDisposable.Dispose()", """ { // Code size 16 (0x10) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stfld "string C.<GetEnumerator>d__0.<local>5__2" IL_0007: ldarg.0 IL_0008: ldc.i4.s -2 IL_000a: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_000f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_YieldBreak() { // When a yield break statement is encountered ... The state of the enumerator object is changed to after. string src = """ var enumerator = C.GetEnumerator(true); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator(bool b) { yield return " one "; if (b) yield break; yield return " two "; } } """; CompileAndVerify(src, expectedOutput: "True one False one False one").VerifyDiagnostics(); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(true); var enumerator = enumerable.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(!object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); System.Console.Write(!enumerator.MoveNext()); System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); class C { public static System.Collections.Generic.IEnumerable<int> Produce(bool b) { yield return 42; if (b) yield break; yield return 43; } } """; // We're not setting the state to "after"/"finished" // Tracked by https://github.com/dotnet/roslyn/issues/76089 CompileAndVerify(src2, expectedOutput: "TrueTrueTrueFalse").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_EndOfBody() { // When the end of the iterator body is encountered ... The state of the enumerator object is changed to after. string src = """ var enumerator = C.GetEnumerator(true); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator(bool b) { yield return " one "; System.Console.Write("done "); } } """; CompileAndVerify(src, expectedOutput: "True one done False one False one").VerifyDiagnostics(); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(true); var enumerator = enumerable.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(!object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); System.Console.Write(!enumerator.MoveNext()); System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); class C { public static System.Collections.Generic.IEnumerable<int> Produce(bool b) { yield return 42; } } """; // We're not setting the state to "after"/"finished" // Tracked by https://github.com/dotnet/roslyn/issues/76089 CompileAndVerify(src2, expectedOutput: "TrueTrueTrueFalse").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_ThrowException() { // When an exception is thrown and propagated out of the iterator block ... The state of the enumerator object is changed to after. string src = """ var enumerator = C.GetEnumerator(true); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); try { System.Console.Write(enumerator.MoveNext()); } catch (System.Exception e) { System.Console.Write(e.Message); } System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator(bool b) { yield return " one "; throw new System.Exception("exception"); } } """; CompileAndVerify(src, expectedOutput: "True one exception one False one").VerifyDiagnostics(); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(); var enumerator = enumerable.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(!object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); try { _ = enumerator.MoveNext(); } catch (System.Exception) { System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); } enumerator.Dispose(); System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { yield return 42; throw new System.Exception("exception"); } } """; // We're not setting the state to "after"/"finished" // Tracked by https://github.com/dotnet/roslyn/issues/76089 CompileAndVerify(src2, expectedOutput: "TrueTrueFalseTrue").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_YieldReturn_InTryFinally() { // When a yield return statement is encountered ... The state of the enumerator object is changed to suspended. // If the state of the enumerator object is suspended, invoking Dispose: // ... // Executes any finally blocks as if the last executed yield return statement were a yield break statement. // If this causes an exception to be thrown and propagated out of the iterator body, // the state of the enumerator object is set to after and the exception is propagated to the caller of the Dispose method. string src = """ var enumerator = C.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write("disposing "); try { enumerator.Dispose(); } catch (System.Exception e) { System.Console.Write(e.Message); } System.Console.Write(" disposed "); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator() { try { yield return " one "; } finally { throw new System.Exception("exception"); } } } """; var verifier = CompileAndVerify(src, expectedOutput: "True one disposing exception disposed False one").VerifyDiagnostics(); verifier.VerifyIL("C.<GetEnumerator>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.<GetEnumerator>d__0.<>1__state" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s -3 IL_000a: beq.s IL_0010 IL_000c: ldloc.0 IL_000d: ldc.i4.1 IL_000e: bne.un.s IL_001a IL_0010: nop .try { IL_0011: leave.s IL_001a } finally { IL_0013: ldarg.0 IL_0014: call "void C.<GetEnumerator>d__0.<>m__Finally1()" IL_0019: endfinally } IL_001a: ldarg.0 IL_001b: ldc.i4.s -2 IL_001d: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_0022: ret } """); verifier.VerifyIL("C.<GetEnumerator>d__0.<>m__Finally1()", """ { // Code size 18 (0x12) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.m1 IL_0002: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_0007: ldstr "exception" IL_000c: newobj "System.Exception..ctor(string)" IL_0011: throw } """); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(); var enumerator = enumerable.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(!object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); try { enumerator.Dispose(); } catch (System.Exception) { System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); } class C { public static System.Collections.Generic.IEnumerable<int> Produce() { try { yield return 42; } finally { throw new System.Exception("exception"); } } } """; // We're not setting the state to "after"/"finished" // Tracked by https://github.com/dotnet/roslyn/issues/76089 CompileAndVerify(src2, expectedOutput: "TrueTrueFalse").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_YieldBreak_InTryFinally() { // When a yield break statement is encountered: // If the yield break statement is within one or more try blocks, the associated finally blocks are executed. // The state of the enumerator object is changed to after. string src = """ var enumerator = C.GetEnumerator(true); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator(bool b) { yield return " one "; try { if (b) yield break; } finally { System.Console.Write("finally "); } yield return " two "; } } """; CompileAndVerify(src, expectedOutput: "True one finally False one False one").VerifyDiagnostics(); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(true); var enumerator = enumerable.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(!object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); System.Console.Write(!enumerator.MoveNext()); System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); class C { public static System.Collections.Generic.IEnumerable<int> Produce(bool b) { yield return 42; try { if (b) yield break; } finally { System.Console.Write(" finally "); } yield return 43; } } """; // We're not setting the state to "after"/"finished" // Tracked by https://github.com/dotnet/roslyn/issues/76089 CompileAndVerify(src2, expectedOutput: "TrueTrue finally TrueFalse").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_ThrowException_InTryFinally() { // When an exception is thrown and propagated out of the iterator block: ... The state of the enumerator object is changed to after. string src = """ var enumerator = C.GetEnumerator(true); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); try { System.Console.Write(enumerator.MoveNext()); } catch (System.Exception e) { System.Console.Write(e.Message); } System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); enumerator.Dispose(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator(bool b) { yield return " one "; try { throw new System.Exception("exception"); } finally { System.Console.Write("finally "); } } } """; CompileAndVerify(src, expectedOutput: "True one finally exception one False one False one").VerifyDiagnostics(); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(); var enumerator = enumerable.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); try { _ = enumerator.MoveNext(); } catch (System.Exception) { System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); } enumerator.Dispose(); System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { yield return 42; try { throw new System.Exception("exception"); } finally { System.Console.Write(" finally "); } } } """; // We're not setting the state to "after"/"finished" // Tracked by https://github.com/dotnet/roslyn/issues/76089 CompileAndVerify(src2, expectedOutput: "True finally FalseTrue").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_ThrowException_InTryFinally_WithYieldInTry() { // When an exception is thrown and propagated out of the iterator block: ... The state of the enumerator object is changed to after. string src = """ var enumerator = C.GetEnumerator(true); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); try { System.Console.Write(enumerator.MoveNext()); } catch (System.Exception e) { System.Console.Write(e.Message); } System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator(bool b) { try { yield return " one "; if (b) throw new System.Exception("exception"); } finally { System.Console.Write("finally "); } } } """; // Note: we generate a `fault { Dispose(); }`, but only if there is a `yield` in a `try`, which is surprising var verifier = CompileAndVerify(src, expectedOutput: "True one finally exception one False one").VerifyDiagnostics(); verifier.VerifyIL("C.<GetEnumerator>d__0.System.Collections.IEnumerator.MoveNext()", """ { // Code size 101 (0x65) .maxstack 2 .locals init (bool V_0, int V_1) .try { IL_0000: ldarg.0 IL_0001: ldfld "int C.<GetEnumerator>d__0.<>1__state" IL_0006: stloc.1 IL_0007: ldloc.1 IL_0008: brfalse.s IL_0012 IL_000a: ldloc.1 IL_000b: ldc.i4.1 IL_000c: beq.s IL_0037 IL_000e: ldc.i4.0 IL_000f: stloc.0 IL_0010: leave.s IL_0063 IL_0012: ldarg.0 IL_0013: ldc.i4.m1 IL_0014: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_0019: ldarg.0 IL_001a: ldc.i4.s -3 IL_001c: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_0021: ldarg.0 IL_0022: ldstr " one " IL_0027: stfld "string C.<GetEnumerator>d__0.<>2__current" IL_002c: ldarg.0 IL_002d: ldc.i4.1 IL_002e: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_0033: ldc.i4.1 IL_0034: stloc.0 IL_0035: leave.s IL_0063 IL_0037: ldarg.0 IL_0038: ldc.i4.s -3 IL_003a: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_003f: ldarg.0 IL_0040: ldfld "bool C.<GetEnumerator>d__0.b" IL_0045: brfalse.s IL_0052 IL_0047: ldstr "exception" IL_004c: newobj "System.Exception..ctor(string)" IL_0051: throw IL_0052: ldarg.0 IL_0053: call "void C.<GetEnumerator>d__0.<>m__Finally1()" IL_0058: ldc.i4.0 IL_0059: stloc.0 IL_005a: leave.s IL_0063 } fault { IL_005c: ldarg.0 IL_005d: call "void C.<GetEnumerator>d__0.Dispose()" IL_0062: endfinally } IL_0063: ldloc.0 IL_0064: ret } """); verifier.VerifyIL("C.<GetEnumerator>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.<GetEnumerator>d__0.<>1__state" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.s -3 IL_000a: beq.s IL_0010 IL_000c: ldloc.0 IL_000d: ldc.i4.1 IL_000e: bne.un.s IL_001a IL_0010: nop .try { IL_0011: leave.s IL_001a } finally { IL_0013: ldarg.0 IL_0014: call "void C.<GetEnumerator>d__0.<>m__Finally1()" IL_0019: endfinally } IL_001a: ldarg.0 IL_001b: ldc.i4.s -2 IL_001d: stfld "int C.<GetEnumerator>d__0.<>1__state" IL_0022: ret } """); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(); var enumerator = enumerable.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); try { _ = enumerator.MoveNext(); } catch (System.Exception) { System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); } class C { public static System.Collections.Generic.IEnumerable<int> Produce() { try { yield return 42; throw new System.Exception("exception"); } finally { System.Console.Write(" finally "); } } } """; CompileAndVerify(src2, expectedOutput: "True finally True").VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76078")] public void StateAfterMoveNext_YieldReturn_AfterTryFinally() { string src = """ var enumerator = C.GetEnumerator(true); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); enumerator.Dispose(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.Current); class C { public static System.Collections.Generic.IEnumerator<string> GetEnumerator(bool b) { try { yield return " one "; } finally { System.Console.Write("finally "); } yield return " two "; System.Console.Write("not executed after disposal"); } } """; CompileAndVerify(src, expectedOutput: "True one finally True two False two").VerifyDiagnostics(); // Verify GetEnumerator string src2 = """ var enumerable = C.Produce(); var enumerator = enumerable.GetEnumerator(); System.Console.Write(enumerator.MoveNext()); System.Console.Write(enumerator.MoveNext()); enumerator.Dispose(); System.Console.Write(object.ReferenceEquals(enumerable, enumerable.GetEnumerator())); class C { public static System.Collections.Generic.IEnumerable<int> Produce() { try { yield return 42; } finally { System.Console.Write(" finally "); } yield return 43; System.Console.Write("not executed after disposal"); } } """; CompileAndVerify(src2, expectedOutput: "True finally TrueTrue").VerifyDiagnostics(); } } }