|
// 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();
}
}
}
|