File: CodeGen\IndexAndRangeTests.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;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
    public class IndexAndRangeTests : CSharpTestBase
    {
        private CompilationVerifier CompileAndVerifyWithIndexAndRange(string s, string expectedOutput = null)
        {
            var comp = CreateCompilationWithIndexAndRange(
                new[] { s, TestSources.GetSubArray, },
                expectedOutput is null ? TestOptions.ReleaseDll : TestOptions.ReleaseExe);
            return CompileAndVerify(comp, expectedOutput: expectedOutput);
        }
 
        private static (SemanticModel model, List<ElementAccessExpressionSyntax> accesses) GetModelAndAccesses(CSharpCompilation comp)
        {
            var syntaxTree = comp.SyntaxTrees[0];
            var root = syntaxTree.GetRoot();
            var model = comp.GetSemanticModel(syntaxTree);
 
            return (model, root.DescendantNodes().OfType<ElementAccessExpressionSyntax>().ToList());
        }
 
        private static void VerifyIndexCall(IMethodSymbol symbol, string methodName, string containingTypeName)
        {
            Assert.NotNull(symbol);
            Assert.Equal(methodName, symbol.Name);
            Assert.Equal(2, symbol.Parameters.Length);
            Assert.Equal(containingTypeName, symbol.ContainingType.Name);
        }
 
        [Fact]
        public void ExpressionTreePatternIndexAndRange()
        {
            var src = @"
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
 
struct S
{
    public int Length => 0;
    public S Slice(int start, int length) => default;
}
 
class Program
{
    static void Main()
    {
        Expression<Func<int[], int>> e = (int[] a) => a[new Index(0, true)]; // 1
        Expression<Func<List<int>, int>> e2 = (List<int> a) => a[new Index(0, true)]; // 2
        
        Expression<Func<int[], int[]>> e3 = (int[] a) => a[new Range(0, 1)]; // 3
        Expression<Func<S, S>> e4 = (S s) => s[new Range(0, 1)]; // 4
    }
}";
            var comp = CreateCompilationWithIndexAndRange(
                new[] { src, TestSources.GetSubArray, });
            comp.VerifyEmitDiagnostics(
                // (16,55): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access
                //         Expression<Func<int[], int>> e = (int[] a) => a[new Index(0, true)]; // 1
                Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, "a[new Index(0, true)]").WithLocation(16, 55),
                // (17,64): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access
                //         Expression<Func<List<int>, int>> e2 = (List<int> a) => a[new Index(0, true)]; // 2
                Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, "a[new Index(0, true)]").WithLocation(17, 64),
                // (19,58): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access
                //         Expression<Func<int[], int[]>> e3 = (int[] a) => a[new Range(0, 1)]; // 3
                Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, "a[new Range(0, 1)]").WithLocation(19, 58),
                // (20,46): error CS8790: An expression tree may not contain a pattern System.Index or System.Range indexer access
                //         Expression<Func<S, S>> e4 = (S s) => s[new Range(0, 1)]; // 4
                Diagnostic(ErrorCode.ERR_ExpressionTreeContainsPatternImplicitIndexer, "s[new Range(0, 1)]").WithLocation(20, 46)
            );
        }
 
        [Fact]
        public void ExpressionTreeFromEndIndexAndRange()
        {
            var src = @"
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
 
class Program
{
    static void Main()
    {
        Expression<Func<Index>> e = () => ^1;
        Expression<Func<Range>> e2 = () => 1..2;
    }
}";
            var comp = CreateCompilationWithIndexAndRange(src);
            comp.VerifyEmitDiagnostics(
                // (10,43): error CS8791: An expression tree may not contain a from-end index ('^') expression.
                //         Expression<Func<Index>> e = () => ^1;
                Diagnostic(ErrorCode.ERR_ExpressionTreeContainsFromEndIndexExpression, "^1").WithLocation(10, 43),
                // (11,44): error CS8792: An expression tree may not contain a range ('..') expression.
                //         Expression<Func<Range>> e2 = () => 1..2;
                Diagnostic(ErrorCode.ERR_ExpressionTreeContainsRangeExpression, "1..2").WithLocation(11, 44)
            );
        }
 
        [Fact]
        public void PatternIndexArray()
        {
            var src = @"
class C
{
    static int M1(int[] arr) => arr[^1];
    static char M2(string s) => s[^1];
    static int M3(int[] arr, int i) => arr[^i];
}
";
            var verifier = CompileAndVerifyWithIndexAndRange(src);
            // Code gen for the following two should look basically
            // the same, except that string will use Length/indexer
            // and array will use ldlen/ldelem, and string may have
            // more temporaries
            verifier.VerifyIL("C.M1", @"
{
  // Code size        8 (0x8)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  dup
  IL_0002:  ldlen
  IL_0003:  conv.i4
  IL_0004:  ldc.i4.1
  IL_0005:  sub
  IL_0006:  ldelem.i4
  IL_0007:  ret
}");
            verifier.VerifyIL("C.M2", @"
{
  // Code size       15 (0xf)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  dup
  IL_0002:  callvirt   ""int string.Length.get""
  IL_0007:  ldc.i4.1
  IL_0008:  sub
  IL_0009:  callvirt   ""char string.this[int].get""
  IL_000e:  ret
}
");
            verifier.VerifyIL("C.M3", @"
{
  // Code size        8 (0x8)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  dup
  IL_0002:  ldlen
  IL_0003:  conv.i4
  IL_0004:  ldarg.1
  IL_0005:  sub
  IL_0006:  ldelem.i4
  IL_0007:  ret
}
");
        }
 
        [Fact]
        [WorkItem(37789, "https://github.com/dotnet/roslyn/issues/37789")]
        public void PatternIndexAndRangeCompoundOperatorRefIndexer()
        {
            var src = @"
using System;
class C
{
    static void Main(string[] args)
    {
        var span = new Span<byte>(new byte[2]);
        Console.WriteLine(span[1]);
        span[^1] += 1;
        Console.WriteLine(span[1]);
    }
}
";
            var comp = CreateCompilationWithIndexAndRangeAndSpan(src, TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"0
1");
            verifier.VerifyIL("C.Main", @"
{
  // Code size       63 (0x3f)
  .maxstack  3
  .locals init (System.Span<byte> V_0) //span
  IL_0000:  ldloca.s   V_0
  IL_0002:  ldc.i4.2
  IL_0003:  newarr     ""byte""
  IL_0008:  call       ""System.Span<byte>..ctor(byte[])""
  IL_000d:  ldloca.s   V_0
  IL_000f:  ldc.i4.1
  IL_0010:  call       ""ref byte System.Span<byte>.this[int].get""
  IL_0015:  ldind.u1
  IL_0016:  call       ""void System.Console.WriteLine(int)""
  IL_001b:  ldloca.s   V_0
  IL_001d:  dup
  IL_001e:  call       ""int System.Span<byte>.Length.get""
  IL_0023:  ldc.i4.1
  IL_0024:  sub
  IL_0025:  call       ""ref byte System.Span<byte>.this[int].get""
  IL_002a:  dup
  IL_002b:  ldind.u1
  IL_002c:  ldc.i4.1
  IL_002d:  add
  IL_002e:  conv.u1
  IL_002f:  stind.i1
  IL_0030:  ldloca.s   V_0
  IL_0032:  ldc.i4.1
  IL_0033:  call       ""ref byte System.Span<byte>.this[int].get""
  IL_0038:  ldind.u1
  IL_0039:  call       ""void System.Console.WriteLine(int)""
  IL_003e:  ret
}
");
        }
 
        [Fact]
        [WorkItem(37789, "https://github.com/dotnet/roslyn/issues/37789")]
        public void PatternIndexCompoundOperator()
        {
            var src = @"
using System;
struct S
{
    private readonly int[] _array;
 
    private int _counter;
 
    public S(int[] a)
    {
        _array = a;
        _counter = 0;
    }
    public int Length
    {
        get
        {
            Console.WriteLine(""Length "" + _counter++);
            return _array.Length;
        }
    }
    public int this[int index] 
    {
        get
        {
            Console.WriteLine(""Get "" + _counter++);
            return _array[index];
        }
        set
        {
            Console.WriteLine(""Set "" + _counter++);
            _array[index] = value;
        }
    }
}
class C
{
    static void Main(string[] args)
    {
        var array = new int[2];
        Console.WriteLine(array[1]);
        var s = new S(array);
        s[^1] += 5;
        Console.WriteLine(array[1]);
    }
}
";
            var comp = CreateCompilationWithIndexAndRangeAndSpan(src, TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"0
Length 0
Get 1
Set 2
5");
            verifier.VerifyIL("C.Main", @"
{
  // Code size       60 (0x3c)
  .maxstack  4
  .locals init (int[] V_0, //array
                S V_1, //s
                S& V_2,
                int V_3)
  IL_0000:  ldc.i4.2
  IL_0001:  newarr     ""int""
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.1
  IL_0009:  ldelem.i4
  IL_000a:  call       ""void System.Console.WriteLine(int)""
  IL_000f:  ldloca.s   V_1
  IL_0011:  ldloc.0
  IL_0012:  call       ""S..ctor(int[])""
  IL_0017:  ldloca.s   V_1
  IL_0019:  stloc.2
  IL_001a:  ldloc.2
  IL_001b:  call       ""int S.Length.get""
  IL_0020:  ldc.i4.1
  IL_0021:  sub
  IL_0022:  stloc.3
  IL_0023:  ldloc.2
  IL_0024:  ldloc.3
  IL_0025:  ldloc.2
  IL_0026:  ldloc.3
  IL_0027:  call       ""int S.this[int].get""
  IL_002c:  ldc.i4.5
  IL_002d:  add
  IL_002e:  call       ""void S.this[int].set""
  IL_0033:  ldloc.0
  IL_0034:  ldc.i4.1
  IL_0035:  ldelem.i4
  IL_0036:  call       ""void System.Console.WriteLine(int)""
  IL_003b:  ret
}
");
        }
 
        [Fact]
        public void PatternIndexCompoundOperator_InReadonlyMethod()
        {
            var src = @"
public struct S
{
    public readonly int[] _array;
    public int _counter;
 
    public S(int[] a)
    {
        _array = a;
        _counter = 0;
    }
    public int Length
    {
        get => throw null;
    }
    public int this[int index]
    {
        get => throw null;
        set => throw null;
    }
 
    readonly void M()
    {
        this[^1] += 5;
    }
}
";
            var comp = CreateCompilationWithIndexAndRangeAndSpan(src);
            comp.VerifyDiagnostics(
                // (24,9): warning CS8656: Call to non-readonly member 'S.Length.get' from a 'readonly' member results in an implicit copy of 'this'.
                //         this[^1] += 5;
                Diagnostic(ErrorCode.WRN_ImplicitCopyInReadOnlyMember, "this").WithArguments("S.Length.get", "this").WithLocation(24, 9),
                // (24,9): error CS1604: Cannot assign to 'this[^1]' because it is read-only
                //         this[^1] += 5;
                Diagnostic(ErrorCode.ERR_AssgReadonlyLocal, "this[^1]").WithArguments("this[^1]").WithLocation(24, 9)
                );
        }
 
        [Fact]
        [WorkItem(37789, "https://github.com/dotnet/roslyn/issues/37789")]
        public void PatternRangeCompoundOperator()
        {
            var src = @"
using System;
struct S
{
    private readonly int[] _array;
 
    private int _counter;
 
    public S(int[] a)
    {
        _array = a;
        _counter = 0;
    }
    public int Length
    {
        get
        {
            Console.WriteLine(""Length "" + _counter++);
            return _array.Length;
        }
    }
    public ref int Slice(int start, int length)
    {
        Console.WriteLine(""Slice "" + _counter++);
        return ref _array[start];
    }
}
class C
{
    static void Main(string[] args)
    {
        var array = new int[2];
        Console.WriteLine(array[1]);
        var s = new S(array);
        s[1..] += 5;
        Console.WriteLine(array[1]);
    }
}
";
            var comp = CreateCompilationWithIndexAndRangeAndSpan(src, TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"0
Length 0
Slice 1
5");
            verifier.VerifyIL("C.Main", @"
{
  // Code size       55 (0x37)
      .maxstack  4
      .locals init (int[] V_0, //array
                    S V_1, //s
                    S& V_2)
      IL_0000:  ldc.i4.2
      IL_0001:  newarr     ""int""
      IL_0006:  stloc.0
      IL_0007:  ldloc.0
      IL_0008:  ldc.i4.1
      IL_0009:  ldelem.i4
      IL_000a:  call       ""void System.Console.WriteLine(int)""
      IL_000f:  ldloca.s   V_1
      IL_0011:  ldloc.0
      IL_0012:  call       ""S..ctor(int[])""
      IL_0017:  ldloca.s   V_1
      IL_0019:  stloc.2
      IL_001a:  ldloc.2
      IL_001b:  ldc.i4.1
      IL_001c:  ldloc.2
      IL_001d:  call       ""int S.Length.get""
      IL_0022:  ldc.i4.1
      IL_0023:  sub
      IL_0024:  call       ""ref int S.Slice(int, int)""
      IL_0029:  dup
      IL_002a:  ldind.i4
      IL_002b:  ldc.i4.5
      IL_002c:  add
      IL_002d:  stind.i4
      IL_002e:  ldloc.0
      IL_002f:  ldc.i4.1
      IL_0030:  ldelem.i4
      IL_0031:  call       ""void System.Console.WriteLine(int)""
      IL_0036:  ret
}");
        }
 
        [Fact]
        [WorkItem(37789, "https://github.com/dotnet/roslyn/issues/37789")]
        public void PatternindexNullableCoalescingAssignmentClass()
        {
            var src = @"
using System;
struct S
{
    private readonly string[] _array;
 
    private int _counter;
 
    public S(string[] a)
    {
        _array = a;
        _counter = 0;
    }
    public int Length
    {
        get
        {
            Console.WriteLine(""Length "" + _counter++);
            return _array.Length;
        }
    }
    public string this[int index] 
    {
        get
        {
            Console.WriteLine(""Get "" + _counter++);
            return _array[index];
        }
        set
        {
            Console.WriteLine(""Set "" + _counter++);
            _array[index] = value;
        }
    }
}
class C
{
    static void Main(string[] args)
    {
        var array = new string[2];
        array[0] = ""abc"";
        Console.WriteLine(array[1] is null);
        var s = new S(array);
        s[^1] ??= s[^2];
        s[^1] ??= s[^2];
        Console.WriteLine(s[^1] ??= ""def"");
        Console.WriteLine(array[1]);
    }
}";
            var comp = CreateCompilationWithIndex(src, options: TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"
True
Length 0
Get 1
Length 2
Get 3
Set 4
Length 5
Get 6
Length 7
Get 8
abc
abc");
            verifier.VerifyIL("C.Main", @"
{
  // Code size      180 (0xb4)
  .maxstack  5
  .locals init (string[] V_0, //array
                S V_1, //s
                S& V_2,
                int V_3,
                string V_4)
  IL_0000:  ldc.i4.2
  IL_0001:  newarr     ""string""
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.0
  IL_0009:  ldstr      ""abc""
  IL_000e:  stelem.ref
  IL_000f:  ldloc.0
  IL_0010:  ldc.i4.1
  IL_0011:  ldelem.ref
  IL_0012:  ldnull
  IL_0013:  ceq
  IL_0015:  call       ""void System.Console.WriteLine(bool)""
  IL_001a:  ldloca.s   V_1
  IL_001c:  ldloc.0
  IL_001d:  call       ""S..ctor(string[])""
  IL_0022:  ldloca.s   V_1
  IL_0024:  stloc.2
  IL_0025:  ldloc.2
  IL_0026:  call       ""int S.Length.get""
  IL_002b:  ldc.i4.1
  IL_002c:  sub
  IL_002d:  stloc.3
  IL_002e:  ldloc.2
  IL_002f:  ldloc.3
  IL_0030:  call       ""string S.this[int].get""
  IL_0035:  brtrue.s   IL_0050
  IL_0037:  ldloc.2
  IL_0038:  ldloc.3
  IL_0039:  ldloca.s   V_1
  IL_003b:  dup
  IL_003c:  call       ""int S.Length.get""
  IL_0041:  ldc.i4.2
  IL_0042:  sub
  IL_0043:  call       ""string S.this[int].get""
  IL_0048:  dup
  IL_0049:  stloc.s    V_4
  IL_004b:  call       ""void S.this[int].set""
  IL_0050:  ldloca.s   V_1
  IL_0052:  stloc.2
  IL_0053:  ldloc.2
  IL_0054:  call       ""int S.Length.get""
  IL_0059:  ldc.i4.1
  IL_005a:  sub
  IL_005b:  stloc.3
  IL_005c:  ldloc.2
  IL_005d:  ldloc.3
  IL_005e:  call       ""string S.this[int].get""
  IL_0063:  brtrue.s   IL_007e
  IL_0065:  ldloc.2
  IL_0066:  ldloc.3
  IL_0067:  ldloca.s   V_1
  IL_0069:  dup
  IL_006a:  call       ""int S.Length.get""
  IL_006f:  ldc.i4.2
  IL_0070:  sub
  IL_0071:  call       ""string S.this[int].get""
  IL_0076:  dup
  IL_0077:  stloc.s    V_4
  IL_0079:  call       ""void S.this[int].set""
  IL_007e:  ldloca.s   V_1
  IL_0080:  stloc.2
  IL_0081:  ldloc.2
  IL_0082:  call       ""int S.Length.get""
  IL_0087:  ldc.i4.1
  IL_0088:  sub
  IL_0089:  stloc.3
  IL_008a:  ldloc.2
  IL_008b:  ldloc.3
  IL_008c:  call       ""string S.this[int].get""
  IL_0091:  dup
  IL_0092:  brtrue.s   IL_00a6
  IL_0094:  pop
  IL_0095:  ldloc.2
  IL_0096:  ldloc.3
  IL_0097:  ldstr      ""def""
  IL_009c:  dup
  IL_009d:  stloc.s    V_4
  IL_009f:  call       ""void S.this[int].set""
  IL_00a4:  ldloc.s    V_4
  IL_00a6:  call       ""void System.Console.WriteLine(string)""
  IL_00ab:  ldloc.0
  IL_00ac:  ldc.i4.1
  IL_00ad:  ldelem.ref
  IL_00ae:  call       ""void System.Console.WriteLine(string)""
  IL_00b3:  ret
}
");
        }
 
        [Fact]
        [WorkItem(37789, "https://github.com/dotnet/roslyn/issues/37789")]
        public void PatternindexNullableCoalescingAssignmentStruct()
        {
            var src = @"
using System;
struct S
{
    private readonly int?[] _array;
 
    private int _counter;
 
    public S(int?[] a)
    {
        _array = a;
        _counter = 0;
    }
    public int Length
    {
        get
        {
            Console.WriteLine(""Length "" + _counter++);
            return _array.Length;
        }
    }
    public int? this[int index] 
    {
        get
        {
            Console.WriteLine(""Get "" + _counter++);
            return _array[index];
        }
        set
        {
            Console.WriteLine(""Set "" + _counter++);
            _array[index] = value;
        }
    }
 
}
class C
{
    static void Main(string[] args)
    {
        var array = new int?[2];
        array[0] = 1;
        Console.WriteLine(array[1] is null);
        var s = new S(array);
        s[^1] ??= s[^2];
        s[^1] ??= s[^2];
        Console.WriteLine(s[^1] ??= 0);
        Console.WriteLine(array[1]);
    }
}";
            var comp = CreateCompilationWithIndex(src, options: TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"
True
Length 0
Get 1
Length 2
Get 3
Set 4
Length 5
Get 6
Length 7
Get 8
1
1");
            verifier.VerifyIL("C.Main", @"
{
  // Code size      256 (0x100)
  .maxstack  5
  .locals init (int?[] V_0, //array
                S V_1, //s
                int? V_2,
                S& V_3,
                int V_4,
                int? V_5,
                int V_6)
  IL_0000:  ldc.i4.2
  IL_0001:  newarr     ""int?""
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.0
  IL_0009:  ldc.i4.1
  IL_000a:  newobj     ""int?..ctor(int)""
  IL_000f:  stelem     ""int?""
  IL_0014:  ldloc.0
  IL_0015:  ldc.i4.1
  IL_0016:  ldelem     ""int?""
  IL_001b:  stloc.2
  IL_001c:  ldloca.s   V_2
  IL_001e:  call       ""bool int?.HasValue.get""
  IL_0023:  ldc.i4.0
  IL_0024:  ceq
  IL_0026:  call       ""void System.Console.WriteLine(bool)""
  IL_002b:  ldloca.s   V_1
  IL_002d:  ldloc.0
  IL_002e:  call       ""S..ctor(int?[])""
  IL_0033:  ldloca.s   V_1
  IL_0035:  stloc.3
  IL_0036:  ldloc.3
  IL_0037:  call       ""int S.Length.get""
  IL_003c:  ldc.i4.1
  IL_003d:  sub
  IL_003e:  stloc.s    V_4
  IL_0040:  ldloc.3
  IL_0041:  ldloc.s    V_4
  IL_0043:  call       ""int? S.this[int].get""
  IL_0048:  stloc.2
  IL_0049:  ldloca.s   V_2
  IL_004b:  call       ""bool int?.HasValue.get""
  IL_0050:  brtrue.s   IL_006c
  IL_0052:  ldloc.3
  IL_0053:  ldloc.s    V_4
  IL_0055:  ldloca.s   V_1
  IL_0057:  dup
  IL_0058:  call       ""int S.Length.get""
  IL_005d:  ldc.i4.2
  IL_005e:  sub
  IL_005f:  call       ""int? S.this[int].get""
  IL_0064:  dup
  IL_0065:  stloc.s    V_5
  IL_0067:  call       ""void S.this[int].set""
  IL_006c:  ldloca.s   V_1
  IL_006e:  stloc.3
  IL_006f:  ldloc.3
  IL_0070:  call       ""int S.Length.get""
  IL_0075:  ldc.i4.1
  IL_0076:  sub
  IL_0077:  stloc.s    V_4
  IL_0079:  ldloc.3
  IL_007a:  ldloc.s    V_4
  IL_007c:  call       ""int? S.this[int].get""
  IL_0081:  stloc.2
  IL_0082:  ldloca.s   V_2
  IL_0084:  call       ""bool int?.HasValue.get""
  IL_0089:  brtrue.s   IL_00a5
  IL_008b:  ldloc.3
  IL_008c:  ldloc.s    V_4
  IL_008e:  ldloca.s   V_1
  IL_0090:  dup
  IL_0091:  call       ""int S.Length.get""
  IL_0096:  ldc.i4.2
  IL_0097:  sub
  IL_0098:  call       ""int? S.this[int].get""
  IL_009d:  dup
  IL_009e:  stloc.s    V_5
  IL_00a0:  call       ""void S.this[int].set""
  IL_00a5:  ldloca.s   V_1
  IL_00a7:  stloc.3
  IL_00a8:  ldloc.3
  IL_00a9:  call       ""int S.Length.get""
  IL_00ae:  ldc.i4.1
  IL_00af:  sub
  IL_00b0:  stloc.s    V_4
  IL_00b2:  ldloc.3
  IL_00b3:  ldloc.s    V_4
  IL_00b5:  call       ""int? S.this[int].get""
  IL_00ba:  stloc.2
  IL_00bb:  ldloca.s   V_2
  IL_00bd:  call       ""int int?.GetValueOrDefault()""
  IL_00c2:  stloc.s    V_6
  IL_00c4:  ldloca.s   V_2
  IL_00c6:  call       ""bool int?.HasValue.get""
  IL_00cb:  brtrue.s   IL_00e7
  IL_00cd:  ldc.i4.0
  IL_00ce:  stloc.s    V_6
  IL_00d0:  ldloc.3
  IL_00d1:  ldloc.s    V_4
  IL_00d3:  ldloca.s   V_5
  IL_00d5:  ldloc.s    V_6
  IL_00d7:  call       ""int?..ctor(int)""
  IL_00dc:  ldloc.s    V_5
  IL_00de:  call       ""void S.this[int].set""
  IL_00e3:  ldloc.s    V_6
  IL_00e5:  br.s       IL_00e9
  IL_00e7:  ldloc.s    V_6
  IL_00e9:  call       ""void System.Console.WriteLine(int)""
  IL_00ee:  ldloc.0
  IL_00ef:  ldc.i4.1
  IL_00f0:  ldelem     ""int?""
  IL_00f5:  box        ""int?""
  IL_00fa:  call       ""void System.Console.WriteLine(object)""
  IL_00ff:  ret
}
");
        }
 
        [Fact]
        public void StringAndSpanPatternRangeOpenEnd()
        {
            var src = @"
using System;
class C
{
    public static void Main()
    {
        string s = ""abcd"";
        Console.WriteLine(s[..]);
        ReadOnlySpan<char> span = s;
        foreach (var c in span[..])
        {
            Console.Write(c);
        }
    }
}";
            var comp = CreateCompilationWithIndexAndRangeAndSpanAndMemoryExtensions(src, TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"
abcd
abcd");
            verifier.VerifyIL("C.Main", @"
{
  // Code size       84 (0x54)
      .maxstack  4
      .locals init (System.ReadOnlySpan<char> V_0, //span
                    string V_1,
                    System.ReadOnlySpan<char> V_2,
                    int V_3,
                    System.ReadOnlySpan<char>& V_4)
      IL_0000:  ldstr      ""abcd""
      IL_0005:  dup
      IL_0006:  stloc.1
      IL_0007:  ldloc.1
      IL_0008:  ldc.i4.0
      IL_0009:  ldloc.1
      IL_000a:  callvirt   ""int string.Length.get""
      IL_000f:  callvirt   ""string string.Substring(int, int)""
      IL_0014:  call       ""void System.Console.WriteLine(string)""
      IL_0019:  call       ""System.ReadOnlySpan<char> System.MemoryExtensions.AsSpan(string)""
      IL_001e:  stloc.0
      IL_001f:  ldloca.s   V_0
      IL_0021:  stloc.s    V_4
      IL_0023:  ldloc.s    V_4
      IL_0025:  ldc.i4.0
      IL_0026:  ldloc.s    V_4
      IL_0028:  call       ""int System.ReadOnlySpan<char>.Length.get""
      IL_002d:  call       ""System.ReadOnlySpan<char> System.ReadOnlySpan<char>.Slice(int, int)""
      IL_0032:  stloc.2
      IL_0033:  ldc.i4.0
      IL_0034:  stloc.3
      IL_0035:  br.s       IL_0049
      IL_0037:  ldloca.s   V_2
      IL_0039:  ldloc.3
      IL_003a:  call       ""ref readonly char System.ReadOnlySpan<char>.this[int].get""
      IL_003f:  ldind.u2
      IL_0040:  call       ""void System.Console.Write(char)""
      IL_0045:  ldloc.3
      IL_0046:  ldc.i4.1
      IL_0047:  add
      IL_0048:  stloc.3
      IL_0049:  ldloc.3
      IL_004a:  ldloca.s   V_2
      IL_004c:  call       ""int System.ReadOnlySpan<char>.Length.get""
      IL_0051:  blt.s      IL_0037
      IL_0053:  ret
}");
 
            var (model, elementAccesses) = GetModelAndAccesses(comp);
 
            var info = model.GetSymbolInfo(elementAccesses[0]);
            var substringCall = (IMethodSymbol)info.Symbol;
            info = model.GetSymbolInfo(elementAccesses[1]);
            var sliceCall = (IMethodSymbol)info.Symbol;
 
            VerifyIndexCall(substringCall, "Substring", "String");
            VerifyIndexCall(sliceCall, "Slice", "ReadOnlySpan");
        }
 
        [Fact]
        public void SpanTaskReturn()
        {
            var src = @"
using System;
using System.Threading.Tasks;
class C
{
    static void Throws(Action a)
    {
        try
        {
            a();
        }
        catch
        {
            Console.WriteLine(""throws"");
        }
    }
 
    public static void Main()
    {
        string s = ""abcd"";
        Throws(() => { var span = new Span<char>(s.ToCharArray())[0..10]; });
    }
}";
            var comp = CreateCompilationWithIndexAndRangeAndSpan(src, TestOptions.ReleaseExe);
            CompileAndVerify(comp, expectedOutput: "throws");
 
            var (model, accesses) = GetModelAndAccesses(comp);
            VerifyIndexCall((IMethodSymbol)model.GetSymbolInfo(accesses[0]).Symbol, "Slice", "Span");
        }
 
        [Fact]
        public void PatternIndexSetter()
        {
            var src = @"
using System;
struct S
{
    public int F;
    public int Length => 1;
    public int this[int i]
    {
        get => F;
        set { F = value; }
    }
}
class C
{
    static void Main()
    {
        S s = new S();
        s.F = 0;
        Console.WriteLine(s[^1]);
        s[^1] = 2;
        Console.WriteLine(s[^1]);
        Console.WriteLine(s.F);
    }
}";
            var comp = CreateCompilationWithIndexAndRange(src, TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"0
2
2");
            verifier.VerifyIL("C.Main", @"
{
  // Code size       84 (0x54)
  .maxstack  3
  .locals init (S V_0) //s
  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    ""S""
  IL_0008:  ldloca.s   V_0
  IL_000a:  ldc.i4.0
  IL_000b:  stfld      ""int S.F""
  IL_0010:  ldloca.s   V_0
  IL_0012:  dup
  IL_0013:  call       ""int S.Length.get""
  IL_0018:  ldc.i4.1
  IL_0019:  sub
  IL_001a:  call       ""int S.this[int].get""
  IL_001f:  call       ""void System.Console.WriteLine(int)""
  IL_0024:  ldloca.s   V_0
  IL_0026:  dup
  IL_0027:  call       ""int S.Length.get""
  IL_002c:  ldc.i4.1
  IL_002d:  sub
  IL_002e:  ldc.i4.2
  IL_002f:  call       ""void S.this[int].set""
  IL_0034:  ldloca.s   V_0
  IL_0036:  dup
  IL_0037:  call       ""int S.Length.get""
  IL_003c:  ldc.i4.1
  IL_003d:  sub
  IL_003e:  call       ""int S.this[int].get""
  IL_0043:  call       ""void System.Console.WriteLine(int)""
  IL_0048:  ldloc.0
  IL_0049:  ldfld      ""int S.F""
  IL_004e:  call       ""void System.Console.WriteLine(int)""
  IL_0053:  ret
}
");
 
            var (model, accesses) = GetModelAndAccesses(comp);
 
            foreach (var access in accesses)
            {
                var info = model.GetSymbolInfo(access);
                var property = (IPropertySymbol)info.Symbol;
 
                Assert.NotNull(property);
                Assert.True(property.IsIndexer);
                Assert.Equal(SpecialType.System_Int32, property.Parameters[0].Type.SpecialType);
                Assert.Equal("S", property.ContainingType.Name);
            }
        }
 
        [Fact]
        public void PatternIndexerRefReturn()
        {
            var comp = CreateCompilationWithIndexAndRangeAndSpan(@"
using System;
class C
{
    static void Main()
    {
        Span<int> s = new int[] { 2, 4, 5, 6 };
        Console.WriteLine(s[^2]);
        ref int x = ref s[^2];
        Console.WriteLine(x);
        s[^2] = 9;
        Console.WriteLine(s[^2]);
        Console.WriteLine(x);
    }
}", TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"5
5
9
9");
            verifier.VerifyIL("C.Main", @"
{
  // Code size      112 (0x70)
  .maxstack  4
  .locals init (System.Span<int> V_0) //s
  IL_0000:  ldc.i4.4
  IL_0001:  newarr     ""int""
  IL_0006:  dup
  IL_0007:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=16 <PrivateImplementationDetails>.B35A10C764778866E34111165FC69660C6171DF0CB0141E39FA0217EF7A97646""
  IL_000c:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
  IL_0011:  call       ""System.Span<int> System.Span<int>.op_Implicit(int[])""
  IL_0016:  stloc.0
  IL_0017:  ldloca.s   V_0
  IL_0019:  dup
  IL_001a:  call       ""int System.Span<int>.Length.get""
  IL_001f:  ldc.i4.2
  IL_0020:  sub
  IL_0021:  call       ""ref int System.Span<int>.this[int].get""
  IL_0026:  ldind.i4
  IL_0027:  call       ""void System.Console.WriteLine(int)""
  IL_002c:  ldloca.s   V_0
  IL_002e:  dup
  IL_002f:  call       ""int System.Span<int>.Length.get""
  IL_0034:  ldc.i4.2
  IL_0035:  sub
  IL_0036:  call       ""ref int System.Span<int>.this[int].get""
  IL_003b:  dup
  IL_003c:  ldind.i4
  IL_003d:  call       ""void System.Console.WriteLine(int)""
  IL_0042:  ldloca.s   V_0
  IL_0044:  dup
  IL_0045:  call       ""int System.Span<int>.Length.get""
  IL_004a:  ldc.i4.2
  IL_004b:  sub
  IL_004c:  call       ""ref int System.Span<int>.this[int].get""
  IL_0051:  ldc.i4.s   9
  IL_0053:  stind.i4
  IL_0054:  ldloca.s   V_0
  IL_0056:  dup
  IL_0057:  call       ""int System.Span<int>.Length.get""
  IL_005c:  ldc.i4.2
  IL_005d:  sub
  IL_005e:  call       ""ref int System.Span<int>.this[int].get""
  IL_0063:  ldind.i4
  IL_0064:  call       ""void System.Console.WriteLine(int)""
  IL_0069:  ldind.i4
  IL_006a:  call       ""void System.Console.WriteLine(int)""
  IL_006f:  ret
}
");
        }
 
        [Fact]
        public void PatternIndexAndRangeSpanChar()
        {
            var comp = CreateCompilationWithIndexAndRangeAndSpanAndMemoryExtensions(@"
using System;
class C
{
    static void Main()
    {
        ReadOnlySpan<char> s = ""abcdefg"";
        Console.WriteLine(s[^2]);
        var index = ^1;
        Console.WriteLine(s[index]);
        s = s[^2..];
        Console.WriteLine(s[0]);
        Console.WriteLine(s[1]);
    }
}", TestOptions.ReleaseExe); ;
            var verifier = CompileAndVerify(comp, expectedOutput: @"f
g
f
g");
            verifier.VerifyIL(@"C.Main", @"
{
  // Code size      124 (0x7c)
  .maxstack  4
  .locals init (System.ReadOnlySpan<char> V_0, //s
                System.Index V_1, //index
                System.ReadOnlySpan<char>& V_2,
                int V_3,
                int V_4)
  IL_0000:  ldstr      ""abcdefg""
  IL_0005:  call       ""System.ReadOnlySpan<char> System.MemoryExtensions.AsSpan(string)""
  IL_000a:  stloc.0
  IL_000b:  ldloca.s   V_0
  IL_000d:  dup
  IL_000e:  call       ""int System.ReadOnlySpan<char>.Length.get""
  IL_0013:  ldc.i4.2
  IL_0014:  sub
  IL_0015:  call       ""ref readonly char System.ReadOnlySpan<char>.this[int].get""
  IL_001a:  ldind.u2
  IL_001b:  call       ""void System.Console.WriteLine(char)""
  IL_0020:  ldloca.s   V_1
  IL_0022:  ldc.i4.1
  IL_0023:  ldc.i4.1
  IL_0024:  call       ""System.Index..ctor(int, bool)""
  IL_0029:  ldloca.s   V_0
  IL_002b:  stloc.2
  IL_002c:  ldloc.2
  IL_002d:  ldloca.s   V_1
  IL_002f:  ldloc.2
  IL_0030:  call       ""int System.ReadOnlySpan<char>.Length.get""
  IL_0035:  call       ""int System.Index.GetOffset(int)""
  IL_003a:  call       ""ref readonly char System.ReadOnlySpan<char>.this[int].get""
  IL_003f:  ldind.u2
  IL_0040:  call       ""void System.Console.WriteLine(char)""
  IL_0045:  ldloca.s   V_0
  IL_0047:  dup
  IL_0048:  call       ""int System.ReadOnlySpan<char>.Length.get""
  IL_004d:  stloc.3
  IL_004e:  ldloc.3
  IL_004f:  ldc.i4.2
  IL_0050:  sub
  IL_0051:  stloc.s    V_4
  IL_0053:  ldloc.s    V_4
  IL_0055:  ldloc.3
  IL_0056:  ldloc.s    V_4
  IL_0058:  sub
  IL_0059:  call       ""System.ReadOnlySpan<char> System.ReadOnlySpan<char>.Slice(int, int)""
  IL_005e:  stloc.0
  IL_005f:  ldloca.s   V_0
  IL_0061:  ldc.i4.0
  IL_0062:  call       ""ref readonly char System.ReadOnlySpan<char>.this[int].get""
  IL_0067:  ldind.u2
  IL_0068:  call       ""void System.Console.WriteLine(char)""
  IL_006d:  ldloca.s   V_0
  IL_006f:  ldc.i4.1
  IL_0070:  call       ""ref readonly char System.ReadOnlySpan<char>.this[int].get""
  IL_0075:  ldind.u2
  IL_0076:  call       ""void System.Console.WriteLine(char)""
  IL_007b:  ret
}
");
        }
 
        [Fact]
        public void PatternIndexAndRangeSpanInt()
        {
            var comp = CreateCompilationWithIndexAndRangeAndSpan(@"
using System;
class C
{
    static void Main()
    {
        Span<int> s = new int[] { 2, 4, 5, 6 };
        Console.WriteLine(s[^2]);
        var index = ^1;
        Console.WriteLine(s[index]);
        s = s[^2..];
        Console.WriteLine(s[0]);
        Console.WriteLine(s[1]);
    }
}", TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"5
6
5
6");
            verifier.VerifyIL("C.Main", @"
{
  // Code size      136 (0x88)
      .maxstack  4
      .locals init (System.Span<int> V_0, //s
                    System.Index V_1, //index
                    System.Span<int>& V_2,
                    int V_3,
                    int V_4)
      IL_0000:  ldc.i4.4
      IL_0001:  newarr     ""int""
      IL_0006:  dup
      IL_0007:  ldtoken    ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=16 <PrivateImplementationDetails>.B35A10C764778866E34111165FC69660C6171DF0CB0141E39FA0217EF7A97646""
      IL_000c:  call       ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
      IL_0011:  call       ""System.Span<int> System.Span<int>.op_Implicit(int[])""
      IL_0016:  stloc.0
      IL_0017:  ldloca.s   V_0
      IL_0019:  dup
      IL_001a:  call       ""int System.Span<int>.Length.get""
      IL_001f:  ldc.i4.2
      IL_0020:  sub
      IL_0021:  call       ""ref int System.Span<int>.this[int].get""
      IL_0026:  ldind.i4
      IL_0027:  call       ""void System.Console.WriteLine(int)""
      IL_002c:  ldloca.s   V_1
      IL_002e:  ldc.i4.1
      IL_002f:  ldc.i4.1
      IL_0030:  call       ""System.Index..ctor(int, bool)""
      IL_0035:  ldloca.s   V_0
      IL_0037:  stloc.2
      IL_0038:  ldloc.2
      IL_0039:  ldloca.s   V_1
      IL_003b:  ldloc.2
      IL_003c:  call       ""int System.Span<int>.Length.get""
      IL_0041:  call       ""int System.Index.GetOffset(int)""
      IL_0046:  call       ""ref int System.Span<int>.this[int].get""
      IL_004b:  ldind.i4
      IL_004c:  call       ""void System.Console.WriteLine(int)""
      IL_0051:  ldloca.s   V_0
      IL_0053:  dup
      IL_0054:  call       ""int System.Span<int>.Length.get""
      IL_0059:  stloc.3
      IL_005a:  ldloc.3
      IL_005b:  ldc.i4.2
      IL_005c:  sub
      IL_005d:  stloc.s    V_4
      IL_005f:  ldloc.s    V_4
      IL_0061:  ldloc.3
      IL_0062:  ldloc.s    V_4
      IL_0064:  sub
      IL_0065:  call       ""System.Span<int> System.Span<int>.Slice(int, int)""
      IL_006a:  stloc.0
      IL_006b:  ldloca.s   V_0
      IL_006d:  ldc.i4.0
      IL_006e:  call       ""ref int System.Span<int>.this[int].get""
      IL_0073:  ldind.i4
      IL_0074:  call       ""void System.Console.WriteLine(int)""
      IL_0079:  ldloca.s   V_0
      IL_007b:  ldc.i4.1
      IL_007c:  call       ""ref int System.Span<int>.this[int].get""
      IL_0081:  ldind.i4
      IL_0082:  call       ""void System.Console.WriteLine(int)""
      IL_0087:  ret
}
");
        }
 
        [Fact]
        public void RealIndexersPreferredToPattern()
        {
            var src = @"
using System;
class C
{
    public int Length => throw null;
    public int this[Index i, int j = 0] { get { Console.WriteLine(""Index""); return 0; } }
    public int this[int i] { get { Console.WriteLine(""int""); return 0; } }    
    public int Slice(int i, int j) => throw null;
    public int this[Range r, int j = 0] { get { Console.WriteLine(""Range""); return 0; } }
 
    static void Main()
    {
        var c = new C();
        _ = c[0];
        _ = c[^0];
        _ = c[0..];
    }
}";
            var verifier = CompileAndVerifyWithIndexAndRange(src, expectedOutput: @"
int
Index
Range");
        }
 
        [Fact]
        public void PatternIndexList()
        {
            var src = @"
using System;
using System.Collections.Generic;
class C
{
    private static List<int> list = new List<int>() { 2, 4, 5, 6 };
    static void Main()
    {
        Console.WriteLine(list[^2]);
        var index = ^1;
        Console.WriteLine(list[index]);
    }
}";
            var verifier = CompileAndVerifyWithIndexAndRange(src, expectedOutput: @"5
6");
            verifier.VerifyIL("C.Main", @"
{
  // Code size       63 (0x3f)
  .maxstack  3
  .locals init (System.Index V_0, //index
                System.Collections.Generic.List<int> V_1)
  IL_0000:  ldsfld     ""System.Collections.Generic.List<int> C.list""
  IL_0005:  dup
  IL_0006:  callvirt   ""int System.Collections.Generic.List<int>.Count.get""
  IL_000b:  ldc.i4.2
  IL_000c:  sub
  IL_000d:  callvirt   ""int System.Collections.Generic.List<int>.this[int].get""
  IL_0012:  call       ""void System.Console.WriteLine(int)""
  IL_0017:  ldloca.s   V_0
  IL_0019:  ldc.i4.1
  IL_001a:  ldc.i4.1
  IL_001b:  call       ""System.Index..ctor(int, bool)""
  IL_0020:  ldsfld     ""System.Collections.Generic.List<int> C.list""
  IL_0025:  stloc.1
  IL_0026:  ldloc.1
  IL_0027:  ldloca.s   V_0
  IL_0029:  ldloc.1
  IL_002a:  callvirt   ""int System.Collections.Generic.List<int>.Count.get""
  IL_002f:  call       ""int System.Index.GetOffset(int)""
  IL_0034:  callvirt   ""int System.Collections.Generic.List<int>.this[int].get""
  IL_0039:  call       ""void System.Console.WriteLine(int)""
  IL_003e:  ret
}
");
        }
 
        [Theory]
        [InlineData("Length")]
        [InlineData("Count")]
        public void PatternRangeIndexers(string propertyName)
        {
            var src = @"
using System;
class C
{
    private int[] _f = { 2, 4, 5, 6 };
    public int " + propertyName + @" => _f.Length;
    public int[] Slice(int start, int length) => _f[start..length];
    static void Main()
    {
        var c = new C();
        foreach (var x in c[1..])
        {
            Console.WriteLine(x);
        }
        foreach (var x in c[..^2])
        {
            Console.WriteLine(x);
        }
    }
}";
            var verifier = CompileAndVerifyWithIndexAndRange(src, @"
4
5
2
4");
            verifier.VerifyIL("C.Main", @"
{
    // Code size       87 (0x57)
    .maxstack  4
    .locals init (C V_0, //c
                  int[] V_1,
                  int V_2,
                  C V_3)
    IL_0000:  newobj     ""C..ctor()""
    IL_0005:  stloc.0
    IL_0006:  ldloc.0
    IL_0007:  stloc.3
    IL_0008:  ldloc.3
    IL_0009:  ldc.i4.1
    IL_000a:  ldloc.3
    IL_000b:  callvirt   ""int C." + propertyName + @".get""
    IL_0010:  ldc.i4.1
    IL_0011:  sub
    IL_0012:  callvirt   ""int[] C.Slice(int, int)""
    IL_0017:  stloc.1
    IL_0018:  ldc.i4.0
    IL_0019:  stloc.2
    IL_001a:  br.s       IL_0028
    IL_001c:  ldloc.1
    IL_001d:  ldloc.2
    IL_001e:  ldelem.i4
    IL_001f:  call       ""void System.Console.WriteLine(int)""
    IL_0024:  ldloc.2
    IL_0025:  ldc.i4.1
    IL_0026:  add
    IL_0027:  stloc.2
    IL_0028:  ldloc.2
    IL_0029:  ldloc.1
    IL_002a:  ldlen
    IL_002b:  conv.i4
    IL_002c:  blt.s      IL_001c
    IL_002e:  ldloc.0
    IL_002f:  stloc.3
    IL_0030:  ldloc.3
    IL_0031:  ldc.i4.0
    IL_0032:  ldloc.3
    IL_0033:  callvirt   ""int C." + propertyName + @".get""
    IL_0038:  ldc.i4.2
    IL_0039:  sub
    IL_003a:  callvirt   ""int[] C.Slice(int, int)""
    IL_003f:  stloc.1
    IL_0040:  ldc.i4.0
    IL_0041:  stloc.2
    IL_0042:  br.s       IL_0050
    IL_0044:  ldloc.1
    IL_0045:  ldloc.2
    IL_0046:  ldelem.i4
    IL_0047:  call       ""void System.Console.WriteLine(int)""
    IL_004c:  ldloc.2
    IL_004d:  ldc.i4.1
    IL_004e:  add
    IL_004f:  stloc.2
    IL_0050:  ldloc.2
    IL_0051:  ldloc.1
    IL_0052:  ldlen
    IL_0053:  conv.i4
    IL_0054:  blt.s      IL_0044
    IL_0056:  ret
}
");
        }
 
        [Theory]
        [InlineData("Length")]
        [InlineData("Count")]
        public void PatternIndexIndexers(string propertyName)
        {
            var src = @"
using System;
class C
{
    private int[] _f = { 2, 4, 5, 6 };
    public int " + propertyName + @" => _f.Length;
    public int this[int x] => _f[x];
    static void Main()
    {
        var c = new C();
        Console.WriteLine(c[0]);
        Console.WriteLine(c[^1]);
    }
}";
            var verifier = CompileAndVerifyWithIndexAndRange(src, @"
2
6");
            verifier.VerifyIL("C.Main", @"
{
  // Code size       36 (0x24)
  .maxstack  3
  IL_0000:  newobj     ""C..ctor()""
  IL_0005:  dup
  IL_0006:  ldc.i4.0
  IL_0007:  callvirt   ""int C.this[int].get""
  IL_000c:  call       ""void System.Console.WriteLine(int)""
  IL_0011:  dup
  IL_0012:  callvirt   ""int C." + propertyName + @".get""
  IL_0017:  ldc.i4.1
  IL_0018:  sub
  IL_0019:  callvirt   ""int C.this[int].get""
  IL_001e:  call       ""void System.Console.WriteLine(int)""
  IL_0023:  ret
}
");
        }
 
        [Fact]
        public void RefToArrayIndexIndexer()
        {
            var verifier = CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        int[] x = { 0, 1, 2, 3 };
        M(x);
    }
 
    static void M(int[] x)
    {
        ref int r1 = ref x[2];
        Console.WriteLine(r1);
        ref int r2 = ref x[^2];
        Console.WriteLine(r2);
        r2 = 7;
        Console.WriteLine(r1);
        Console.WriteLine(r2);
        r1 = 5;
        Console.WriteLine(r1);
        Console.WriteLine(r2);
    }
}", expectedOutput: @"2
2
7
7
5
5");
            verifier.VerifyIL("C.M", @"
{
  // Code size       67 (0x43)
  .maxstack  4
  .locals init (int& V_0) //r2
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.2
  IL_0002:  ldelema    ""int""
  IL_0007:  dup
  IL_0008:  ldind.i4
  IL_0009:  call       ""void System.Console.WriteLine(int)""
  IL_000e:  ldarg.0
  IL_000f:  dup
  IL_0010:  ldlen
  IL_0011:  conv.i4
  IL_0012:  ldc.i4.2
  IL_0013:  sub
  IL_0014:  ldelema    ""int""
  IL_0019:  stloc.0
  IL_001a:  ldloc.0
  IL_001b:  ldind.i4
  IL_001c:  call       ""void System.Console.WriteLine(int)""
  IL_0021:  ldloc.0
  IL_0022:  ldc.i4.7
  IL_0023:  stind.i4
  IL_0024:  dup
  IL_0025:  ldind.i4
  IL_0026:  call       ""void System.Console.WriteLine(int)""
  IL_002b:  ldloc.0
  IL_002c:  ldind.i4
  IL_002d:  call       ""void System.Console.WriteLine(int)""
  IL_0032:  dup
  IL_0033:  ldc.i4.5
  IL_0034:  stind.i4
  IL_0035:  ldind.i4
  IL_0036:  call       ""void System.Console.WriteLine(int)""
  IL_003b:  ldloc.0
  IL_003c:  ldind.i4
  IL_003d:  call       ""void System.Console.WriteLine(int)""
  IL_0042:  ret
}");
        }
 
        [Fact]
        public void RangeIndexerStringIsFromEndStart()
        {
            CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        string s = ""abcdef"";
        Console.WriteLine(s[^2..]);
    }
}", expectedOutput: "ef");
        }
 
        [Fact]
        public void FakeRangeIndexerStringBothIsFromEnd()
        {
            CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        string s = ""abcdef"";
        Console.WriteLine(s[^4..^1]);
    }
}", expectedOutput: "cde");
        }
 
        [Fact]
        public void IndexIndexerStringTwoArgs()
        {
            var comp = CreateCompilationWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var s = ""abcdef"";
        M(s);
    }
    public static void M(string s)
    {
        Console.WriteLine(s[new Index(1, false)]);
        Console.WriteLine(s[new Index(1, false), ^1]);
    }
}");
            comp.VerifyDiagnostics(
                // (13,27): error CS1501: No overload for method 'this' takes 2 arguments
                //         Console.WriteLine(s[new Index(1, false), ^1]);
                Diagnostic(ErrorCode.ERR_BadArgCount, "s[new Index(1, false), ^1]").WithArguments("this", "2").WithLocation(13, 27));
        }
 
        [Fact]
        public void IndexIndexerArrayTwoArgs()
        {
            var comp = CreateCompilationWithIndex(@"
using System;
class C
{
    public static void Main()
    {
        var x = new int[1,1];
        M(x);
    }
    public static void M(int[,] s)
    {
        Console.WriteLine(s[new Index(1, false), ^1]);
    }
}");
            comp.VerifyDiagnostics(
                // (12,27): error CS0029: Cannot implicitly convert type 'System.Index' to 'int'
                //         Console.WriteLine(s[new Index(1, false), ^1]);
                Diagnostic(ErrorCode.ERR_NoImplicitConv, "new Index(1, false)").WithArguments("System.Index", "int").WithLocation(12, 29),
                // (12,27): error CS0029: Cannot implicitly convert type 'System.Index' to 'int'
                //         Console.WriteLine(s[new Index(1, false), ^1]);
                Diagnostic(ErrorCode.ERR_NoImplicitConv, "^1").WithArguments("System.Index", "int").WithLocation(12, 50));
        }
 
        [Fact]
        public void FakeIndexIndexerString()
        {
            var verifier = CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var s = ""abcdef"";
        int i = 1;
        Console.WriteLine(s[new Index(i, false)]);
        Console.WriteLine(s[(Index)2]);
        Console.WriteLine(s[^1]);
        Console.WriteLine(s[new Index(3, false)]);
        Console.WriteLine(s[new Index(2, true)]);
        i = 6;
        Console.WriteLine(s[new Index(i, true)]);
        Console.WriteLine(s[new Index(value: 2, fromEnd: false)]);
        Console.WriteLine(s[new Index(fromEnd: true, value: 3)]);
        Console.WriteLine(s[new Index(3, false) {}]);
    }
}", expectedOutput: @"b
c
f
d
e
a
c
d
d");
            verifier.VerifyIL("C.Main", @"
{
  // Code size      219 (0xdb)
  .maxstack  4
  .locals init (int V_0, //i
                string V_1,
                System.Index V_2)
  IL_0000:  ldstr      ""abcdef""
  IL_0005:  ldc.i4.1
  IL_0006:  stloc.0
  IL_0007:  dup
  IL_0008:  stloc.1
  IL_0009:  ldloc.1
  IL_000a:  ldloc.0
  IL_000b:  ldc.i4.0
  IL_000c:  newobj     ""System.Index..ctor(int, bool)""
  IL_0011:  stloc.2
  IL_0012:  ldloca.s   V_2
  IL_0014:  ldloc.1
  IL_0015:  callvirt   ""int string.Length.get""
  IL_001a:  call       ""int System.Index.GetOffset(int)""
  IL_001f:  callvirt   ""char string.this[int].get""
  IL_0024:  call       ""void System.Console.WriteLine(char)""
  IL_0029:  dup
  IL_002a:  ldc.i4.2
  IL_002b:  callvirt   ""char string.this[int].get""
  IL_0030:  call       ""void System.Console.WriteLine(char)""
  IL_0035:  dup
  IL_0036:  dup
  IL_0037:  callvirt   ""int string.Length.get""
  IL_003c:  ldc.i4.1
  IL_003d:  sub
  IL_003e:  callvirt   ""char string.this[int].get""
  IL_0043:  call       ""void System.Console.WriteLine(char)""
  IL_0048:  dup
  IL_0049:  ldc.i4.3
  IL_004a:  callvirt   ""char string.this[int].get""
  IL_004f:  call       ""void System.Console.WriteLine(char)""
  IL_0054:  dup
  IL_0055:  dup
  IL_0056:  callvirt   ""int string.Length.get""
  IL_005b:  ldc.i4.2
  IL_005c:  sub
  IL_005d:  callvirt   ""char string.this[int].get""
  IL_0062:  call       ""void System.Console.WriteLine(char)""
  IL_0067:  ldc.i4.6
  IL_0068:  stloc.0
  IL_0069:  dup
  IL_006a:  stloc.1
  IL_006b:  ldloc.1
  IL_006c:  ldloc.0
  IL_006d:  ldc.i4.1
  IL_006e:  newobj     ""System.Index..ctor(int, bool)""
  IL_0073:  stloc.2
  IL_0074:  ldloca.s   V_2
  IL_0076:  ldloc.1
  IL_0077:  callvirt   ""int string.Length.get""
  IL_007c:  call       ""int System.Index.GetOffset(int)""
  IL_0081:  callvirt   ""char string.this[int].get""
  IL_0086:  call       ""void System.Console.WriteLine(char)""
  IL_008b:  dup
  IL_008c:  ldc.i4.2
  IL_008d:  callvirt   ""char string.this[int].get""
  IL_0092:  call       ""void System.Console.WriteLine(char)""
  IL_0097:  dup
  IL_0098:  stloc.1
  IL_0099:  ldloc.1
  IL_009a:  ldc.i4.3
  IL_009b:  ldc.i4.1
  IL_009c:  newobj     ""System.Index..ctor(int, bool)""
  IL_00a1:  stloc.2
  IL_00a2:  ldloca.s   V_2
  IL_00a4:  ldloc.1
  IL_00a5:  callvirt   ""int string.Length.get""
  IL_00aa:  call       ""int System.Index.GetOffset(int)""
  IL_00af:  callvirt   ""char string.this[int].get""
  IL_00b4:  call       ""void System.Console.WriteLine(char)""
  IL_00b9:  stloc.1
  IL_00ba:  ldloc.1
  IL_00bb:  ldc.i4.3
  IL_00bc:  ldc.i4.0
  IL_00bd:  newobj     ""System.Index..ctor(int, bool)""
  IL_00c2:  stloc.2
  IL_00c3:  ldloca.s   V_2
  IL_00c5:  ldloc.1
  IL_00c6:  callvirt   ""int string.Length.get""
  IL_00cb:  call       ""int System.Index.GetOffset(int)""
  IL_00d0:  callvirt   ""char string.this[int].get""
  IL_00d5:  call       ""void System.Console.WriteLine(char)""
  IL_00da:  ret
}
");
        }
 
        [Fact]
        public void FakeRangeIndexerString()
        {
            var verifier = CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var s = ""abcdef"";
        Console.WriteLine(s[1..3]);
    }
}", expectedOutput: "bc");
            verifier.VerifyIL("C.Main", @"
{
    // Code size       18 (0x12)
    .maxstack  3
    IL_0000:  ldstr      ""abcdef""
    IL_0005:  ldc.i4.1
    IL_0006:  ldc.i4.2
    IL_0007:  callvirt   ""string string.Substring(int, int)""
    IL_000c:  call       ""void System.Console.WriteLine(string)""
    IL_0011:  ret
}");
        }
 
        [Fact, WorkItem(40776, "https://github.com/dotnet/roslyn/issues/40776")]
        public void FakeIndexIndexerOnDefaultStruct()
        {
            var verifier = CompileAndVerifyWithIndexAndRange(@"
using System;
 
struct NotASpan
{
    public int Length => 1;
 
    public int this[int index] => 0;
}
 
class C
{
    static int Repro() => default(NotASpan)[^0];
 
    static void Main() => Repro();
}");
 
            verifier.VerifyIL("C.Repro", @"
{
  // Code size       21 (0x15)
  .maxstack  2
  .locals init (NotASpan V_0)
  IL_0000:  ldloca.s   V_0
  IL_0002:  dup
  IL_0003:  initobj    ""NotASpan""
  IL_0009:  dup
  IL_000a:  call       ""int NotASpan.Length.get""
  IL_000f:  call       ""int NotASpan.this[int].get""
  IL_0014:  ret
}
");
        }
 
        [Fact, WorkItem(40776, "https://github.com/dotnet/roslyn/issues/40776")]
        public void FakeIndexIndexerOnStructConstructor()
        {
            var comp = CreateCompilationWithIndexAndRangeAndSpan(@"
using System;
 
class C
{
    static byte Repro() => new Span<byte>(new byte[] { })[^1];
}");
 
            var verifier = CompileAndVerify(comp);
 
            verifier.VerifyIL("C.Repro", @"
{
  // Code size       29 (0x1d)
  .maxstack  3
  .locals init (System.Span<byte> V_0)
  IL_0000:  ldc.i4.0
  IL_0001:  newarr     ""byte""
  IL_0006:  newobj     ""System.Span<byte>..ctor(byte[])""
  IL_000b:  stloc.0
  IL_000c:  ldloca.s   V_0
  IL_000e:  dup
  IL_000f:  call       ""int System.Span<byte>.Length.get""
  IL_0014:  ldc.i4.1
  IL_0015:  sub
  IL_0016:  call       ""ref byte System.Span<byte>.this[int].get""
  IL_001b:  ldind.u1
  IL_001c:  ret
}
");
        }
 
        [Fact]
        public void FakeRangeIndexerStringOpenEnd()
        {
            CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var s = ""abcdef"";
        var result = M(s);
        Console.WriteLine(result);
    }
    public static string M(string s) => s[1..];
}", expectedOutput: "bcdef");
        }
 
        [Fact]
        public void FakeRangeIndexerStringOpenStart()
        {
            CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var s = ""abcdef"";
        var result = M(s);
        Console.WriteLine(result);
    }
    public static string M(string s) => s[..^2];
}", expectedOutput: "abcd");
        }
 
        [Fact]
        public void FakeIndexIndexerArray()
        {
            var comp = CreateCompilationWithIndex(@"
using System;
class C
{
    public static void Main()
    {
        var x = new[] { 1, 2, 3, 11 };
        M(x);
    }
 
    public static void M(int[] array)
    {
        bool fromEnd = false;
        Console.WriteLine(array[new Index(1, fromEnd)]);
        Console.WriteLine(array[^1]);
    }
}", TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: @"2
11");
            verifier.VerifyIL("C.M", @"
{
  // Code size       42 (0x2a)
  .maxstack  3
  .locals init (bool V_0, //fromEnd
                int[] V_1,
                System.Index V_2)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  ldarg.0
  IL_0003:  stloc.1
  IL_0004:  ldloc.1
  IL_0005:  ldc.i4.1
  IL_0006:  ldloc.0
  IL_0007:  newobj     ""System.Index..ctor(int, bool)""
  IL_000c:  stloc.2
  IL_000d:  ldloca.s   V_2
  IL_000f:  ldloc.1
  IL_0010:  ldlen
  IL_0011:  conv.i4
  IL_0012:  call       ""int System.Index.GetOffset(int)""
  IL_0017:  ldelem.i4
  IL_0018:  call       ""void System.Console.WriteLine(int)""
  IL_001d:  ldarg.0
  IL_001e:  dup
  IL_001f:  ldlen
  IL_0020:  conv.i4
  IL_0021:  ldc.i4.1
  IL_0022:  sub
  IL_0023:  ldelem.i4
  IL_0024:  call       ""void System.Console.WriteLine(int)""
  IL_0029:  ret
}");
        }
 
        [Fact]
        public void SuppressNullableWarning_FakeIndexIndexerArray()
        {
            string source = @"
using System;
class C
{
    public static void Main()
    {
        var x = new[] { 1, 2, 3, 11 };
        M(x);
    }
 
    public static void M(int[] array)
    {
        Console.Write(array[new Index(1, false)!]);
        Console.Write(array[(^1)!]);
    }
}";
            // cover case in ConvertToArrayIndex
            var comp = CreateCompilationWithIndex(source, WithNullableEnable(TestOptions.DebugExe));
            comp.VerifyDiagnostics();
            CompileAndVerify(comp, expectedOutput: "211");
        }
 
        [Fact]
        public void FakeRangeIndexerArray()
        {
            var verifier = CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var arr = new[] { 1, 2, 3, 11 };
        var result = M(arr);
        Console.WriteLine(result.Length);
        foreach (var x in result)
        {
            Console.WriteLine(x);
        }
    }
    public static int[] M(int[] array) => array[1..3];
}", expectedOutput: @"2
2
3");
            verifier.VerifyIL("C.M", @"
{
  // Code size       24 (0x18)
  .maxstack  3
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.1
  IL_0002:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_0007:  ldc.i4.3
  IL_0008:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_000d:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_0012:  call       ""int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<int>(int[], System.Range)""
  IL_0017:  ret
}
");
        }
 
        [Fact]
        public void FakeRangeStartIsFromEndIndexerArray()
        {
            CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var arr = new[] { 1, 2, 3, 11 };
        var result = M(arr);
        Console.WriteLine(result.Length);
        foreach (var x in result)
        {
            Console.WriteLine(x);
        }
    }
    public static int[] M(int[] array) => array[^2..];
}", expectedOutput: @"2
3
11");
        }
 
        [Fact]
        public void FakeRangeBothIsFromEndIndexerArray()
        {
            CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var arr = new[] { 1, 2, 3, 11 };
        var result = M(arr);
        Console.WriteLine(result.Length);
        foreach (var x in result)
        {
            Console.WriteLine(x);
        }
    }
    public static int[] M(int[] array) => array[^3..^1];
}", expectedOutput: @"2
2
3");
        }
 
        [Fact]
        public void FakeRangeToEndIndexerArray()
        {
            var verifier = CompileAndVerifyWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var arr = new[] { 1, 2, 3, 11 };
        var result = M(arr);
        Console.WriteLine(result.Length);
        foreach (var x in result)
        {
            Console.WriteLine(x);
        }
    }
    public static int[] M(int[] array) => array[1..];
}", expectedOutput: @"3
2
3
11");
            verifier.VerifyIL("C.M", @"
{
  // Code size       18 (0x12)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.1
  IL_0002:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_0007:  call       ""System.Range System.Range.StartAt(System.Index)""
  IL_000c:  call       ""int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<int>(int[], System.Range)""
  IL_0011:  ret
}
");
        }
 
        [Fact]
        public void FakeRangeFromStartIndexerArray()
        {
            var comp = CreateCompilationWithIndexAndRange(@"
using System;
class C
{
    public static void Main()
    {
        var arr = new[] { 1, 2, 3, 11 };
        var result = M(arr);
        Console.WriteLine(result.Length);
        foreach (var x in result)
        {
            Console.WriteLine(x);
        }
    }
    public static int[] M(int[] array) => array[..3];
}" + TestSources.GetSubArray, TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, verify: Verification.Passes, expectedOutput: @"3
1
2
3");
            verifier.VerifyIL("C.M", @"
{
  // Code size       18 (0x12)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.3
  IL_0002:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_0007:  call       ""System.Range System.Range.EndAt(System.Index)""
  IL_000c:  call       ""int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<int>(int[], System.Range)""
  IL_0011:  ret
}");
        }
 
        [Fact]
        public void LowerIndex_Int()
        {
            var compilation = CreateCompilationWithIndex(@"
using System;
public static class Util
{
    public static Index Convert(int a) => ^a;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.Convert", @"
{
  // Code size        8 (0x8)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.1
  IL_0002:  newobj     ""System.Index..ctor(int, bool)""
  IL_0007:  ret
}");
        }
 
        [Fact]
        public void LowerIndex_NullableInt()
        {
            var compilation = CreateCompilationWithIndex(@"
using System;
public static class Util
{
    public static Index? Convert(int? a) => ^a;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.Convert", @"
{
  // Code size       40 (0x28)
  .maxstack  2
  .locals init (int? V_0,
                System.Index? V_1)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldloca.s   V_0
  IL_0004:  call       ""bool int?.HasValue.get""
  IL_0009:  brtrue.s   IL_0015
  IL_000b:  ldloca.s   V_1
  IL_000d:  initobj    ""System.Index?""
  IL_0013:  ldloc.1
  IL_0014:  ret
  IL_0015:  ldloca.s   V_0
  IL_0017:  call       ""int int?.GetValueOrDefault()""
  IL_001c:  ldc.i4.1
  IL_001d:  newobj     ""System.Index..ctor(int, bool)""
  IL_0022:  newobj     ""System.Index?..ctor(System.Index)""
  IL_0027:  ret
}");
        }
 
        [Fact]
        public void PrintIndexExpressions()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        int nonNullable = 1;
        int? nullableValue = 2;
        int? nullableDefault = default;
 
        Index a = nonNullable;
        Console.WriteLine(""a: "" + Print(a));
 
        Index b = ^nonNullable;
        Console.WriteLine(""b: "" + Print(b));
 
        // --------------------------------------------------------
        
        Index? c = nullableValue;
        Console.WriteLine(""c: "" + Print(c));
 
        Index? d = ^nullableValue;
        Console.WriteLine(""d: "" + Print(d));
 
        // --------------------------------------------------------
        
        Index? e = nullableDefault;
        Console.WriteLine(""e: "" + Print(e));
 
        Index? f = ^nullableDefault;
        Console.WriteLine(""f: "" + Print(f));
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe);
 
            CompileAndVerify(compilation, expectedOutput: @"
a: value: '1', fromEnd: 'False'
b: value: '1', fromEnd: 'True'
c: value: '2', fromEnd: 'False'
d: value: '2', fromEnd: 'True'
e: default
f: default");
        }
 
        [Fact]
        public void LowerRange_Create_Index_Index()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range Create(Index start, Index end) => start..end;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.Create", @"
{
  // Code size        8 (0x8)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_0007:  ret
}");
        }
 
        [Fact]
        public void LowerRange_Create_Index_NullableIndex()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range? Create(Index start, Index? end) => start..end;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.Create", @"
{
  // Code size       42 (0x2a)
  .maxstack  2
  .locals init (System.Index V_0,
                System.Index? V_1,
                System.Range? V_2)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldarg.1
  IL_0003:  stloc.1
  IL_0004:  ldloca.s   V_1
  IL_0006:  call       ""bool System.Index?.HasValue.get""
  IL_000b:  brtrue.s   IL_0017
  IL_000d:  ldloca.s   V_2
  IL_000f:  initobj    ""System.Range?""
  IL_0015:  ldloc.2
  IL_0016:  ret
  IL_0017:  ldloc.0
  IL_0018:  ldloca.s   V_1
  IL_001a:  call       ""System.Index System.Index?.GetValueOrDefault()""
  IL_001f:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_0024:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0029:  ret
}");
        }
 
        [Fact]
        public void LowerRange_Create_NullableIndex_Index()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range? Create(Index? start, Index end) => start..end;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.Create", @"
{
  // Code size       42 (0x2a)
  .maxstack  2
  .locals init (System.Index? V_0,
                System.Index V_1,
                System.Range? V_2)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldarg.1
  IL_0003:  stloc.1
  IL_0004:  ldloca.s   V_0
  IL_0006:  call       ""bool System.Index?.HasValue.get""
  IL_000b:  brtrue.s   IL_0017
  IL_000d:  ldloca.s   V_2
  IL_000f:  initobj    ""System.Range?""
  IL_0015:  ldloc.2
  IL_0016:  ret
  IL_0017:  ldloca.s   V_0
  IL_0019:  call       ""System.Index System.Index?.GetValueOrDefault()""
  IL_001e:  ldloc.1
  IL_001f:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_0024:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0029:  ret
}");
        }
 
        [Fact]
        public void LowerRange_Create_NullableIndex_NullableIndex()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range? Create(Index? start, Index? end) => start..end;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.Create", @"
{
  // Code size       56 (0x38)
  .maxstack  2
  .locals init (System.Index? V_0,
                System.Index? V_1,
                System.Range? V_2)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldarg.1
  IL_0003:  stloc.1
  IL_0004:  ldloca.s   V_0
  IL_0006:  call       ""bool System.Index?.HasValue.get""
  IL_000b:  ldloca.s   V_1
  IL_000d:  call       ""bool System.Index?.HasValue.get""
  IL_0012:  and
  IL_0013:  brtrue.s   IL_001f
  IL_0015:  ldloca.s   V_2
  IL_0017:  initobj    ""System.Range?""
  IL_001d:  ldloc.2
  IL_001e:  ret
  IL_001f:  ldloca.s   V_0
  IL_0021:  call       ""System.Index System.Index?.GetValueOrDefault()""
  IL_0026:  ldloca.s   V_1
  IL_0028:  call       ""System.Index System.Index?.GetValueOrDefault()""
  IL_002d:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_0032:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0037:  ret
}");
        }
 
        [Fact]
        public void LowerRange_ToEnd_Index()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range ToEnd(Index end) => ..end;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.ToEnd", @"
{
  // Code size        7 (0x7)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  call       ""System.Range System.Range.EndAt(System.Index)""
  IL_0006:  ret
}");
        }
 
        [Fact]
        public void LowerRange_ToEnd_NullableIndex()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range? ToEnd(Index? end) => ..end;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.ToEnd", @"
{
  // Code size       39 (0x27)
  .maxstack  1
  .locals init (System.Index? V_0,
                System.Range? V_1)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldloca.s   V_0
  IL_0004:  call       ""bool System.Index?.HasValue.get""
  IL_0009:  brtrue.s   IL_0015
  IL_000b:  ldloca.s   V_1
  IL_000d:  initobj    ""System.Range?""
  IL_0013:  ldloc.1
  IL_0014:  ret
  IL_0015:  ldloca.s   V_0
  IL_0017:  call       ""System.Index System.Index?.GetValueOrDefault()""
  IL_001c:  call       ""System.Range System.Range.EndAt(System.Index)""
  IL_0021:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0026:  ret
}");
        }
 
        [Fact]
        public void LowerRange_FromStart_Index()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range FromStart(Index start) => start..;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.FromStart", @"
{
  // Code size        7 (0x7)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  call       ""System.Range System.Range.StartAt(System.Index)""
  IL_0006:  ret
}");
        }
 
        [Fact]
        public void LowerRange_FromStart_NullableIndex()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range? FromStart(Index? start) => start..;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.FromStart", @"
{
  // Code size       39 (0x27)
  .maxstack  1
  .locals init (System.Index? V_0,
                System.Range? V_1)
  IL_0000:  ldarg.0
  IL_0001:  stloc.0
  IL_0002:  ldloca.s   V_0
  IL_0004:  call       ""bool System.Index?.HasValue.get""
  IL_0009:  brtrue.s   IL_0015
  IL_000b:  ldloca.s   V_1
  IL_000d:  initobj    ""System.Range?""
  IL_0013:  ldloc.1
  IL_0014:  ret
  IL_0015:  ldloca.s   V_0
  IL_0017:  call       ""System.Index System.Index?.GetValueOrDefault()""
  IL_001c:  call       ""System.Range System.Range.StartAt(System.Index)""
  IL_0021:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0026:  ret
}");
        }
 
        [Fact]
        public void LowerRange_All()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    public static Range All() => ..;
}");
 
            CompileAndVerify(compilation).VerifyIL("Util.All", @"
{
  // Code size        6 (0x6)
  .maxstack  1
  IL_0000:  call       ""System.Range System.Range.All.get""
  IL_0005:  ret
}");
        }
 
        [Fact]
        public void PrintRangeExpressions()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Index nonNullable = 1;
        Index? nullableValue = 2;
        Index? nullableDefault = default;
 
        Range a = nonNullable..nonNullable;
        Console.WriteLine(""a: "" + Print(a));
 
        Range? b = nonNullable..nullableValue;
        Console.WriteLine(""b: "" + Print(b));
 
        Range? c = nonNullable..nullableDefault;
        Console.WriteLine(""c: "" + Print(c));
 
        // --------------------------------------------------------
 
        Range? d = nullableValue..nonNullable;
        Console.WriteLine(""d: "" + Print(d));
 
        Range? e = nullableValue..nullableValue;
        Console.WriteLine(""e: "" + Print(e));
 
        Range? f = nullableValue..nullableDefault;
        Console.WriteLine(""f: "" + Print(f));
 
        // --------------------------------------------------------
 
        Range? g = nullableDefault..nonNullable;
        Console.WriteLine(""g: "" + Print(g));
 
        Range? h = nullableDefault..nullableValue;
        Console.WriteLine(""h: "" + Print(h));
 
        Range? i = nullableDefault..nullableDefault;
        Console.WriteLine(""i: "" + Print(i));
 
        // --------------------------------------------------------
 
        Range? j = ..nonNullable;
        Console.WriteLine(""j: "" + Print(j));
 
        Range? k = ..nullableValue;
        Console.WriteLine(""k: "" + Print(k));
 
        Range? l = ..nullableDefault;
        Console.WriteLine(""l: "" + Print(l));
 
        // --------------------------------------------------------
 
        Range? m = nonNullable..;
        Console.WriteLine(""m: "" + Print(m));
 
        Range? n = nullableValue..;
        Console.WriteLine(""n: "" + Print(n));
 
        Range? o = nullableDefault..;
        Console.WriteLine(""o: "" + Print(o));
 
        // --------------------------------------------------------
 
        Range? p = ..;
        Console.WriteLine(""p: "" + Print(p));
 
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe);
 
            CompileAndVerify(compilation, expectedOutput: @"
a: value: 'value: '1', fromEnd: 'False'', fromEnd: 'value: '1', fromEnd: 'False''
b: value: 'value: '1', fromEnd: 'False'', fromEnd: 'value: '2', fromEnd: 'False''
c: default
d: value: 'value: '2', fromEnd: 'False'', fromEnd: 'value: '1', fromEnd: 'False''
e: value: 'value: '2', fromEnd: 'False'', fromEnd: 'value: '2', fromEnd: 'False''
f: default
g: default
h: default
i: default
j: value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '1', fromEnd: 'False''
k: value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '2', fromEnd: 'False''
l: default
m: value: 'value: '1', fromEnd: 'False'', fromEnd: 'value: '0', fromEnd: 'True''
n: value: 'value: '2', fromEnd: 'False'', fromEnd: 'value: '0', fromEnd: 'True''
o: default
p: value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '0', fromEnd: 'True''");
        }
 
        [Fact]
        public void PassingAsArguments()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(^1));
        Console.WriteLine(Print(..));
        Console.WriteLine(Print(2..));
        Console.WriteLine(Print(..3));
        Console.WriteLine(Print(4..5));
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe).VerifyDiagnostics();
 
            CompileAndVerify(compilation, expectedOutput: @"
value: '1', fromEnd: 'True'
value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '0', fromEnd: 'True''
value: 'value: '2', fromEnd: 'False'', fromEnd: 'value: '0', fromEnd: 'True''
value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '3', fromEnd: 'False''
value: 'value: '4', fromEnd: 'False'', fromEnd: 'value: '5', fromEnd: 'False''");
        }
 
        [Fact]
        public void LowerRange_OrderOfEvaluation_Index_NullableIndex()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    static void Main()
    {
        var x = Create();
    }
 
    public static Range? Create()
    {
        return GetIndex1() .. GetIndex2();
    }
 
    static Index GetIndex1()
    {
        System.Console.WriteLine(""1"");
        return default;
    }
 
    static Index? GetIndex2()
    {
        System.Console.WriteLine(""2"");
        return new Index(1, true);
    }
}", options: TestOptions.DebugExe);
 
            CompileAndVerify(compilation, expectedOutput: @"
1
2").VerifyIL("Util.Create", @"
{
  // Code size       56 (0x38)
  .maxstack  2
  .locals init (System.Index V_0,
                System.Index? V_1,
                System.Range? V_2,
                System.Range? V_3)
  IL_0000:  nop
  IL_0001:  call       ""System.Index Util.GetIndex1()""
  IL_0006:  stloc.0
  IL_0007:  call       ""System.Index? Util.GetIndex2()""
  IL_000c:  stloc.1
  IL_000d:  ldloca.s   V_1
  IL_000f:  call       ""bool System.Index?.HasValue.get""
  IL_0014:  brtrue.s   IL_0021
  IL_0016:  ldloca.s   V_2
  IL_0018:  initobj    ""System.Range?""
  IL_001e:  ldloc.2
  IL_001f:  br.s       IL_0033
  IL_0021:  ldloc.0
  IL_0022:  ldloca.s   V_1
  IL_0024:  call       ""System.Index System.Index?.GetValueOrDefault()""
  IL_0029:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_002e:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0033:  stloc.3
  IL_0034:  br.s       IL_0036
  IL_0036:  ldloc.3
  IL_0037:  ret
}");
        }
 
        [Fact]
        public void LowerRange_OrderOfEvaluation_Index_Null()
        {
            var compilation = CreateCompilationWithIndexAndRange(@"
using System;
public static class Util
{
    static void Main()
    {
        var x = Create();
    }
 
    public static Range? Create()
    {
        return GetIndex1() .. GetIndex2();
    }
 
    static Index GetIndex1()
    {
        System.Console.WriteLine(""1"");
        return default;
    }
 
    static Index? GetIndex2()
    {
        System.Console.WriteLine(""2"");
        return null;
    }
}", options: TestOptions.DebugExe);
 
            CompileAndVerify(compilation, expectedOutput: @"
1
2").VerifyIL("Util.Create", @"
{
  // Code size       56 (0x38)
  .maxstack  2
  .locals init (System.Index V_0,
                System.Index? V_1,
                System.Range? V_2,
                System.Range? V_3)
  IL_0000:  nop
  IL_0001:  call       ""System.Index Util.GetIndex1()""
  IL_0006:  stloc.0
  IL_0007:  call       ""System.Index? Util.GetIndex2()""
  IL_000c:  stloc.1
  IL_000d:  ldloca.s   V_1
  IL_000f:  call       ""bool System.Index?.HasValue.get""
  IL_0014:  brtrue.s   IL_0021
  IL_0016:  ldloca.s   V_2
  IL_0018:  initobj    ""System.Range?""
  IL_001e:  ldloc.2
  IL_001f:  br.s       IL_0033
  IL_0021:  ldloc.0
  IL_0022:  ldloca.s   V_1
  IL_0024:  call       ""System.Index System.Index?.GetValueOrDefault()""
  IL_0029:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_002e:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0033:  stloc.3
  IL_0034:  br.s       IL_0036
  IL_0036:  ldloc.3
  IL_0037:  ret
}");
        }
 
        [Fact]
        public void Index_OperandConvertibleToInt()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        byte a = 3;
        Index b = ^a;
        Console.WriteLine(Print(b));
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe), expectedOutput: "value: '3', fromEnd: 'True'");
        }
 
        [Fact]
        public void Index_NullableAlwaysHasValue()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create()));
    }
    static Index? Create()
    {
        // should be lowered into: new Nullable<Index>(new Index(5, fromEnd: true))
        return ^new Nullable<int>(5);
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe),
                expectedOutput: "value: '5', fromEnd: 'True'")
                .VerifyIL("Program.Create", @"
{
  // Code size       13 (0xd)
  .maxstack  2
  IL_0000:  ldc.i4.5
  IL_0001:  ldc.i4.1
  IL_0002:  newobj     ""System.Index..ctor(int, bool)""
  IL_0007:  newobj     ""System.Index?..ctor(System.Index)""
  IL_000c:  ret
}");
        }
 
        [Fact]
        public void Range_NullableAlwaysHasValue_Left()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create(^1)));
    }
    static Range? Create(Index arg)
    {
        // should be lowered into: new Nullable<Range>(Range.FromStart(arg))
        return new Nullable<Index>(arg)..;
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe),
                expectedOutput: "value: 'value: '1', fromEnd: 'True'', fromEnd: 'value: '0', fromEnd: 'True''")
                .VerifyIL("Program.Create", @"
{
  // Code size       12 (0xc)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  call       ""System.Range System.Range.StartAt(System.Index)""
  IL_0006:  newobj     ""System.Range?..ctor(System.Range)""
  IL_000b:  ret
}");
        }
 
        [Fact]
        public void Range_NullableAlwaysHasValue_Right()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create(^1)));
    }
    static Range? Create(Index arg)
    {
        // should be lowered into: new Nullable<Range>(Range.ToEnd(arg))
        return ..new Nullable<Index>(arg);
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe),
                expectedOutput: "value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '1', fromEnd: 'True''")
                .VerifyIL("Program.Create", @"
{
  // Code size       12 (0xc)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  call       ""System.Range System.Range.EndAt(System.Index)""
  IL_0006:  newobj     ""System.Range?..ctor(System.Range)""
  IL_000b:  ret
}");
        }
 
        [Fact]
        public void Range_NullableAlwaysHasValue_Both()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create(^2, ^1)));
    }
    static Range? Create(Index arg1, Index arg2)
    {
        // should be lowered into: new Nullable<Range>(Range.Create(arg1, arg2))
        return new Nullable<Index>(arg1)..new Nullable<Index>(arg2);
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe),
                expectedOutput: "value: 'value: '2', fromEnd: 'True'', fromEnd: 'value: '1', fromEnd: 'True''")
                .VerifyIL("Program.Create", @"
{
  // Code size       13 (0xd)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldarg.1
  IL_0002:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_0007:  newobj     ""System.Range?..ctor(System.Range)""
  IL_000c:  ret
}");
        }
 
        [Fact]
        public void Index_NullableNeverHasValue()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create()));
    }
    static Index? Create()
    {
        // should be lowered into: new Nullable<Index>(new Index(default, fromEnd: true))
        return ^new Nullable<int>();
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe), expectedOutput: "value: '0', fromEnd: 'True'")
                .VerifyIL("Program.Create", @"
{
  // Code size       13 (0xd)
  .maxstack  2
  IL_0000:  ldc.i4.0
  IL_0001:  ldc.i4.1
  IL_0002:  newobj     ""System.Index..ctor(int, bool)""
  IL_0007:  newobj     ""System.Index?..ctor(System.Index)""
  IL_000c:  ret
}");
        }
 
        [Fact]
        public void Range_NullableNeverhasValue_Left()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create()));
    }
    static Range? Create()
    {
        // should be lowered into: new Nullable<Range>(Range.FromStart(default))
        return new Nullable<Index>()..;
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe),
                expectedOutput: "value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '0', fromEnd: 'True''")
                .VerifyIL("Program.Create", @"
{
  // Code size       20 (0x14)
  .maxstack  1
  .locals init (System.Index V_0)
  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    ""System.Index""
  IL_0008:  ldloc.0
  IL_0009:  call       ""System.Range System.Range.StartAt(System.Index)""
  IL_000e:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0013:  ret
}");
        }
 
        [Fact]
        public void Range_NullableNeverHasValue_Right()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create()));
    }
    static Range? Create()
    {
        // should be lowered into: new Nullable<Range>(Range.ToEnd(default))
        return ..new Nullable<Index>();
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe),
                expectedOutput: "value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '0', fromEnd: 'False''")
                .VerifyIL("Program.Create", @"
{
  // Code size       20 (0x14)
  .maxstack  1
  .locals init (System.Index V_0)
  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    ""System.Index""
  IL_0008:  ldloc.0
  IL_0009:  call       ""System.Range System.Range.EndAt(System.Index)""
  IL_000e:  newobj     ""System.Range?..ctor(System.Range)""
  IL_0013:  ret
}");
        }
 
        [Fact]
        public void Range_NullableNeverHasValue_Both()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create()));
    }
    static Range? Create()
    {
        // should be lowered into: new Nullable<Range>(Range.Create(default, default))
        return new Nullable<Index>()..new Nullable<Index>();
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe),
                expectedOutput: "value: 'value: '0', fromEnd: 'False'', fromEnd: 'value: '0', fromEnd: 'False''")
                .VerifyIL("Program.Create", @"
{
  // Code size       29 (0x1d)
  .maxstack  2
  .locals init (System.Index V_0)
  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    ""System.Index""
  IL_0008:  ldloc.0
  IL_0009:  ldloca.s   V_0
  IL_000b:  initobj    ""System.Index""
  IL_0011:  ldloc.0
  IL_0012:  newobj     ""System.Range..ctor(System.Index, System.Index)""
  IL_0017:  newobj     ""System.Range?..ctor(System.Range)""
  IL_001c:  ret
}");
        }
 
        [Fact]
        public void Index_OnFunctionCall()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(^Create(5)));
    }
    static int Create(int x) => x;
}" + PrintIndexesAndRangesCode,
                options: TestOptions.ReleaseExe),
                expectedOutput: "value: '5', fromEnd: 'True'");
        }
 
        [Fact]
        public void Range_OnFunctionCall()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create(1)..Create(2)));
    }
    static Index Create(int x) => ^x;
}" + PrintIndexesAndRangesCode,
                options: TestOptions.ReleaseExe),
                expectedOutput: "value: 'value: '1', fromEnd: 'True'', fromEnd: 'value: '2', fromEnd: 'True''");
        }
 
        [Fact]
        public void Index_OnAssignment()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        int x = default;
        Console.WriteLine(Print(^(x = Create(5))));
        Console.WriteLine(x);
    }
    static int Create(int x) => x;
}" + PrintIndexesAndRangesCode,
                options: TestOptions.ReleaseExe),
                expectedOutput: @"
value: '5', fromEnd: 'True'
5");
        }
 
        [Fact]
        public void Range_OnAssignment()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Index x = default, y = default;
        Console.WriteLine(Print((x = Create(1))..(y = Create(2))));
        Console.WriteLine(Print(x));
        Console.WriteLine(Print(y));
    }
    static Index Create(int x) => ^x;
}" + PrintIndexesAndRangesCode,
                options: TestOptions.ReleaseExe),
                expectedOutput: @"
value: 'value: '1', fromEnd: 'True'', fromEnd: 'value: '2', fromEnd: 'True''
value: '1', fromEnd: 'True'
value: '2', fromEnd: 'True'");
        }
 
        [Fact]
        public void Range_OnVarOut()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        Console.WriteLine(Print(Create(1, out Index y)..y));
    }
    static Index Create(int x, out Index y)
    {
        y = ^2;
        return ^x;
    }
}" + PrintIndexesAndRangesCode, options: TestOptions.ReleaseExe),
                expectedOutput: "value: 'value: '1', fromEnd: 'True'', fromEnd: 'value: '2', fromEnd: 'True''");
        }
 
        [Fact]
        public void Range_EvaluationInCondition()
        {
            CompileAndVerify(CreateCompilationWithIndexAndRange(@"
using System;
partial class Program
{
    static void Main()
    {
        if ((Create(1, out int a)..Create(2, out int b)).Start.IsFromEnd && a < b)
        {
            Console.WriteLine(""YES"");
        }
        if ((Create(4, out int c)..Create(3, out int d)).Start.IsFromEnd && c < d)
        {
            Console.WriteLine(""NO"");
        }
    }
    static Index Create(int x, out int y)
    {
        y = x;
        return ^x;
    }
}", options: TestOptions.ReleaseExe), expectedOutput: "YES");
        }
 
        private const string PrintIndexesAndRangesCode = @"
partial class Program
{
    static string Print(Index arg)
    {
        return $""value: '{arg.Value}', fromEnd: '{arg.IsFromEnd}'"";
    }
    static string Print(Range arg)
    {
        return $""value: '{Print(arg.Start)}', fromEnd: '{Print(arg.End)}'"";
    }
    static string Print(Index? arg)
    {
        if (arg.HasValue)
        {
            return Print(arg.Value);
        }
        else
        {
            return ""default"";
        }
    }
    static string Print(Range? arg)
    {
        if (arg.HasValue)
        {
            return Print(arg.Value);
        }
        else
        {
            return ""default"";
        }
    }
}";
 
        [Fact]
        [WorkItem(54085, "https://github.com/dotnet/roslyn/issues/54085")]
        public void OrderOfEvaluation_01()
        {
            var comp = CreateCompilationWithIndexAndRangeAndSpan(@"
using System;
 
class CollectionX
{
    private int[] _array = new[] { 1, 2, 3 };
 
    public int Length
    {
        get
        {
            Console.WriteLine(""Length"");
            return _array.Length;
        }
    }
 
    public ref int this[int index]
    {
        get
        {
            Console.WriteLine(""Indexer get"");
            return ref _array[index];
        }
    }
}
 
static class SideEffect
{
    static CollectionX Get()
    {
        Console.WriteLine(""Get"");
        return new CollectionX();
    }
    static int GetIdx()
    {
        Console.WriteLine(""GetIdx"");
        return 1;
    }
 
    public static void Main()
    {
        int i2 =  Get()[Index.FromEnd(GetIdx())];
        Console.WriteLine(i2);
    }
}
", TestOptions.ReleaseExe);
            CompileAndVerify(comp, expectedOutput:
@"Get
GetIdx
Length
Indexer get
3
");
        }
 
        [Fact]
        [WorkItem(54085, "https://github.com/dotnet/roslyn/issues/54085")]
        public void OrderOfEvaluation_02()
        {
            var comp = CreateCompilationWithIndexAndRangeAndSpan(@"
using System;
 
class CollectionX
{
    private int[] _array = new[] { 1, 2, 3 };
 
    public int Length
    {
        get
        {
            Console.WriteLine(""Length"");
            return _array.Length;
        }
    }
 
    public ref int this[int index]
    {
        get
        {
            Console.WriteLine(""Indexer get"");
            return ref _array[index];
        }
    }
}
 
static class SideEffect
{
    static CollectionX Get()
    {
        Console.WriteLine(""Get"");
        return new CollectionX();
    }
    static int GetIdx()
    {
        Console.WriteLine(""GetIdx"");
        return 1;
    }
 
    public static void Main()
    {
        int i2 =  Get()[^GetIdx()];
        Console.WriteLine(i2);
        Console.WriteLine();
        i2 =  Get()[GetIdx()];
        Console.WriteLine(i2);
        Console.WriteLine();
        i2 =  Get()[(Index)GetIdx()];
        Console.WriteLine(i2);
    }
}
", TestOptions.ReleaseExe);
            CompileAndVerify(comp, expectedOutput:
@"Get
GetIdx
Length
Indexer get
3
 
Get
GetIdx
Indexer get
2
 
Get
GetIdx
Indexer get
2
");
        }
 
        [Fact]
        [WorkItem(57349, "https://github.com/dotnet/roslyn/issues/57349")]
        public void OrderOfEvaluation_03()
        {
            var comp = CreateCompilationWithIndexAndRangeAndSpan(@"
using System;
 
class CollectionX
{
 
    public int Length
    {
        get
        {
            Console.Write("" Length"");
            return 4;
        }
    }
 
    public int Slice(int x, int y)
    {
        Console.Write("" Slice {0}, {1}"", x, y);
        return 111;
    }
}
 
static class SideEffect
{
    static CollectionX Get()
    {
        Console.WriteLine();
        Console.Write(""Get"");
        return new CollectionX();
    }
    static Range GetIdx()
    {
        Console.Write("" GetIdx"");
        return 1..3;
    }
    static int GetIdx1(int x)
    {
        Console.Write("" GetIdx1"");
        return x;
    }
    static int GetIdx2(int x)
    {
        Console.Write("" GetIdx2"");
        return x;
    }
    static Index GetIdx3(Index x)
    {
        Console.Write("" GetIdx3"");
        return x;
    }
    static Index GetIdx4(Index x)
    {
        Console.Write("" GetIdx4"");
        return x;
    }
 
    public static void Main()
    {
        _ = Get()[GetIdx()];
        _ = Get()[GetIdx1(1)..GetIdx2(3)];
        _ = Get()[^GetIdx1(3)..GetIdx2(3)];
        _ = Get()[GetIdx1(1)..^GetIdx2(1)];
        _ = Get()[^GetIdx1(3)..^GetIdx2(1)];
        _ = Get()[GetIdx3(1)..GetIdx4(3)];
        _ = Get()[^GetIdx1(3)..];
        _ = Get()[GetIdx1(1)..];
        _ = Get()[..GetIdx2(3)];
        _ = Get()[..^GetIdx2(1)];
        _ = Get()[..];
        _ = Get()[GetIdx3(1)..];
        _ = Get()[..GetIdx4(3)];
        _ = Get()[^GetIdx1(3)..GetIdx4(3)];
        _ = Get()[GetIdx1(1)..GetIdx4(3)];
        _ = Get()[GetIdx3(1)..GetIdx2(3)];
        _ = Get()[GetIdx3(1)..^GetIdx2(1)];
    }
}
", TestOptions.ReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput:
@"
Get GetIdx Length Slice 1, 2
Get GetIdx1 GetIdx2 Slice 1, 2
Get GetIdx1 GetIdx2 Length Slice 1, 2
Get GetIdx1 GetIdx2 Length Slice 1, 2
Get GetIdx1 GetIdx2 Length Slice 1, 2
Get GetIdx3 GetIdx4 Length Slice 1, 2
Get GetIdx1 Length Slice 1, 3
Get GetIdx1 Length Slice 1, 3
Get GetIdx2 Slice 0, 3
Get GetIdx2 Length Slice 0, 3
Get Length Slice 0, 4
Get GetIdx3 Length Slice 1, 3
Get GetIdx4 Length Slice 0, 3
Get GetIdx1 GetIdx4 Length Slice 1, 2
Get GetIdx1 GetIdx4 Length Slice 1, 2
Get GetIdx3 GetIdx2 Length Slice 1, 2
Get GetIdx3 GetIdx2 Length Slice 1, 2
");
            verifier.VerifyMethodBody("SideEffect.Main", @"
{
  // Code size      731 (0x2db)
  .maxstack  4
  .locals init (System.Range V_0,
                int V_1,
                int V_2,
                int V_3,
                System.Index V_4,
                CollectionX V_5,
                int V_6,
                System.Index V_7)
  // sequence point: _ = Get()[GetIdx()];
  IL_0000:  call       ""CollectionX SideEffect.Get()""
  IL_0005:  call       ""System.Range SideEffect.GetIdx()""
  IL_000a:  stloc.0
  IL_000b:  dup
  IL_000c:  callvirt   ""int CollectionX.Length.get""
  IL_0011:  stloc.1
  IL_0012:  ldloca.s   V_0
  IL_0014:  call       ""System.Index System.Range.Start.get""
  IL_0019:  stloc.s    V_4
  IL_001b:  ldloca.s   V_4
  IL_001d:  ldloc.1
  IL_001e:  call       ""int System.Index.GetOffset(int)""
  IL_0023:  stloc.2
  IL_0024:  ldloca.s   V_0
  IL_0026:  call       ""System.Index System.Range.End.get""
  IL_002b:  stloc.s    V_4
  IL_002d:  ldloca.s   V_4
  IL_002f:  ldloc.1
  IL_0030:  call       ""int System.Index.GetOffset(int)""
  IL_0035:  ldloc.2
  IL_0036:  sub
  IL_0037:  stloc.3
  IL_0038:  ldloc.2
  IL_0039:  ldloc.3
  IL_003a:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_003f:  pop
  // sequence point: _ = Get()[GetIdx1(1)..GetIdx2(3)];
  IL_0040:  call       ""CollectionX SideEffect.Get()""
  IL_0045:  ldc.i4.1
  IL_0046:  call       ""int SideEffect.GetIdx1(int)""
  IL_004b:  stloc.3
  IL_004c:  ldloc.3
  IL_004d:  ldc.i4.3
  IL_004e:  call       ""int SideEffect.GetIdx2(int)""
  IL_0053:  ldloc.3
  IL_0054:  sub
  IL_0055:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_005a:  pop
  // sequence point: _ = Get()[^GetIdx1(3)..GetIdx2(3)];
  IL_005b:  call       ""CollectionX SideEffect.Get()""
  IL_0060:  ldc.i4.3
  IL_0061:  call       ""int SideEffect.GetIdx1(int)""
  IL_0066:  stloc.3
  IL_0067:  ldc.i4.3
  IL_0068:  call       ""int SideEffect.GetIdx2(int)""
  IL_006d:  stloc.2
  IL_006e:  dup
  IL_006f:  callvirt   ""int CollectionX.Length.get""
  IL_0074:  ldloc.3
  IL_0075:  sub
  IL_0076:  stloc.1
  IL_0077:  ldloc.1
  IL_0078:  ldloc.2
  IL_0079:  ldloc.1
  IL_007a:  sub
  IL_007b:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_0080:  pop
  // sequence point: _ = Get()[GetIdx1(1)..^GetIdx2(1)];
  IL_0081:  call       ""CollectionX SideEffect.Get()""
  IL_0086:  stloc.s    V_5
  IL_0088:  ldc.i4.1
  IL_0089:  call       ""int SideEffect.GetIdx1(int)""
  IL_008e:  stloc.1
  IL_008f:  ldc.i4.1
  IL_0090:  call       ""int SideEffect.GetIdx2(int)""
  IL_0095:  stloc.2
  IL_0096:  ldloc.s    V_5
  IL_0098:  ldloc.1
  IL_0099:  ldloc.s    V_5
  IL_009b:  callvirt   ""int CollectionX.Length.get""
  IL_00a0:  ldloc.2
  IL_00a1:  sub
  IL_00a2:  ldloc.1
  IL_00a3:  sub
  IL_00a4:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_00a9:  pop
  // sequence point: _ = Get()[^GetIdx1(3)..^GetIdx2(1)];
  IL_00aa:  call       ""CollectionX SideEffect.Get()""
  IL_00af:  ldc.i4.3
  IL_00b0:  call       ""int SideEffect.GetIdx1(int)""
  IL_00b5:  stloc.2
  IL_00b6:  ldc.i4.1
  IL_00b7:  call       ""int SideEffect.GetIdx2(int)""
  IL_00bc:  stloc.1
  IL_00bd:  dup
  IL_00be:  callvirt   ""int CollectionX.Length.get""
  IL_00c3:  stloc.3
  IL_00c4:  ldloc.3
  IL_00c5:  ldloc.2
  IL_00c6:  sub
  IL_00c7:  stloc.s    V_6
  IL_00c9:  ldloc.s    V_6
  IL_00cb:  ldloc.3
  IL_00cc:  ldloc.1
  IL_00cd:  sub
  IL_00ce:  ldloc.s    V_6
  IL_00d0:  sub
  IL_00d1:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_00d6:  pop
  // sequence point: _ = Get()[GetIdx3(1)..GetIdx4(3)];
  IL_00d7:  call       ""CollectionX SideEffect.Get()""
  IL_00dc:  ldc.i4.1
  IL_00dd:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_00e2:  call       ""System.Index SideEffect.GetIdx3(System.Index)""
  IL_00e7:  stloc.s    V_4
  IL_00e9:  ldc.i4.3
  IL_00ea:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_00ef:  call       ""System.Index SideEffect.GetIdx4(System.Index)""
  IL_00f4:  stloc.s    V_7
  IL_00f6:  dup
  IL_00f7:  callvirt   ""int CollectionX.Length.get""
  IL_00fc:  stloc.s    V_6
  IL_00fe:  ldloca.s   V_4
  IL_0100:  ldloc.s    V_6
  IL_0102:  call       ""int System.Index.GetOffset(int)""
  IL_0107:  stloc.3
  IL_0108:  ldloc.3
  IL_0109:  ldloca.s   V_7
  IL_010b:  ldloc.s    V_6
  IL_010d:  call       ""int System.Index.GetOffset(int)""
  IL_0112:  ldloc.3
  IL_0113:  sub
  IL_0114:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_0119:  pop
  // sequence point: _ = Get()[^GetIdx1(3)..];
  IL_011a:  call       ""CollectionX SideEffect.Get()""
  IL_011f:  ldc.i4.3
  IL_0120:  call       ""int SideEffect.GetIdx1(int)""
  IL_0125:  stloc.3
  IL_0126:  dup
  IL_0127:  callvirt   ""int CollectionX.Length.get""
  IL_012c:  stloc.s    V_6
  IL_012e:  ldloc.s    V_6
  IL_0130:  ldloc.3
  IL_0131:  sub
  IL_0132:  stloc.1
  IL_0133:  ldloc.1
  IL_0134:  ldloc.s    V_6
  IL_0136:  ldloc.1
  IL_0137:  sub
  IL_0138:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_013d:  pop
  // sequence point: _ = Get()[GetIdx1(1)..];
  IL_013e:  call       ""CollectionX SideEffect.Get()""
  IL_0143:  stloc.s    V_5
  IL_0145:  ldc.i4.1
  IL_0146:  call       ""int SideEffect.GetIdx1(int)""
  IL_014b:  stloc.1
  IL_014c:  ldloc.s    V_5
  IL_014e:  ldloc.1
  IL_014f:  ldloc.s    V_5
  IL_0151:  callvirt   ""int CollectionX.Length.get""
  IL_0156:  ldloc.1
  IL_0157:  sub
  IL_0158:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_015d:  pop
  // sequence point: _ = Get()[..GetIdx2(3)];
  IL_015e:  call       ""CollectionX SideEffect.Get()""
  IL_0163:  ldc.i4.0
  IL_0164:  ldc.i4.3
  IL_0165:  call       ""int SideEffect.GetIdx2(int)""
  IL_016a:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_016f:  pop
  // sequence point: _ = Get()[..^GetIdx2(1)];
  IL_0170:  call       ""CollectionX SideEffect.Get()""
  IL_0175:  stloc.s    V_5
  IL_0177:  ldc.i4.1
  IL_0178:  call       ""int SideEffect.GetIdx2(int)""
  IL_017d:  stloc.1
  IL_017e:  ldloc.s    V_5
  IL_0180:  ldc.i4.0
  IL_0181:  ldloc.s    V_5
  IL_0183:  callvirt   ""int CollectionX.Length.get""
  IL_0188:  ldloc.1
  IL_0189:  sub
  IL_018a:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_018f:  pop
  // sequence point: _ = Get()[..];
  IL_0190:  call       ""CollectionX SideEffect.Get()""
  IL_0195:  stloc.s    V_5
  IL_0197:  ldloc.s    V_5
  IL_0199:  ldc.i4.0
  IL_019a:  ldloc.s    V_5
  IL_019c:  callvirt   ""int CollectionX.Length.get""
  IL_01a1:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_01a6:  pop
  // sequence point: _ = Get()[GetIdx3(1)..];
  IL_01a7:  call       ""CollectionX SideEffect.Get()""
  IL_01ac:  ldc.i4.1
  IL_01ad:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_01b2:  call       ""System.Index SideEffect.GetIdx3(System.Index)""
  IL_01b7:  stloc.s    V_7
  IL_01b9:  dup
  IL_01ba:  callvirt   ""int CollectionX.Length.get""
  IL_01bf:  stloc.1
  IL_01c0:  ldloca.s   V_7
  IL_01c2:  ldloc.1
  IL_01c3:  call       ""int System.Index.GetOffset(int)""
  IL_01c8:  stloc.s    V_6
  IL_01ca:  ldloc.s    V_6
  IL_01cc:  ldloc.1
  IL_01cd:  ldloc.s    V_6
  IL_01cf:  sub
  IL_01d0:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_01d5:  pop
  // sequence point: _ = Get()[..GetIdx4(3)];
  IL_01d6:  call       ""CollectionX SideEffect.Get()""
  IL_01db:  stloc.s    V_5
  IL_01dd:  ldloc.s    V_5
  IL_01df:  ldc.i4.0
  IL_01e0:  ldc.i4.3
  IL_01e1:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_01e6:  call       ""System.Index SideEffect.GetIdx4(System.Index)""
  IL_01eb:  stloc.s    V_7
  IL_01ed:  ldloca.s   V_7
  IL_01ef:  ldloc.s    V_5
  IL_01f1:  callvirt   ""int CollectionX.Length.get""
  IL_01f6:  call       ""int System.Index.GetOffset(int)""
  IL_01fb:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_0200:  pop
  // sequence point: _ = Get()[^GetIdx1(3)..GetIdx4(3)];
  IL_0201:  call       ""CollectionX SideEffect.Get()""
  IL_0206:  ldc.i4.3
  IL_0207:  call       ""int SideEffect.GetIdx1(int)""
  IL_020c:  stloc.s    V_6
  IL_020e:  ldc.i4.3
  IL_020f:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_0214:  call       ""System.Index SideEffect.GetIdx4(System.Index)""
  IL_0219:  stloc.s    V_7
  IL_021b:  dup
  IL_021c:  callvirt   ""int CollectionX.Length.get""
  IL_0221:  stloc.1
  IL_0222:  ldloc.1
  IL_0223:  ldloc.s    V_6
  IL_0225:  sub
  IL_0226:  stloc.3
  IL_0227:  ldloc.3
  IL_0228:  ldloca.s   V_7
  IL_022a:  ldloc.1
  IL_022b:  call       ""int System.Index.GetOffset(int)""
  IL_0230:  ldloc.3
  IL_0231:  sub
  IL_0232:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_0237:  pop
  // sequence point: _ = Get()[GetIdx1(1)..GetIdx4(3)];
  IL_0238:  call       ""CollectionX SideEffect.Get()""
  IL_023d:  stloc.s    V_5
  IL_023f:  ldc.i4.1
  IL_0240:  call       ""int SideEffect.GetIdx1(int)""
  IL_0245:  stloc.3
  IL_0246:  ldloc.s    V_5
  IL_0248:  ldloc.3
  IL_0249:  ldc.i4.3
  IL_024a:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_024f:  call       ""System.Index SideEffect.GetIdx4(System.Index)""
  IL_0254:  stloc.s    V_7
  IL_0256:  ldloca.s   V_7
  IL_0258:  ldloc.s    V_5
  IL_025a:  callvirt   ""int CollectionX.Length.get""
  IL_025f:  call       ""int System.Index.GetOffset(int)""
  IL_0264:  ldloc.3
  IL_0265:  sub
  IL_0266:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_026b:  pop
  // sequence point: _ = Get()[GetIdx3(1)..GetIdx2(3)];
  IL_026c:  call       ""CollectionX SideEffect.Get()""
  IL_0271:  stloc.s    V_5
  IL_0273:  ldc.i4.1
  IL_0274:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_0279:  call       ""System.Index SideEffect.GetIdx3(System.Index)""
  IL_027e:  stloc.s    V_7
  IL_0280:  ldc.i4.3
  IL_0281:  call       ""int SideEffect.GetIdx2(int)""
  IL_0286:  stloc.3
  IL_0287:  ldloca.s   V_7
  IL_0289:  ldloc.s    V_5
  IL_028b:  callvirt   ""int CollectionX.Length.get""
  IL_0290:  call       ""int System.Index.GetOffset(int)""
  IL_0295:  stloc.1
  IL_0296:  ldloc.s    V_5
  IL_0298:  ldloc.1
  IL_0299:  ldloc.3
  IL_029a:  ldloc.1
  IL_029b:  sub
  IL_029c:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_02a1:  pop
  // sequence point: _ = Get()[GetIdx3(1)..^GetIdx2(1)];
  IL_02a2:  call       ""CollectionX SideEffect.Get()""
  IL_02a7:  ldc.i4.1
  IL_02a8:  call       ""System.Index System.Index.op_Implicit(int)""
  IL_02ad:  call       ""System.Index SideEffect.GetIdx3(System.Index)""
  IL_02b2:  stloc.s    V_7
  IL_02b4:  ldc.i4.1
  IL_02b5:  call       ""int SideEffect.GetIdx2(int)""
  IL_02ba:  stloc.1
  IL_02bb:  dup
  IL_02bc:  callvirt   ""int CollectionX.Length.get""
  IL_02c1:  stloc.3
  IL_02c2:  ldloca.s   V_7
  IL_02c4:  ldloc.3
  IL_02c5:  call       ""int System.Index.GetOffset(int)""
  IL_02ca:  stloc.s    V_6
  IL_02cc:  ldloc.s    V_6
  IL_02ce:  ldloc.3
  IL_02cf:  ldloc.1
  IL_02d0:  sub
  IL_02d1:  ldloc.s    V_6
  IL_02d3:  sub
  IL_02d4:  callvirt   ""int CollectionX.Slice(int, int)""
  IL_02d9:  pop
  // sequence point: }
  IL_02da:  ret
}
");
        }
 
        [Fact, WorkItem(57745, "https://github.com/dotnet/roslyn/issues/57745")]
        public void ObsoleteRangeType()
        {
            var source = @"
_ = new C()[..];
 
class C
{
    public int Length => 0;
    public int this[int i] => 0;
    public int Slice(int i, int j) => 0;
}
 
namespace System
{
    [Obsolete]
    public readonly struct Range
    {
        public Index Start { get; }
 
        public Index End { get; }
 
        public Range(Index start, Index end) => throw null;
 
        public static Range StartAt(Index start) => throw null;
 
        public static Range EndAt(Index end) => throw null;
 
        public static Range All => throw null;
    }
}
";
            // Note: we don't have to report Obsolete diagnostic on well-known types/members
            var comp = CreateCompilation(new[] { source, TestSources.Index });
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void ImplicitIndexerAccessAsLValue()
        {
            var source = @"
#nullable enable
 
object? o1 = new object();
M(o1)[^1] = null; // 1
M(o1)[..] = null; // 2
 
_ = M(o1)[^1].ToString();
_ = M(o1)[..].ToString();
 
object o2 = null; // 3
M(o2)[^1] = null;
M(o2)[..] = null;
 
_ = M(o2)[^1].ToString(); // 4
_ = M(o2)[..].ToString(); // 5
 
static C<T> M<T>(T t) => throw null!;
 
class C<T>
{
    public int Length => 0;
    public ref T this[int i] => throw null!;
    public ref T Slice(int i, int j) => throw null!;
}
";
            var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range });
            comp.VerifyDiagnostics(
                // (5,13): warning CS8625: Cannot convert null literal to non-nullable reference type.
                // M(o1)[^1] = null; // 1
                Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 13),
                // (6,13): warning CS8625: Cannot convert null literal to non-nullable reference type.
                // M(o1)[..] = null; // 2
                Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(6, 13),
                // (11,13): warning CS8600: Converting null literal or possible null value to non-nullable type.
                // object o2 = null; // 3
                Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(11, 13),
                // (15,5): warning CS8602: Dereference of a possibly null reference.
                // _ = M(o2)[^1].ToString(); // 4
                Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(o2)[^1]").WithLocation(15, 5),
                // (16,5): warning CS8602: Dereference of a possibly null reference.
                // _ = M(o2)[..].ToString(); // 5
                Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(o2)[..]").WithLocation(16, 5)
                );
        }
 
        [Fact]
        public void NullableIndexerArgument()
        {
            var source = @"
#nullable enable
var c = new C();
 
object o1 = null;
_ = c[M1(o1.ToString())];
 
object o2 = null;
_ = c[M2(o2.ToString())];
 
static System.Index M1(object? o) => throw null!;
static System.Range M2(object? o) => throw null!;
 
class C
{
    public int Length => 0;
    public int this[int i] => throw null!;
    public int Slice(int i, int j) => throw null!;
}
";
            var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range });
            comp.VerifyDiagnostics(
                // (5,13): warning CS8600: Converting null literal or possible null value to non-nullable type.
                // object o1 = null;
                Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(5, 13),
                // (6,10): warning CS8602: Dereference of a possibly null reference.
                // _ = c[M1(o1.ToString())];
                Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o1").WithLocation(6, 10),
                // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type.
                // object o2 = null;
                Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 13),
                // (9,10): warning CS8602: Dereference of a possibly null reference.
                // _ = c[M2(o2.ToString())];
                Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o2").WithLocation(9, 10)
                );
        }
 
        [Fact]
        public void SemanticModelOnReceiver()
        {
            var source = @"
var c = new C();
 
_ = c[^1];
_ = c[..];
 
class C
{
    public int Length => 0;
    public int this[int i] => throw null!;
    public int Slice(int i, int j) => throw null!;
}
";
            var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range });
            comp.VerifyDiagnostics();
 
            var tree = comp.SyntaxTrees.First();
            var receivers = tree.GetRoot().DescendantNodes().OfType<ElementAccessExpressionSyntax>().Select(e => e.Expression).ToArray();
            Assert.Equal(2, receivers.Length);
            var model = comp.GetSemanticModel(tree, ignoreAccessibility: false);
 
            Assert.Equal("c", receivers[0].ToString());
            Assert.Equal("C", model.GetTypeInfo(receivers[0]).Type.ToTestDisplayString());
 
            Assert.Equal("c", receivers[1].ToString());
            Assert.Equal("C", model.GetTypeInfo(receivers[1]).Type.ToTestDisplayString());
        }
 
        [Fact]
        public void Nullable_OrderOfEvaluation()
        {
            var source = @"
#nullable enable
C? c = null;
_ = (c = new C())[M1(c.ToString())];
 
c = null;
_ = (c = new C())[M2(c.ToString())];
 
c = null;
_ = c[M1((c = new C()).ToString())]; // 1
 
string? s = null;
_ = (s = string.Empty)[M1(s.ToString())];
 
s = null;
_ = (s = string.Empty)[M2(s.ToString())];
 
int[]? a = null;
_ = (a = new[] { 1 })[M1(a.ToString())];
 
a = null;
_ = (a = new[] { 1 })[M2(a.ToString())];
 
static System.Index M1(object? o) => throw null!;
static System.Range M2(object? o) => throw null!;
 
class C
{
    public int Length => 0;
    public int this[int i] => throw null!;
    public int Slice(int i, int j) => throw null!;
}
";
            var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.GetSubArray });
            comp.VerifyDiagnostics(
                // (10,5): warning CS8602: Dereference of a possibly null reference.
                // _ = c[M1((c = new C()).ToString())]; // 1
                Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c").WithLocation(10, 5)
                );
        }
 
        [Fact]
        public void PatternIndexArrayAndAwait_01()
        {
            var src = @"
class C
{
    static async System.Threading.Tasks.Task M1(int[] arr)
    {
        arr[^1] = await System.Threading.Tasks.Task.FromResult(0);
    }
 
    static async System.Threading.Tasks.Task Main()
    {
        var arr = new[] { 123 };
        System.Console.WriteLine(arr[0]);
        await M1(arr);
        System.Console.WriteLine(arr[0]);
    }
}
";
            CompileAndVerifyWithIndexAndRange(src, expectedOutput:
@"
123
0
").VerifyDiagnostics();
        }
 
        [Fact]
        public void PatternIndexArrayAndAwait_02()
        {
            var src = @"
class C
{
    static async System.Threading.Tasks.Task M1(int[] arr)
    {
        (arr[^1], arr[0]) = (123, await System.Threading.Tasks.Task.FromResult(124));
    }
 
    static async System.Threading.Tasks.Task Main()
    {
        var arr = new int[2];
        await M1(arr);
        System.Console.WriteLine(arr[0]);
        System.Console.WriteLine(arr[1]);
    }
}
";
            CompileAndVerifyWithIndexAndRange(src, expectedOutput:
@"
124
123
").VerifyDiagnostics();
        }
 
        [Fact]
        public void PatternIndexArrayAndAwait_03()
        {
            var src = @"
class C
{
    static async System.Threading.Tasks.Task M1((int x, int y)[] arr)
    {
        arr[^1].x = await System.Threading.Tasks.Task.FromResult(124);
    }
 
    static async System.Threading.Tasks.Task Main()
    {
        var arr = new (int x, int y)[1];
        await M1(arr);
        System.Console.WriteLine(arr[0].x);
    }
}
";
            CompileAndVerifyWithIndexAndRange(src, expectedOutput:
@"
124
").VerifyDiagnostics();
        }
 
        [Fact]
        [WorkItem(58569, "https://github.com/dotnet/roslyn/issues/58569")]
        public void PatternIndexArrayAndAwait_04()
        {
            var src = @"
class C
{
    static async System.Threading.Tasks.Task M1((int x, int y)[] arr)
    {
        (arr[^1].x, arr[0].y) = (123, await System.Threading.Tasks.Task.FromResult(124));
    }
 
    static async System.Threading.Tasks.Task Main()
    {
        var arr = new (int x, int y)[2];
        await M1(arr);
        System.Console.WriteLine(arr[0].y);
        System.Console.WriteLine(arr[1].x);
    }
}
";
            CompileAndVerifyWithIndexAndRange(src, expectedOutput:
@"
124
123
").VerifyDiagnostics();
        }
 
        [Fact]
        [WorkItem(58569, "https://github.com/dotnet/roslyn/issues/58569")]
        public void PatternIndexArrayAndAwait_05()
        {
            var src = @"
class C
{
    static async System.Threading.Tasks.Task M1((int x, int y)[] arr)
    {
        arr[^1].x += await System.Threading.Tasks.Task.FromResult(124);
    }
 
    static async System.Threading.Tasks.Task Main()
    {
        var arr = new (int x, int y)[] { (1, 2) };
        await M1(arr);
        System.Console.WriteLine(arr[0].x);
    }
}
";
            CompileAndVerifyWithIndexAndRange(src, expectedOutput:
@"
125
").VerifyDiagnostics();
        }
 
        [Fact]
        public void PatternIndexArrayAndAwait_06()
        {
            var src = @"
class C
{
    static async System.Threading.Tasks.Task M1(int[] arr)
    {
        arr[^1] += await System.Threading.Tasks.Task.FromResult(124);
    }
 
    static async System.Threading.Tasks.Task Main()
    {
        var arr = new int[] { 1 };
        await M1(arr);
        System.Console.WriteLine(arr[0]);
    }
}
";
            CompileAndVerifyWithIndexAndRange(src, expectedOutput:
@"
125
").VerifyDiagnostics();
        }
 
        [Fact]
        public void PatternIndexArrayAndAwait_07()
        {
            var src = @"
class C
{
    static async System.Threading.Tasks.Task M1(int[][] arr)
    {
        arr[^1][0] += await System.Threading.Tasks.Task.FromResult(124);
    }
 
    static async System.Threading.Tasks.Task Main()
    {
        var arr = new[] { new[] { 1 } };
        await M1(arr);
        System.Console.WriteLine(arr[0][0]);
    }
}
";
            CompileAndVerifyWithIndexAndRange(src, expectedOutput:
@"
125
").VerifyDiagnostics();
        }
 
        [Fact]
        public void PatternIndexArrayAndAwait_08()
        {
            var src = @"
class C
{
    static async System.Threading.Tasks.Task M1((int x, int y)[] arr)
    {
        (arr[1..][^1].x, arr[1..][0].y) = (123, await System.Threading.Tasks.Task.FromResult(124));
    }
 
    static async System.Threading.Tasks.Task Main()
    {
        var arr = new (int x, int y)[5];
        await M1(arr);
        System.Console.WriteLine(""Done"");
    }
}
";
            CompileAndVerifyWithIndexAndRange(src, expectedOutput: "Done").VerifyDiagnostics();
        }
 
        [Fact]
        [WorkItem(65586, "https://github.com/dotnet/roslyn/issues/65586")]
        public void SideeffectsOfSlicing_01()
        {
            var src = @"
using System;
 
class Program
{
    static S s = new S();
    
    static void Main()
    {
        Test();
        Console.WriteLine(s.count); 
    }
 
    static void Test()
    {
        _ = s[1..2];
    }
}
 
struct S
{
    public int count;
    
    public int Slice(int start, int length)
    {
        count++;
        return 0;
    }
    
    public int Length => 10;
}";
            var verifier = CompileAndVerifyWithIndexAndRange(src, expectedOutput: "1").VerifyDiagnostics();
 
            verifier.VerifyIL("Program.Test",
@"
{
  // Code size       14 (0xe)
  .maxstack  3
  IL_0000:  ldsflda    ""S Program.s""
  IL_0005:  ldc.i4.1
  IL_0006:  ldc.i4.1
  IL_0007:  call       ""int S.Slice(int, int)""
  IL_000c:  pop
  IL_000d:  ret
}
");
        }
 
        [Fact]
        [WorkItem(65586, "https://github.com/dotnet/roslyn/issues/65586")]
        public void SideeffectsOfSlicing_02()
        {
            var src = @"
using System;
 
class Program
{
    static S s = new S();
    
    static void Main()
    {
        Test();
        Console.WriteLine(s.count); 
    }
 
    static void Test()
    {
        _ = s[1..2];
    }
}
 
class S
{
    public int count;
    
    public int Slice(int start, int length)
    {
        count++;
        return 0;
    }
    
    public int Length => 10;
}";
            var verifier = CompileAndVerifyWithIndexAndRange(src, expectedOutput: "1").VerifyDiagnostics();
 
            verifier.VerifyIL("Program.Test",
@"
{
  // Code size       14 (0xe)
  .maxstack  3
  IL_0000:  ldsfld     ""S Program.s""
  IL_0005:  ldc.i4.1
  IL_0006:  ldc.i4.1
  IL_0007:  callvirt   ""int S.Slice(int, int)""
  IL_000c:  pop
  IL_000d:  ret
}
");
        }
 
        [ConditionalFact(typeof(CoreClrOnly))]
        public void SpanSlice()
        {
            string source = """
                using System;
                class Program
                {
                    static void M(Span<int> s)
                    {
                        var x = s[1..^1];
                        var y = s[^1];
                    }
                }
                """;
            var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70);
            comp.VerifyDiagnostics();
        }
    }
}