File: CodeGen\ForeachTest.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Emit\Microsoft.CodeAnalysis.CSharp.Emit.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Emit.UnitTests)
// 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 System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
    public class ForeachTest : EmitMetadataTestBase
    {
        // The loop object must be an array or an object collection
        [Fact]
        public void SimpleLoop()
        {
            var text =
@"
using System;
public class Test
{
    static void Main(string[] args)
    {
        string[] arr = new string[4]; // Initialize
        arr[0] = ""one"";               // Element 1
        arr[1] = ""two"";               // Element 2
        arr[2] = ""three"";             // Element 3
        arr[3] = ""four"";              // Element 4
        foreach (string s in arr)
        {
            System.Console.WriteLine(s);
        }
    }
}
";
            string expectedOutput = @"one
two
three
four";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestIteration()
        {
            CompileAndVerify(@"
using System;
public class Test
{
    public static void Main(string[] args)
    {
        unsafe
        {
            int* y = null;
            foreach (var x in new int*[] { y }) { }
        }
    }
}", options: TestOptions.UnsafeReleaseDll, verify: Verification.Fails).VerifyIL("Test.Main", @"
{
  // Code size       33 (0x21)
  .maxstack  4
  .locals init (int* V_0, //y
  int*[] V_1,
  int V_2)
  IL_0000:  ldc.i4.0
  IL_0001:  conv.u
  IL_0002:  stloc.0
  IL_0003:  ldc.i4.1
  IL_0004:  newarr     ""int*""
  IL_0009:  dup
  IL_000a:  ldc.i4.0
  IL_000b:  ldloc.0
  IL_000c:  stelem.i
  IL_000d:  stloc.1
  IL_000e:  ldc.i4.0
  IL_000f:  stloc.2
  IL_0010:  br.s       IL_001a
  IL_0012:  ldloc.1
  IL_0013:  ldloc.2
  IL_0014:  ldelem.i
  IL_0015:  pop
  IL_0016:  ldloc.2
  IL_0017:  ldc.i4.1
  IL_0018:  add
  IL_0019:  stloc.2
  IL_001a:  ldloc.2
  IL_001b:  ldloc.1
  IL_001c:  ldlen
  IL_001d:  conv.i4
  IL_001e:  blt.s      IL_0012
  IL_0020:  ret
}");
        }
 
        // Using the Linq as iteration variable
        [Fact]
        public void TestLinqInForeach()
        {
            var text =
@"using System;
using System.Linq;
public class Test
{
    public static void Main(string[] args)
    {
        foreach (int x in from char c in ""abc"" select c)
        {
            Console.WriteLine(x);
        }
    }
}";
            string expectedOutput = @"97
98
99";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        // Empty foreach statement
        [Fact]
        public void TestEmptyStatementForeach()
        {
            var text =
@"class C
{
    static void Main()
    {
        foreach (char C in ""abc"");
    }
}";
            string expectedIL = @"{
  // Code size       32 (0x20)
  .maxstack  2
  .locals init (string V_0,
  int V_1)
  IL_0000:  ldstr      ""abc""
  IL_0005:  stloc.0
  IL_0006:  ldc.i4.0
  IL_0007:  stloc.1
  IL_0008:  br.s       IL_0016
  IL_000a:  ldloc.0
  IL_000b:  ldloc.1
  IL_000c:  callvirt   ""char string.this[int].get""
  IL_0011:  pop
  IL_0012:  ldloc.1
  IL_0013:  ldc.i4.1
  IL_0014:  add
  IL_0015:  stloc.1
  IL_0016:  ldloc.1
  IL_0017:  ldloc.0
  IL_0018:  callvirt   ""int string.Length.get""
  IL_001d:  blt.s      IL_000a
  IL_001f:  ret
}";
            CompileAndVerify(text).VerifyIL("C.Main", expectedIL);
        }
 
        // Foreach value can't be deleted in a loop
        [Fact]
        public void TestRemoveValueInForeach()
        {
            var text =
@"using System.Collections;
using System.Collections.Generic;
 
class C
{
    static public void Main()
    {
        List<int> arrInt = new List<int>();
        arrInt.Add(1);
        foreach (int i in arrInt)
        {
            arrInt.Remove(i);//It will generate error in run-time
        }
    }
}
";
            string expectedIL = @"{
  // Code size       64 (0x40)
  .maxstack  2
  .locals init (System.Collections.Generic.List<int> V_0, //arrInt
           System.Collections.Generic.List<int>.Enumerator V_1,
           int V_2) //i
  IL_0000:  newobj     ""System.Collections.Generic.List<int>..ctor()""
  IL_0005:  stloc.0   
  IL_0006:  ldloc.0   
  IL_0007:  ldc.i4.1  
  IL_0008:  callvirt   ""void System.Collections.Generic.List<int>.Add(int)""
  IL_000d:  ldloc.0   
  IL_000e:  callvirt   ""System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()""
  IL_0013:  stloc.1   
  .try
  {
    IL_0014:  br.s       IL_0026
    IL_0016:  ldloca.s   V_1
    IL_0018:  call       ""int System.Collections.Generic.List<int>.Enumerator.Current.get""
    IL_001d:  stloc.2   
    IL_001e:  ldloc.0   
    IL_001f:  ldloc.2   
    IL_0020:  callvirt   ""bool System.Collections.Generic.List<int>.Remove(int)""
    IL_0025:  pop       
    IL_0026:  ldloca.s   V_1
    IL_0028:  call       ""bool System.Collections.Generic.List<int>.Enumerator.MoveNext()""
    IL_002d:  brtrue.s   IL_0016
    IL_002f:  leave.s    IL_003f
  }
  finally
  {
    IL_0031:  ldloca.s   V_1
    IL_0033:  constrained. ""System.Collections.Generic.List<int>.Enumerator""
    IL_0039:  callvirt   ""void System.IDisposable.Dispose()""
    IL_003e:  endfinally
  }
  IL_003f:  ret       
}";
            CompileAndVerify(text).VerifyIL("C.Main", expectedIL);
        }
 
        // With multidimensional arrays, you can use one loop to iterate through the elements
        [Fact]
        public void TestMultiDimensionalArray()
        {
            var text =
@"class T
{
    static public void Main()
    {
        int[,] numbers2D = new int[3, 2] { { 9, 99 }, { 3, 33 }, { 5, 55 } };
        foreach (int i in numbers2D)
        {
            System.Console.WriteLine(i);
        }
    }
}
";
            string expectedOutput = @"9
99
3
33
5
55";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [WorkItem(540917, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540917")]
        [Fact]
        public void TestArray()
        {
            var text =
@"using System;
public class Test
{
    static void Main(string[] args)
    {
        string[] arr = new string[4]; // Initialize
        arr[0] = ""one"";               // Element 1
        arr[1] = ""two"";               // Element 2
        foreach (string s in arr)
        {
            System.Console.WriteLine(s);
        }
    }
}
";
            string expectedIL = @"
{
  // Code size       46 (0x2e)
  .maxstack  4
  .locals init (string[] V_0,
  int V_1)
  IL_0000:  ldc.i4.4
  IL_0001:  newarr     ""string""
  IL_0006:  dup
  IL_0007:  ldc.i4.0
  IL_0008:  ldstr      ""one""
  IL_000d:  stelem.ref
  IL_000e:  dup
  IL_000f:  ldc.i4.1
  IL_0010:  ldstr      ""two""
  IL_0015:  stelem.ref
  IL_0016:  stloc.0
  IL_0017:  ldc.i4.0
  IL_0018:  stloc.1
  IL_0019:  br.s       IL_0027
  IL_001b:  ldloc.0
  IL_001c:  ldloc.1
  IL_001d:  ldelem.ref
  IL_001e:  call       ""void System.Console.WriteLine(string)""
  IL_0023:  ldloc.1
  IL_0024:  ldc.i4.1
  IL_0025:  add
  IL_0026:  stloc.1
  IL_0027:  ldloc.1
  IL_0028:  ldloc.0
  IL_0029:  ldlen
  IL_002a:  conv.i4
  IL_002b:  blt.s      IL_001b
  IL_002d:  ret
}";
            CompileAndVerify(text).VerifyIL("Test.Main", expectedIL);
        }
 
        [Fact]
        public void TestSpan()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
 
class Test
{
    public static void Main()
    {       
        var sp = new Span<int>(new[] {1, 2, 3});
        foreach(var i in sp)
        {
            Console.Write(i);
        }
    }
}
 
", TestOptions.ReleaseExe);
 
            CompileAndVerify(comp, expectedOutput: "123").VerifyIL("Test.Main", @"
{
  // Code size       56 (0x38)
  .maxstack  3
  .locals init (System.Span<int> V_0,
                int V_1)
  IL_0000:  ldc.i4.3
  IL_0001:  newarr     ""int""
  IL_0006:  dup
  IL_0007:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_000c:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0011:  newobj     ""System.Span<int>..ctor(int[])""
  IL_0016:  stloc.0
  IL_0017:  ldc.i4.0
  IL_0018:  stloc.1
  IL_0019:  br.s       IL_002d
  IL_001b:  ldloca.s   V_0
  IL_001d:  ldloc.1
  IL_001e:  call       ""ref int System.Span<int>.this[int].get""
  IL_0023:  ldind.i4
  IL_0024:  call       ""void System.Console.Write(int)""
  IL_0029:  ldloc.1
  IL_002a:  ldc.i4.1
  IL_002b:  add
  IL_002c:  stloc.1
  IL_002d:  ldloc.1
  IL_002e:  ldloca.s   V_0
  IL_0030:  call       ""int System.Span<int>.Length.get""
  IL_0035:  blt.s      IL_001b
  IL_0037:  ret
}");
        }
 
        [Fact]
        public void TestSpanSideeffectingLoopBody()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
 
class Test
{
    public static void Main()
    {       
        var sp = new Span<int>(new[] {1, 2, 3});
        foreach(var i in sp)
        {
            Console.Write(i);
            sp = default;
        }
 
        Console.Write(sp.Length);
    }
}
 
", TestOptions.ReleaseExe);
 
            CompileAndVerify(comp, expectedOutput: "1230").VerifyIL("Test.Main", @"
{
  // Code size       79 (0x4f)
  .maxstack  4
  .locals init (System.Span<int> V_0, //sp
                System.Span<int> V_1,
                int V_2)
  IL_0000:  ldloca.s   V_0
  IL_0002:  ldc.i4.3
  IL_0003:  newarr     ""int""
  IL_0008:  dup
  IL_0009:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_000e:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0013:  call       ""System.Span<int>..ctor(int[])""
  IL_0018:  ldloc.0
  IL_0019:  stloc.1
  IL_001a:  ldc.i4.0
  IL_001b:  stloc.2
  IL_001c:  br.s       IL_0038
  IL_001e:  ldloca.s   V_1
  IL_0020:  ldloc.2
  IL_0021:  call       ""ref int System.Span<int>.this[int].get""
  IL_0026:  ldind.i4
  IL_0027:  call       ""void System.Console.Write(int)""
  IL_002c:  ldloca.s   V_0
  IL_002e:  initobj    ""System.Span<int>""
  IL_0034:  ldloc.2
  IL_0035:  ldc.i4.1
  IL_0036:  add
  IL_0037:  stloc.2
  IL_0038:  ldloc.2
  IL_0039:  ldloca.s   V_1
  IL_003b:  call       ""int System.Span<int>.Length.get""
  IL_0040:  blt.s      IL_001e
  IL_0042:  ldloca.s   V_0
  IL_0044:  call       ""int System.Span<int>.Length.get""
  IL_0049:  call       ""void System.Console.Write(int)""
  IL_004e:  ret
}");
        }
 
        [Fact]
        public void TestReadOnlySpan()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
 
class Test
{
    public static void Main()
    {       
        var sp = new ReadOnlySpan<Color>(new [] {Color.Red, Color.Green, Color.Blue});
        foreach(var i in sp)
        {
            Console.Write(i);
        }
    }
}
 
", TestOptions.ReleaseExe);
 
            //NOTE: the verification error is expected. Wrapping of literals into readonly spans uses unsafe Span.ctor.
            CompileAndVerify(comp, expectedOutput: "RedGreenBlue", verify: Verification.Fails).VerifyIL("Test.Main", @"
{
  // Code size       50 (0x32)
  .maxstack  2
  .locals init (System.ReadOnlySpan<System.Color> V_0,
                int V_1)
  IL_0000:  ldsflda    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.AE4B3280E56E2FAF83F414A6E3DABE9D5FBE18976544C05FED121ACCB85B53FC""
  IL_0005:  ldc.i4.3
  IL_0006:  newobj     ""System.ReadOnlySpan<System.Color>..ctor(void*, int)""
  IL_000b:  stloc.0
  IL_000c:  ldc.i4.0
  IL_000d:  stloc.1
  IL_000e:  br.s       IL_0027
  IL_0010:  ldloca.s   V_0
  IL_0012:  ldloc.1
  IL_0013:  call       ""ref readonly System.Color System.ReadOnlySpan<System.Color>.this[int].get""
  IL_0018:  ldind.i1
  IL_0019:  box        ""System.Color""
  IL_001e:  call       ""void System.Console.Write(object)""
  IL_0023:  ldloc.1
  IL_0024:  ldc.i4.1
  IL_0025:  add
  IL_0026:  stloc.1
  IL_0027:  ldloc.1
  IL_0028:  ldloca.s   V_0
  IL_002a:  call       ""int System.ReadOnlySpan<System.Color>.Length.get""
  IL_002f:  blt.s      IL_0010
  IL_0031:  ret
}");
        }
 
        [ConditionalFact(typeof(CoreClrOnly))]
        public void TestReadOnlySpanString()
        {
            var comp = CreateCompilation(@"
using System;
 
class Test
{
    public static void Main()
    {       
        var sp = (ReadOnlySpan<char>)""hello"";
        foreach(var i in sp)
        {
            Console.Write(i);
        }
    }
}
 
", targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe);
 
            CompileAndVerify(comp, expectedOutput: "hello", verify: Verification.Passes).VerifyIL("Test.Main", @"
{
  // Code size       44 (0x2c)
  .maxstack  2
  .locals init (System.ReadOnlySpan<char> V_0,
                int V_1)
  IL_0000:  ldstr      ""hello""
  IL_0005:  call       ""System.ReadOnlySpan<char> System.MemoryExtensions.AsSpan(string)""
  IL_000a:  stloc.0
  IL_000b:  ldc.i4.0
  IL_000c:  stloc.1
  IL_000d:  br.s       IL_0021
  IL_000f:  ldloca.s   V_0
  IL_0011:  ldloc.1
  IL_0012:  call       ""ref readonly char System.ReadOnlySpan<char>.this[int].get""
  IL_0017:  ldind.u2
  IL_0018:  call       ""void System.Console.Write(char)""
  IL_001d:  ldloc.1
  IL_001e:  ldc.i4.1
  IL_001f:  add
  IL_0020:  stloc.1
  IL_0021:  ldloc.1
  IL_0022:  ldloca.s   V_0
  IL_0024:  call       ""int System.ReadOnlySpan<char>.Length.get""
  IL_0029:  blt.s      IL_000f
  IL_002b:  ret
}");
        }
 
        [Fact]
        public void TestReadOnlySpan2()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
 
class Test
{
    public static void Main()
    {       
        foreach(var i in (ReadOnlySpan<byte>)new byte[] {1, 2, 3})
        {
            Console.Write(i);
        }
    }
}
 
", TestOptions.ReleaseExe);
 
            CompileAndVerify(comp, expectedOutput: "123", verify: Verification.Fails).VerifyIL("Test.Main", @"
{
  // Code size       45 (0x2d)
  .maxstack  2
  .locals init (System.ReadOnlySpan<byte> V_0,
                int V_1)
  IL_0000:  ldsflda    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.039058C6F2C0CB492C533B0A4D14EF77CC0F78ABCCCED5287D84A1A2011CFB81""
  IL_0005:  ldc.i4.3
  IL_0006:  newobj     ""System.ReadOnlySpan<byte>..ctor(void*, int)""
  IL_000b:  stloc.0
  IL_000c:  ldc.i4.0
  IL_000d:  stloc.1
  IL_000e:  br.s       IL_0022
  IL_0010:  ldloca.s   V_0
  IL_0012:  ldloc.1
  IL_0013:  call       ""ref readonly byte System.ReadOnlySpan<byte>.this[int].get""
  IL_0018:  ldind.u1
  IL_0019:  call       ""void System.Console.Write(int)""
  IL_001e:  ldloc.1
  IL_001f:  ldc.i4.1
  IL_0020:  add
  IL_0021:  stloc.1
  IL_0022:  ldloc.1
  IL_0023:  ldloca.s   V_0
  IL_0025:  call       ""int System.ReadOnlySpan<byte>.Length.get""
  IL_002a:  blt.s      IL_0010
  IL_002c:  ret
}");
        }
 
        [Fact]
        public void TestSpanNoIndexer()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
 
class Test
{
    public static void Main()
    {       
        var sp = new Span<int>(new[] {1, 2, 3});
        foreach(var i in sp)
        {
            Console.Write(i);
        }
    }
}
", TestOptions.ReleaseExe);
 
            comp.MakeMemberMissing(WellKnownMember.System_Span_T__get_Item);
 
            CompileAndVerify(comp, expectedOutput: "123").VerifyIL("Test.Main", @"
{
  // Code size       57 (0x39)
  .maxstack  4
  .locals init (System.Span<int> V_0, //sp
                System.Span<int>.Enumerator V_1)
  IL_0000:  ldloca.s   V_0
  IL_0002:  ldc.i4.3
  IL_0003:  newarr     ""int""
  IL_0008:  dup
  IL_0009:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_000e:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0013:  call       ""System.Span<int>..ctor(int[])""
  IL_0018:  ldloca.s   V_0
  IL_001a:  call       ""System.Span<int>.Enumerator System.Span<int>.GetEnumerator()""
  IL_001f:  stloc.1
  IL_0020:  br.s       IL_002f
  IL_0022:  ldloca.s   V_1
  IL_0024:  call       ""ref int System.Span<int>.Enumerator.Current.get""
  IL_0029:  ldind.i4
  IL_002a:  call       ""void System.Console.Write(int)""
  IL_002f:  ldloca.s   V_1
  IL_0031:  call       ""bool System.Span<int>.Enumerator.MoveNext()""
  IL_0036:  brtrue.s   IL_0022
  IL_0038:  ret
}");
        }
 
        [Fact]
        public void TestSpanValIndexer()
        {
            var comp = CreateEmptyCompilation(@"
using System;
 
class Test
{
    public static void Main()
    {       
        var sp = new ReadOnlySpan<int>(new[] {1, 2, 3});
        foreach(var i in sp)
        {
            Console.Write(i);
        }
    }
}
 
 
namespace System
{
    public readonly ref struct ReadOnlySpan<T>
    {
        private readonly T[] arr;
 
        public T this[int i] => arr[i];
        public int Length { get; }
 
        public ReadOnlySpan(T[] arr)
        {
            this.arr = arr;
            this.Length = arr.Length;
        }
 
        public Enumerator GetEnumerator() => new Enumerator(this);
 
        public ref struct Enumerator
        {
            private readonly ReadOnlySpan<T> _span;
            private int _index;
 
            internal Enumerator(ReadOnlySpan<T> span)
            {
                _span = span;
                _index = -1;
            }
 
            public bool MoveNext()
            {
                int index = _index + 1;
                if (index < _span.Length)
                {
                    _index = index;
                    return true;
                }
 
                return false;
            }
 
            public T Current
            {
                get => _span[_index];
            }
        }
    }
}
 
", references: new[] { MscorlibRef_v4_0_30316_17626 }, TestOptions.ReleaseExe);
 
            CompileAndVerify(comp, expectedOutput: "123", verify: Verification.Fails).VerifyIL("Test.Main", @"
{
  // Code size       56 (0x38)
  .maxstack  4
  .locals init (System.ReadOnlySpan<int> V_0, //sp
                System.ReadOnlySpan<int>.Enumerator V_1)
  IL_0000:  ldloca.s   V_0
  IL_0002:  ldc.i4.3
  IL_0003:  newarr     ""int""
  IL_0008:  dup
  IL_0009:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_000e:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0013:  call       ""System.ReadOnlySpan<int>..ctor(int[])""
  IL_0018:  ldloca.s   V_0
  IL_001a:  call       ""System.ReadOnlySpan<int>.Enumerator System.ReadOnlySpan<int>.GetEnumerator()""
  IL_001f:  stloc.1
  IL_0020:  br.s       IL_002e
  IL_0022:  ldloca.s   V_1
  IL_0024:  call       ""int System.ReadOnlySpan<int>.Enumerator.Current.get""
  IL_0029:  call       ""void System.Console.Write(int)""
  IL_002e:  ldloca.s   V_1
  IL_0030:  call       ""bool System.ReadOnlySpan<int>.Enumerator.MoveNext()""
  IL_0035:  brtrue.s   IL_0022
  IL_0037:  ret
}");
        }
 
        [Fact]
        public void TestSpanConvert()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
 
class Test
{
    public static void Main()
    {       
        var sp = new Span<int>(new[] {1, 2, 3});
        foreach(byte i in sp)
        {
            Console.Write(i);
        }
    }
}
 
", TestOptions.ReleaseExe);
 
            CompileAndVerify(comp, expectedOutput: "123").VerifyIL("Test.Main", @"
{
  // Code size       57 (0x39)
  .maxstack  3
  .locals init (System.Span<int> V_0,
                int V_1)
  IL_0000:  ldc.i4.3
  IL_0001:  newarr     ""int""
  IL_0006:  dup
  IL_0007:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_000c:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0011:  newobj     ""System.Span<int>..ctor(int[])""
  IL_0016:  stloc.0
  IL_0017:  ldc.i4.0
  IL_0018:  stloc.1
  IL_0019:  br.s       IL_002e
  IL_001b:  ldloca.s   V_0
  IL_001d:  ldloc.1
  IL_001e:  call       ""ref int System.Span<int>.this[int].get""
  IL_0023:  ldind.i4
  IL_0024:  conv.u1
  IL_0025:  call       ""void System.Console.Write(int)""
  IL_002a:  ldloc.1
  IL_002b:  ldc.i4.1
  IL_002c:  add
  IL_002d:  stloc.1
  IL_002e:  ldloc.1
  IL_002f:  ldloca.s   V_0
  IL_0031:  call       ""int System.Span<int>.Length.get""
  IL_0036:  blt.s      IL_001b
  IL_0038:  ret
}");
        }
 
        [Fact]
        public void TestSpanDeconstruct()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
 
class Test
{
        static void Main(string[] args)
        {
            var sp = new Span<(int, int)>(new[] {(1, 2), (3, 4)});
            foreach(var (i, j) in sp)
            {
                Console.Write(i);
                Console.Write(j);
            }
        }
}
 
", TestOptions.ReleaseExe);
 
            comp = comp.WithReferences(comp.References.Concat(new[] { SystemRuntimeFacadeRef, ValueTupleRef }));
 
            CompileAndVerify(comp, expectedOutput: "1234").VerifyIL("Test.Main", @"
{
  // Code size       95 (0x5f)
  .maxstack  5
  .locals init (System.Span<System.ValueTuple<int, int>> V_0,
                int V_1,
                int V_2) //i
  IL_0000:  ldc.i4.2
  IL_0001:  newarr     ""System.ValueTuple<int, int>""
  IL_0006:  dup
  IL_0007:  ldc.i4.0
  IL_0008:  ldc.i4.1
  IL_0009:  ldc.i4.2
  IL_000a:  newobj     ""System.ValueTuple<int, int>..ctor(int, int)""
  IL_000f:  stelem     ""System.ValueTuple<int, int>""
  IL_0014:  dup
  IL_0015:  ldc.i4.1
  IL_0016:  ldc.i4.3
  IL_0017:  ldc.i4.4
  IL_0018:  newobj     ""System.ValueTuple<int, int>..ctor(int, int)""
  IL_001d:  stelem     ""System.ValueTuple<int, int>""
  IL_0022:  newobj     ""System.Span<System.ValueTuple<int, int>>..ctor(System.ValueTuple<int, int>[])""
  IL_0027:  stloc.0
  IL_0028:  ldc.i4.0
  IL_0029:  stloc.1
  IL_002a:  br.s       IL_0054
  IL_002c:  ldloca.s   V_0
  IL_002e:  ldloc.1
  IL_002f:  call       ""ref System.ValueTuple<int, int> System.Span<System.ValueTuple<int, int>>.this[int].get""
  IL_0034:  ldobj      ""System.ValueTuple<int, int>""
  IL_0039:  dup
  IL_003a:  ldfld      ""int System.ValueTuple<int, int>.Item1""
  IL_003f:  stloc.2
  IL_0040:  ldfld      ""int System.ValueTuple<int, int>.Item2""
  IL_0045:  ldloc.2
  IL_0046:  call       ""void System.Console.Write(int)""
  IL_004b:  call       ""void System.Console.Write(int)""
  IL_0050:  ldloc.1
  IL_0051:  ldc.i4.1
  IL_0052:  add
  IL_0053:  stloc.1
  IL_0054:  ldloc.1
  IL_0055:  ldloca.s   V_0
  IL_0057:  call       ""int System.Span<System.ValueTuple<int, int>>.Length.get""
  IL_005c:  blt.s      IL_002c
  IL_005e:  ret
}");
        }
 
        [Fact]
        public void TestSpanConvertDebug()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
 
class Test
{
    public static void Main()
    {       
        var sp = new Span<int>(new[] {1, 2, 3});
        foreach(byte i in sp)
        {
            Console.Write(i);
        }
    }
}
 
", TestOptions.DebugExe);
 
            CompileAndVerify(comp, expectedOutput: "123").VerifyIL("Test.Main", @"
{
  // Code size       67 (0x43)
  .maxstack  4
  .locals init (System.Span<int> V_0, //sp
                System.Span<int> V_1,
                int V_2,
                byte V_3) //i
  IL_0000:  nop
  IL_0001:  ldloca.s   V_0
  IL_0003:  ldc.i4.3
  IL_0004:  newarr     ""int""
  IL_0009:  dup
  IL_000a:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D""
  IL_000f:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0014:  call       ""System.Span<int>..ctor(int[])""
  IL_0019:  nop
  IL_001a:  ldloc.0
  IL_001b:  stloc.1
  IL_001c:  ldc.i4.0
  IL_001d:  stloc.2
  IL_001e:  br.s       IL_0038
  IL_0020:  ldloca.s   V_1
  IL_0022:  ldloc.2
  IL_0023:  call       ""ref int System.Span<int>.this[int].get""
  IL_0028:  ldind.i4
  IL_0029:  conv.u1
  IL_002a:  stloc.3
  IL_002b:  nop
  IL_002c:  ldloc.3
  IL_002d:  call       ""void System.Console.Write(int)""
  IL_0032:  nop
  IL_0033:  nop
  IL_0034:  ldloc.2
  IL_0035:  ldc.i4.1
  IL_0036:  add
  IL_0037:  stloc.2
  IL_0038:  ldloc.2
  IL_0039:  ldloca.s   V_1
  IL_003b:  call       ""int System.Span<int>.Length.get""
  IL_0040:  blt.s      IL_0020
  IL_0042:  ret
}");
        }
 
        // Traveled Multi-dimensional jagged arrays 
        [WorkItem(9229, "DevDiv_Projects/Roslyn")]
        [Fact]
        public void TestJaggedArray()
        {
            var text =
@"using System;
public class Test
{
    static void Main(string[] args)
    {
        int[][] arr = new int[][] { new int[] { 1, 2 }, new int[] { 4, 5, 6 } };
        foreach (int[] outer in arr)
        {
            foreach (int i in outer)
            {
                Console.WriteLine(i);
            }
        }
    }
}
";
            string expectedOutput = @"1
2
4
5
6";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        // Optimization to foreach (char c in String) by treating String as a char array 
        [Fact]
        public void TestString01()
        {
            var text =
@"using System;
public class Test
{
    static void Main(string[] args)
    {
        System.String Str = new System.String('\0', 1024);
        foreach (char C in Str) { }
    }
}
";
            string expectedOutput = @"";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestString02()
        {
            var text =
@"using System;
public class Test
{
    static public int Main(string[] args)
    {
        foreach (var var in ""goo"")
        {
            if (!var.GetType().Equals(typeof(char)))
            {
                System.Console.WriteLine(-1);
                return -1;
            }
        }
        return 0;
    }
}
";
            string expectedOutput = @"";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestString03()
        {
            var text =
@"using System;
public class Test
{
    static public void Main(string[] args)
    {
        String Str = null;
        foreach (char C in Str) { }
    }
}
";
            string expectedIL = @"{
  // Code size       28 (0x1c)
  .maxstack  2
  .locals init (string V_0,
  int V_1)
  IL_0000:  ldnull
  IL_0001:  stloc.0
  IL_0002:  ldc.i4.0
  IL_0003:  stloc.1
  IL_0004:  br.s       IL_0012
  IL_0006:  ldloc.0
  IL_0007:  ldloc.1
  IL_0008:  callvirt   ""char string.this[int].get""
  IL_000d:  pop
  IL_000e:  ldloc.1
  IL_000f:  ldc.i4.1
  IL_0010:  add
  IL_0011:  stloc.1
  IL_0012:  ldloc.1
  IL_0013:  ldloc.0
  IL_0014:  callvirt   ""int string.Length.get""
  IL_0019:  blt.s      IL_0006
  IL_001b:  ret
}";
            CompileAndVerify(text).VerifyIL("Test.Main", expectedIL);
        }
 
        // Traversing items in 'Dictionary'
        [Fact]
        public void TestDictionary()
        {
            var text =
@"using System;
using System.Collections.Generic;
public class Test
{
    static public void Main(string[] args)
    {
        Dictionary<int, int> s = new Dictionary<int, int>();
        s.Add(1, 2);
        s.Add(2, 3);
        s.Add(3, 4);
        foreach (var pair in s) { Console.WriteLine( pair.Key );}
        foreach (KeyValuePair<int, int> pair in s) {Console.WriteLine( pair.Value ); }
    }
}
";
            string expectedOutput = @"1
2
3
2
3
4";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        // Inner foreach loop referencing the outer foreach loop iteration variable
        [Fact]
        public void TestNestedLoop()
        {
            var text =
@"public class Test
{
    static public void Main(string[] args)
    {
        string[] S = new string[] { ""ABC"", ""XYZ"" };
        foreach (string x in S)
        {
            foreach (char y in x)
            {
                System.Console.WriteLine(y);
            }
        }
    }
}
";
            string expectedOutput = @"A
B
C
X
Y
Z";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        // Breaking from nested Loops
        [Fact]
        public void TestBreakInNestedLoop()
        {
            var text =
@"public class Test
{
    static public void Main(string[] args)
    {
        string[] S = new string[] { ""ABC"", ""XYZ"" };
        foreach (string x in S)
        {
            foreach (char y in x)
            {
                if (y == 'A')
                    break;
                else 
                    System.Console.WriteLine(y);
            }
            System.Console.WriteLine(x);
        }
    }
}
";
            string expectedOutput = @"ABC
X
Y
Z
XYZ";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        // Continuing  from nested Loops
        [Fact]
        public void TestContinueInNestedLoop()
        {
            var text =
@"public class Test
{
    static public void Main(string[] args)
    {
        string[] S = new string[] { ""ABC"", ""XYZ"" };
        foreach (string x in S)
        {
            foreach (char y in x)
            {
                if (y == 'C')
                    continue;
                System.Console.WriteLine(y);
            }
        }
    }
}
";
            string expectedOutput = @"A
B
X
Y
Z";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        // Goto in foreach loops
        [Fact]
        public void TestGoto01()
        {
            var text =
@"public class Test
{
    static public void Main(string[] args)
    {
        string[] S = new string[] { ""ABC"", ""XYZ"" };
        foreach (string x in S)
        {
            foreach (char y in x)
            {
                System.Console.WriteLine(y);
                goto stop;
            }
        }
    stop:
        return;
    }
}
";
            string expectedOutput = @"A";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestGoto02()
        {
            var text =
@"public class Test
{
    static public void Main(string[] args)
    {
        string[] S = new string[] { ""ABC"", ""XYZ"" };
        foreach (string x in S)
        {
            foreach (char y in x)
            {
                System.Console.WriteLine(y);
                goto outerLoop;
            }
        outerLoop:
            return;
        }
    }
}
";
            string expectedOutput = @"A";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        // 'Return' in foreach
        [Fact]
        public void TestReturn()
        {
            var text =
@"public class Test
{
    static public void Main(string[] args)
    {
        string[] S = new string[] { ""ABC"", ""XYZ"" };
        foreach (string x in S)
        {
            return;
        }
    }
}
";
            string expectedIL = @"{
  // Code size       39 (0x27)
  .maxstack  4
  .locals init (string[] V_0,
  int V_1)
  IL_0000:  ldc.i4.2
  IL_0001:  newarr     ""string""
  IL_0006:  dup
  IL_0007:  ldc.i4.0
  IL_0008:  ldstr      ""ABC""
  IL_000d:  stelem.ref
  IL_000e:  dup
  IL_000f:  ldc.i4.1
  IL_0010:  ldstr      ""XYZ""
  IL_0015:  stelem.ref
  IL_0016:  stloc.0
  IL_0017:  ldc.i4.0
  IL_0018:  stloc.1
  IL_0019:  br.s       IL_0020
  IL_001b:  ldloc.0
  IL_001c:  ldloc.1
  IL_001d:  ldelem.ref
  IL_001e:  pop
  IL_001f:  ret
  IL_0020:  ldloc.1
  IL_0021:  ldloc.0
  IL_0022:  ldlen
  IL_0023:  conv.i4
  IL_0024:  blt.s      IL_001b
  IL_0026:  ret
}
";
            CompileAndVerify(text).VerifyIL("Test.Main", expectedIL);
        }
 
        // Dynamic works in foreach 
        [Fact]
        public void TestDynamic()
        {
            var text =
@"public class Test
{
    static public void Main(string[] args)
    {
        string[] S = new string[] { ""ABC"", ""XYZ"" };
        foreach (dynamic x in S)
        {
            System.Console.WriteLine(x.ToLower());
        }
    }
}
";
            string expectedOutput = @"abc
xyz";
            CompileAndVerify(text, new[] { CSharpRef }, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestVar01()
        {
            var text =
@"using System.Collections.Generic;
public class Test
{
    static public void Main(string[] args)
    {
        foreach (var var in new List<double> { 1.0, 2.0, 3.0 })
        {
            if (var.GetType().Equals(typeof(double)))
            {
                System.Console.WriteLine(true);
            }
        }
    }
}
";
            string expectedOutput = @"True
True
True";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestVar02()
        {
            var text =
@"
public class Test
{
    static public void Main(string[] args)
    {
        foreach (var var in new string[] { ""one"", ""two"", ""three"" })
        {
            if (!var.GetType().Equals(typeof(double)))
            {
                System.Console.WriteLine(false);
            }
        }
    }
}
";
            string expectedOutput = @"False
False
False";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestVar03()
        {
            var text =
@"
public class Test
{
    static public void Main(string[] args)
    {
        foreach (var var in new MyClass())
        {
            if (var.GetType().Equals(typeof(int)))
            {
                System.Console.WriteLine(true);
            }
        }
    }
}
class MyClass
{
    public MyEnumerator GetEnumerator()
    {
        return new MyEnumerator();
    }
}
class MyEnumerator
{
    int count = 4;
    public int Current
    {
        get
        {
            return count;
        }
    }
    public bool MoveNext()
    {
        count--;
        return count != 0;
    }
}
";
            string expectedOutput = @"True
True
True";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestQuery()
        {
            var text =
@"
using System.Linq;
public class Test
{
    static public void Main(string[] args)
    {
        foreach (var x in from x in new[] { 'A', 'B', 'C' }
                          let z = x.ToString()
                          select z into w
                          select w)
        {
            System.Console.WriteLine(x.ToLower());
        }
    }
}
";
            string expectedOutput = @"a
b
c
";
            var comp = CreateCompilationWithMscorlib40AndSystemCore(text, options: TestOptions.ReleaseExe);
 
            CompileAndVerify(comp, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestYield01()
        {
            var text =
@"
using System.Collections;
public class Test
{
    public static void Main(string[] args)
    {
        foreach (int i in myClass.Power(2, 8))
        {
            System.Console.WriteLine(""{0}"", i);
        }
    }
}
 
public class myClass
{
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
        {
            result = result * number;
            yield return result;
        }
    }
}
";
            string expectedOutput = @"2
4
8
16
32
64
128
256
";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestYield02()
        {
            var text =
@"
using System.Collections.Generic;
public class Test
{
    public static void Main(string[] args)
    {
        foreach (int i in FromTo(2,4))
        {
            System.Console.WriteLine(""{0}"", i);
        }
    }
    public static IEnumerable<int> FromTo(int from, int to)
    {
        for (int i = from; i <= to; i++) yield return i;
    }
}
";
            string expectedOutput = @"2
3
4
";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestYield03()
        {
            var text =
@"
using System.Collections.Generic;
public class Test
{
    public static void Main(string[] args)
    {
        foreach (var i in EnumerateIt<string>(new List<string>() { ""abc"" }))
        {
            System.Console.WriteLine(i);
        }
    }
    public static IEnumerable<T> EnumerateIt<T>(IEnumerable<T> xs)
    {
        foreach (T x in xs) yield return x;
    }
}
";
            string expectedOutput = @"abc";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestYield04()
        {
            var text =
@"
using System.Collections.Generic;
public class Test
{
    public static void Main(string[] args)
    {
        foreach (int p in EnumerateIt(FromTo(3, 5)))
        {
            System.Console.WriteLine(""{0}"", p);
        }
    }
    public static IEnumerable<int> FromTo(int from, int to)
    {
        for (int i = from; i <= to; i++) yield return i;
    }
 
    public static IEnumerable<T> EnumerateIt<T>(IEnumerable<T> xs)
    {
        foreach (T x in xs) yield return x;
    }
}
";
            string expectedOutput = @"3
4
5";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestYield05()
        {
            var text =
@"
using System.Collections.Generic;
public class Test
{
    public static void Main(string[] args)
    {
        foreach (var j in new Gen<double>()) { System.Console.WriteLine(j); }
    }
}
public class Gen<T> where T : new()
{
    public IEnumerator<T> GetEnumerator()
    {
        yield return new T();
        yield return new T();
        yield return new T();
    }
}
";
            string expectedOutput = @"0
0
0";
            CompileAndVerify(text, expectedOutput: expectedOutput);
        }
 
        [Fact]
        public void TestValueTypeIterationVariableCanBeMutatedByInstanceMethods()
        {
            const string source = @"
struct A
{
    int field;
 
    void Set(A a)
    {
        this = a;
    }
 
    static void Main()
    {
        foreach (var a in new A[1])
        {
            a.Set(new A { field = 5 });
            System.Console.Write(a.field);
        }
    }  
}";
 
            CompileAndVerify(source, expectedOutput: "5");
        }
 
        [Fact, WorkItem(1077204, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077204")]
        public void TestValueTypeIterationVariableFieldsAreReadonly()
        {
            const string source = @"
using System;
 
struct A
{
    public B B;
 
    static void Main()
    {
        A[] array = { default(A) };
 
        foreach (A a in array)
        {
            a.B.SetField(5);
            Console.Write(a.B.Field);
        }
    }
}
 
struct B
{
    public int Field;
 
    public void SetField(int value)
    {
        this.Field = value;
    }
}";
 
            CompileAndVerify(source, expectedOutput: "0");
        }
 
        [Fact]
        public void TestValueTypeIterationVariableFieldsAreReadonly2()
        {
            const string source = @"
struct C
{
    public int field;
 
    public void SetField(int value)
    {
        field = value;
    }
}
 
struct B
{
    public C c;
}
 
struct A
{
    B b;
 
    static void Main()
    {
        foreach (var a in new A[1])
        {
            a.b.c.SetField(5);
            System.Console.Write(a.b.c.field);
        }
    }
}";
 
            CompileAndVerify(source, expectedOutput: "0");
        }
 
        [Fact]
        public void Var_ExtensionGetEnumerator()
        {
            var source = """
                using System.Collections.Generic;
                class MyCollection<T>
                {
                    public readonly List<T> Items;
                    public MyCollection(params T[] items) { Items = new(items); }
                }
                static class Extensions
                {
                    public static IEnumerator<T> GetEnumerator<T>(this MyCollection<T> c) => c.Items.GetEnumerator();
                }
                class Program
                {
                    static void Main()
                    {
                        MyCollection<int> x = new(1, 2, 3);
                        int total = 0;
                        foreach (var y in x)
                            total += y;
                    }
                }
                """;
 
            var comp = CreateCompilation(source, parseOptions: TestOptions.RegularDefault.WithFeature("run-nullable-analysis", "never"));
 
            var tree = comp.SyntaxTrees[0];
            var model = comp.GetSemanticModel(tree);
            var decl = tree.GetRoot().DescendantNodes().OfType<ForEachStatementSyntax>().Single();
            var local = (SourceLocalSymbol)model.GetDeclaredSymbol(decl).GetSymbol<LocalSymbol>();
            Assert.True(local.IsVar);
            Assert.Equal("System.Int32", local.Type.ToTestDisplayString());
 
            comp.VerifyDiagnostics();
        }
    }
}