/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/CSharp15/ExtensionIndexersTests.cs
42 306 строк
1 MB
Fred Silberberg
Add C# 15 language version (#84799)
11 авг 2026, 23:51
Не верифицирован
11 авг 2026, 23:51
1284a4a
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.VisualBasic; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics; [CompilerTrait(CompilerFeature.Extensions)] public class ExtensionIndexersTests : CompilingTestBase { private static string ExpectedOutput(string output) { return ExecutionConditionUtil.IsMonoOrCoreClr ? output : null; } private static readonly string corlibWithNoArrayMembersSource = """ namespace System { public class Object { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class String { } public class Attribute { } public class Array { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Collections { public interface IEnumerable { } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public static class RuntimeHelpers { public static T[] GetSubArray<T>(T[] array, Range range) => throw null; } public sealed class CompilerFeatureRequiredAttribute : Attribute { public CompilerFeatureRequiredAttribute(string featureName) => throw null; public string FeatureName => throw null; public bool IsOptional { get => throw null; set => throw null; } } public class ExtensionAttribute : Attribute { } } """; [Theory, CombinatorialData] public void LangVer_01(bool useCompilationReference) { var libSrc = """ public static class E { extension(object o) { public int this[int i] { get { System.Console.Write($"get({i})"); return i + 1; } set { System.Console.Write($"set({i}, {value})"); } } } } """; var libComp = CreateCompilation(libSrc); var libRef = AsReference(libComp, useCompilationReference); var src = """ object o = new object(); o[0] = 1; _ = o[2]; """; CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular14).VerifyEmitDiagnostics( // (2,1): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // o[0] = 1; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "o[0]").WithArguments("extension indexers", "15.0").WithLocation(2, 1), // (3,5): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // _ = o[2]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "o[2]").WithArguments("extension indexers", "15.0").WithLocation(3, 5)); CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular15).VerifyEmitDiagnostics(); CreateCompilation(src, references: [libRef], parseOptions: TestOptions.RegularPreview).VerifyEmitDiagnostics(); CreateCompilation([src, libSrc], parseOptions: TestOptions.Regular14).VerifyEmitDiagnostics( // (5,20): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // public int this[int i] Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "this").WithArguments("extension indexers", "15.0").WithLocation(5, 20)); CreateCompilation([src, libSrc], parseOptions: TestOptions.Regular15).VerifyEmitDiagnostics(); CreateCompilation([src, libSrc], parseOptions: TestOptions.RegularPreview).VerifyEmitDiagnostics(); } [Fact] public void Declaration_01() { // unnamed extension parameter var src = """ public static class E { extension(object) { public int this[int i] { get => throw null; set => throw null; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,20): error CS9303: 'this[]': cannot declare instance members in an extension block with an unnamed receiver parameter // public int this[int i] Diagnostic(ErrorCode.ERR_InstanceMemberWithUnnamedExtensionsParameter, "this").WithArguments("this[]").WithLocation(5, 20)); } [Fact] public void Declaration_02() { // static indexer var src = """ public static class E { extension(object o) { public static int this[int i] { get => throw null; set => throw null; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,27): error CS0106: The modifier 'static' is not valid for this item // public static int this[int i] Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(5, 27)); } [Fact] public void Declaration_03() { // protected indexer var src = """ public static class E { extension(object) { protected int this[int i] { get => throw null; set => throw null; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,23): error CS9302: 'E.extension(object).this[int]': new protected member declared in an extension block // protected int this[int i] Diagnostic(ErrorCode.ERR_ProtectedInExtension, "this").WithArguments("E.extension(object).this[int]").WithLocation(5, 23)); } [Fact] public void Declaration_04() { // non-inferrable extension member var src = """ public static class E { extension<T>(object o) { public int this[int i] { get => throw null; set => throw null; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,20): error CS9295: The type parameter `T` is not referenced by either the extension parameter or a parameter of this member // public int this[int i] Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "this").WithArguments("T").WithLocation(5, 20)); } [Fact] public void Declaration_05() { // indexer uses same parameter name as extension parameter var src = """ public static class E { extension(int i) { public int this[int i] { get => throw null; set => throw null; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,29): error CS9290: 'i': a parameter, local variable, or local function cannot have the same name as an extension parameter // public int this[int i] Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionParameter, "i").WithArguments("i").WithLocation(5, 29)); } [Fact] public void Declaration_07() { // parameter name conflict with extension type parameter var src = """ public static class E { extension<T>(T t) { public int this[object T] => throw null; } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,32): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter // public int this[object T] => throw null; Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(5, 32)); } [Fact] public void Declaration_08() { // parameter name conflict with enclosing static class var src = """ public static class E { extension(object o) { public int this[object E] => throw null; } } class C { public int this[object C] => throw null; } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Declaration_09() { var src = """ public static class E { extension(int i) { public int this[int j] { get => field; set { field = value; } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (7,20): error CS0103: The name 'field' does not exist in the current context // get => field; Diagnostic(ErrorCode.ERR_NameNotInContext, "field").WithArguments("field").WithLocation(7, 20), // (8,19): error CS0103: The name 'field' does not exist in the current context // set { field = value; } Diagnostic(ErrorCode.ERR_NameNotInContext, "field").WithArguments("field").WithLocation(8, 19)); } [Fact] public void Declaration_10() { var src = """ int i = 42; i[43] = 0; public static class E { extension(int i) { public int this[int j] { set { var field = j; System.Console.Write(field); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "43").VerifyDiagnostics(); } [Fact] public void Declaration_12() { var src = """ new object()[0] = 1; static class E { extension(object o) { public int this[int i] { init => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (1,1): error CS8852: Init-only property or indexer 'E.extension(object).this[int]' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. // new object()[0] = 1; Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "new object()[0]").WithArguments("E.extension(object).this[int]").WithLocation(1, 1), // (7,34): error CS9304: 'E.extension(object).this[int]': cannot declare init-only accessors in an extension block // public int this[int i] { init => throw null; } Diagnostic(ErrorCode.ERR_InitInExtension, "init").WithArguments("E.extension(object).this[int]").WithLocation(7, 34)); } [Fact] public void Declaration_13() { // getter named after enclosing static class var src = """ static class get_Item { extension(object o) { public int this[int i] { get => throw null; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (5,34): error CS0542: 'get_Item': member names cannot be the same as their enclosing type // public int this[int i] { get => throw null; } Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "get").WithArguments("get_Item").WithLocation(5, 34)); } [Fact] public void Declaration_14() { // setter named after enclosing static class var src = """ static class set_Item { extension(object o) { public int this[int i] { set => throw null; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (5,34): error CS0542: 'set_Item': member names cannot be the same as their enclosing type // public int this[int i] { set => throw null; } Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "set").WithArguments("set_Item").WithLocation(5, 34)); } [Fact] public void InconsistentTypeAccessibility_01() { var src = """ public static class E { extension(C c) { public int this[int i] => 0; } extension(int i) { public int this[C c] => 0; public C this[int j] => null; } private class C {} } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,20): error CS0055: Inconsistent accessibility: parameter type 'E.C' is less accessible than indexer or property 'E.extension(E.C).this[int]' // public int this[int i] => 0; Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "this").WithArguments("E.extension(E.C).this[int]", "E.C").WithLocation(5, 20), // (10,20): error CS0055: Inconsistent accessibility: parameter type 'E.C' is less accessible than indexer or property 'E.extension(int).this[E.C]' // public int this[C c] => 0; Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "this").WithArguments("E.extension(int).this[E.C]", "E.C").WithLocation(10, 20), // (11,18): error CS0054: Inconsistent accessibility: indexer return type 'E.C' is less accessible than indexer 'E.extension(int).this[int]' // public C this[int j] => null; Diagnostic(ErrorCode.ERR_BadVisIndexerReturn, "this").WithArguments("E.extension(int).this[int]", "E.C").WithLocation(11, 18) ); } [Fact] public void Indexing_01() { // no instance indexer var src = """ C c = new C(); c[0] = 1; _ = c[2]; E.set_Item(c, 0, 1); _ = E.get_Item(c, 2); public static class E { extension(C c) { public int this[int i] { get { System.Console.Write($"get({i}) "); return i + 1; } set { System.Console.Write($"set({i}, {value}) "); } } } } public class C { public int this[string s] { get => throw null; set => throw null; } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "set(0, 1) get(2) set(0, 1) get(2)").VerifyDiagnostics(); var indexer = comp.GlobalNamespace.GetTypeMember("E").GetTypeMember("").GetIndexer<PropertySymbol>("Item"); AssertEx.Equal("E.extension(C).this[int]", indexer.ToDisplayString()); AssertEx.Equal("System.Int32 E.get_Item(C c, System.Int32 i)", indexer.GetMethod.GetPublicSymbol().AssociatedExtensionImplementation.ToTestDisplayString()); AssertEx.Equal("void E.set_Item(C c, System.Int32 i, System.Int32 value)", indexer.SetMethod.GetPublicSymbol().AssociatedExtensionImplementation.ToTestDisplayString()); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var indexerDeclaration = tree.GetRoot().DescendantNodes().OfType<ExtensionBlockDeclarationSyntax>().Single().Members.OfType<IndexerDeclarationSyntax>().Single(); var declaredIndexer = model.GetDeclaredSymbol(indexerDeclaration); AssertEx.Equal("E.extension(C).this[int]", declaredIndexer.ToDisplayString()); Assert.True(declaredIndexer.IsIndexer); var getterAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "c[0]"); AssertEx.Equal("E.extension(C).this[int]", model.GetSymbolInfo(getterAccess).Symbol.ToDisplayString()); var setterAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "c[2]"); AssertEx.Equal("E.extension(C).this[int]", model.GetSymbolInfo(setterAccess).Symbol.ToDisplayString()); } [Fact] public void Indexing_02() { // instance indexer is not applicable var src = """ C c = new C(); c[0] = 1; _ = c[2]; public static class E { extension(C c) { public int this[int i] { get { System.Console.Write($"get({i})"); return i + 1; } set { System.Console.Write($"set({i}, {value}) "); } } } } public class C { public int this[string s] { get => throw null; set => throw null; } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "set(0, 1) get(2)").VerifyDiagnostics(); } [Fact] public void Indexing_03() { // ambiguous extension indexers var src = """ object o = new object(); o[0] = 1; _ = o[2]; static class E1 { extension(object o) { public int this[int i] { get => throw null; set => throw null; } } } static class E2 { extension(object o) { public int this[int i] { get => throw null; set => throw null; } } } """; // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, report something better (ambiguity between X and Y) var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (2,1): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // o[0] = 1; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[0]").WithArguments("object").WithLocation(2, 1), // (3,5): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o[2]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[2]").WithArguments("object").WithLocation(3, 5)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var indexing = GetSyntax<ElementAccessExpressionSyntax>(tree, "o[0]"); Assert.Null(model.GetSymbolInfo(indexing).Symbol); // https://github.com/dotnet/roslyn/issues/78829 public API, consider returning the candidate extension members (like we do for method calls) Assert.Empty(model.GetMemberGroup(indexing)); } [Fact] public void Indexing_04() { // ambiguous extension indexers var src = """ object o = new object(); o[0, 1] = 2; _ = o[3, 4]; static class E1 { extension(object o) { public int this[int i, long l] { get => throw null; set => throw null; } } } static class E2 { extension(object o) { public int this[long l, int i] { get => throw null; set => throw null; } } } """; // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, report something better (ambiguity between X and Y) CreateCompilation(src).VerifyEmitDiagnostics( // (2,1): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // o[0, 1] = 2; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[0, 1]").WithArguments("object").WithLocation(2, 1), // (3,5): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o[3, 4]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[3, 4]").WithArguments("object").WithLocation(3, 5)); } [Fact] public void Indexing_05() { // instance indexer comes first var src = """ C c = new C(); c[0] = 1; _ = c[2]; public static class E { extension(C c) { public int this[int i] { get => throw null; set => throw null; } } } public class C { public int this[int i] { get { System.Console.Write($"get({i})"); return i + 1; } set { System.Console.Write($"set({i}, {value}) "); } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "set(0, 1) get(2)").VerifyDiagnostics(); } [Fact] public void Indexing_06() { // instance Index indexer takes precedence over extension Index indexer var src = """ var c = new C(); _ = c[^1]; c[^2] = 10; static class E { extension(C c) { public int this[System.Index i] { get => throw null; set => throw null; } } } class C { public int this[System.Index i] { get { System.Console.Write($"get({i}) "); return 42; } set { System.Console.Write($"set({i}, {value}) "); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(^1) set(^2, 10)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void Indexing_07() { // instance Index indexer takes precedence over extension Index indexer var src = """ var c = new C(); _ = c[^1]; c[^2] = 10; static class E { extension(C c) { public int this[System.Index i] { get => throw null; set => throw null; } } } class C { public int this[string s] => 0; public int this[System.Index i] { get { System.Console.Write($"get({i}) "); return 42; } set { System.Console.Write($"set({i}, {value}) "); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(^1) set(^2, 10)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void Indexing_08() { // extension indexer is obsolete var src = """ var o = new object(); o[0] = 1; _ = o[2]; E.set_Item(o, 0, 1); _ = E.get_Item(o, 2); public static class E { extension(object o) { [System.Obsolete("indexer")] public int this[int i] { get => throw null; set => throw null; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,1): warning CS0618: 'E.extension(object).this[int]' is obsolete: 'indexer' // o[0] = 1; Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "o[0]").WithArguments("E.extension(object).this[int]", "indexer").WithLocation(2, 1), // (3,5): warning CS0618: 'E.extension(object).this[int]' is obsolete: 'indexer' // _ = o[2]; Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "o[2]").WithArguments("E.extension(object).this[int]", "indexer").WithLocation(3, 5)); } [Fact] public void Indexing_09() { var src = """ C.M(new C()); public class C { public static void M(C C) { C[0] = 1; _ = C[2]; } } public static class E { extension(C c) { public int this[int i] { get { System.Console.Write($"get({i})"); return i + 1; } set { System.Console.Write($"set({i}, {value}) "); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "set(0, 1) get(2)").VerifyDiagnostics(); } [Fact] public void Indexing_10() { // generic extension block var src = """ var o = new object(); o[0] = 1; _ = o[2]; public static class E { extension<T>(T t) { public int this[int i] { get { System.Console.Write($"get({i}) "); return i + 1; } set { System.Console.Write($"set({i}, {value}) "); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "set(0, 1) get(2)").VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var indexerDeclaration = tree.GetRoot().DescendantNodes().OfType<ExtensionBlockDeclarationSyntax>().Single().Members.OfType<IndexerDeclarationSyntax>().Single(); var declaredIndexer = model.GetDeclaredSymbol(indexerDeclaration); AssertEx.Equal("E.extension<T>(T).this[int]", declaredIndexer.ToDisplayString()); Assert.True(declaredIndexer.IsIndexer); var setterAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "o[0]"); AssertEx.Equal("E.extension<object>(object).this[int]", model.GetSymbolInfo(setterAccess).Symbol.ToDisplayString()); var getterAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "o[2]"); AssertEx.Equal("E.extension<object>(object).this[int]", model.GetSymbolInfo(getterAccess).Symbol.ToDisplayString()); var indexer = comp.GlobalNamespace.GetTypeMember("E").GetTypeMember("").GetMembers().OfType<PropertySymbol>().Single().GetPublicSymbol(); var byteType = comp.GetSpecialType(SpecialType.System_Byte).GetPublicSymbol(); AssertEx.Equal("E.extension<byte>(byte).this[int]", indexer.ReduceExtensionMember(byteType).ToDisplayString()); AssertEx.Equal("System.Int32 E.get_Item<T>(T t, System.Int32 i)", indexer.GetMethod.AssociatedExtensionImplementation.ToTestDisplayString()); AssertEx.Equal("void E.set_Item<T>(T t, System.Int32 i, System.Int32 value)", indexer.SetMethod.AssociatedExtensionImplementation.ToTestDisplayString()); } [Fact] public void Indexing_11() { // generic extension block, type parameter used in indexer parameter var src = """ var o = new object(); o[0] = 1; _ = o[2]; public static class E { extension<T>(object o) { public int this[T t] { get { System.Console.Write($"get({t}) "); return 0; } set { System.Console.Write($"set({t}, {value}) "); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "set(0, 1) get(2)").VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var indexerDeclaration = tree.GetRoot().DescendantNodes().OfType<ExtensionBlockDeclarationSyntax>().Single().Members.OfType<IndexerDeclarationSyntax>().Single(); var declaredIndexer = model.GetDeclaredSymbol(indexerDeclaration); AssertEx.Equal("E.extension<T>(object).this[T]", declaredIndexer.ToDisplayString()); Assert.True(declaredIndexer.IsIndexer); var setterAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "o[0]"); AssertEx.Equal("E.extension<int>(object).this[int]", model.GetSymbolInfo(setterAccess).Symbol.ToDisplayString()); var getterAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "o[2]"); AssertEx.Equal("E.extension<int>(object).this[int]", model.GetSymbolInfo(getterAccess).Symbol.ToDisplayString()); } [Fact] public void Indexing_12() { // named arguments and evaluation order var src = """ var o = new object(); o[b: id(1), a: id(2)] = id(3); _ = o[b: id(4), a: id(5)]; int id(int x) { System.Console.Write($"id({x}) "); return x; } public static class E { extension(object o) { public int this[int a, int b] { get { System.Console.Write($"get({a}, {b}) "); return 0; } set { System.Console.Write($"set({a}, {b}, {value}) "); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "id(1) id(2) id(3) set(2, 1, 3) id(4) id(5) get(5, 4)").VerifyDiagnostics(); } [Fact] public void Indexing_13() { // convert receiver var src = """ C c = new C(); c[0] = 1; public static class E { extension(object o) { public int this[int i] { set { System.Console.Write($"set({i}, {value}) "); } } } } class C { } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "set(0, 1)").VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var setterAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "c[0]"); var typeInfo = model.GetTypeInfo(setterAccess.Expression); AssertEx.Equal("C", typeInfo.Type.ToTestDisplayString()); AssertEx.Equal("System.Object", typeInfo.ConvertedType.ToTestDisplayString()); } [Fact] public void Indexing_14() { // default parameter values var src = """ var o = new object(); _ = o[a: 100]; o[b: 101] = 0; public static class E { extension(object o) { public int this[int a = 42, int b = 43] { get { System.Console.Write($"get({a}, {b}) "); return 0; } set { System.Console.Write($"set({a}, {b}, {value}) "); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "get(100, 43) set(42, 101, 0)").VerifyDiagnostics(); } [Fact] public void Indexing_15() { // params var src = """ var o = new object(); _ = o[42, 43, 44]; public static class E { extension(object o) { public int this[int a, params int[] b] { get { System.Console.Write($"get({a}, {b[0]}, {b[1]}) "); return 0; } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "get(42, 43, 44)").VerifyDiagnostics(); } [Fact] public void Indexing_16() { // multiple scopes var src = """ namespace N { public class C { public static void Main() { var o = new object(); _ = o[42]; } } public static class E1 { extension(object o) { public int this[int a] { get { System.Console.Write($"get({a}) "); return 0; } } } } } public static class E2 { extension(object o) { public int this[int a] => throw null; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: "get(42)").VerifyDiagnostics(); } [Fact] public void Indexing_17() { // broken constraint var src = """ var o = new object(); _ = o[42]; E.get_Item(o, 42); public static class E { extension<T>(T t) where T : struct { public int this[int a] => throw null; } } """; // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, report that the constraint is not satisfied CreateCompilation(src).VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o[42]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[42]").WithArguments("object").WithLocation(2, 5), // (3,3): error CS0453: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'E.get_Item<T>(T, int)' // E.get_Item(o, 42); Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "get_Item").WithArguments("E.get_Item<T>(T, int)", "T", "object").WithLocation(3, 3)); } [Fact] public void Indexing_18() { // no getter var src = """ var o = new object(); _ = o[42]; E.get_Item(o, 42); _ = new C()[42]; public static class E { extension(object o) { public int this[int a] { set { } } } } class C { public int this[int a] { set { } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,5): error CS0154: The property or indexer 'E.extension(object).this[int]' cannot be used in this context because it lacks the get accessor // _ = o[42]; Diagnostic(ErrorCode.ERR_PropertyLacksGet, "o[42]").WithArguments("E.extension(object).this[int]").WithLocation(2, 5), // (3,3): error CS0117: 'E' does not contain a definition for 'get_Item' // E.get_Item(o, 42); Diagnostic(ErrorCode.ERR_NoSuchMember, "get_Item").WithArguments("E", "get_Item").WithLocation(3, 3), // (5,5): error CS0154: The property or indexer 'C.this[int]' cannot be used in this context because it lacks the get accessor // _ = new C()[42]; Diagnostic(ErrorCode.ERR_PropertyLacksGet, "new C()[42]").WithArguments("C.this[int]").WithLocation(5, 5)); } [Fact] public void Indexing_19() { // inaccessible, file-scope var src = """ var o = new object(); _ = o[42]; """; var src2 = """ file static class E { extension(object o) { public int this[int a] => throw null; } } """; CreateCompilation([src, (src2, "program.cs")]).VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o[42]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[42]").WithArguments("object").WithLocation(2, 5)); } [Fact] public void Indexing_20() { // constant receiver var src = """ 42[43] = 10; static class E { extension(int i) { public int this[int j] { get => 0; set { } } } } """; // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members CreateCompilation(src).VerifyEmitDiagnostics( // (1,1): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // 42[43] = 10; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "42[43]").WithLocation(1, 1)); } [Fact] public void Indexing_21() { // ref receiver parameter var src = """ 42[43] = 10; static class E { extension(ref int i) { public int this[int j] { get => 0; set { } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,1): error CS1510: A ref or out value must be an assignable variable // 42[43] = 10; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "42").WithLocation(1, 1)); } [Fact] public void Indexing_22() { // ref receiver parameter, variable var src = """ int i = 42; i[43] = 10; System.Console.WriteLine(i); static class E { extension(ref int i) { public int this[int j] { set { System.Console.Write($"set({j}, {value}) "); i = 100; } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set(43, 10) 100")).VerifyDiagnostics(); } [Theory] [InlineData("ref readonly")] [InlineData("in")] public void Indexing_23(string refKind) { // ref readonly receiver parameter, variable var src = $$""" int i = 42; i[43] = 10; static class E { extension({{refKind}} int i) { public int this[int j] { set { System.Console.Write($"set({j}, {value}) "); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set(43, 10)")).VerifyDiagnostics(); } [Fact] public void Indexing_24() { // ref and out parameters in indexer var src = """ static class E { extension(int i) { public int this[int j, ref int k, out int l] { set => throw null; } } } class C { public int this[int j, ref int k, out int l] { set => throw null; } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (11,28): error CS0631: ref and out are not valid in this context // public int this[int j, ref int k, out int l] { set => throw null; } Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(11, 28), // (11,39): error CS0631: ref and out are not valid in this context // public int this[int j, ref int k, out int l] { set => throw null; } Diagnostic(ErrorCode.ERR_IllegalRefParam, "out").WithLocation(11, 39), // (5,32): error CS0631: ref and out are not valid in this context // public int this[int j, ref int k, out int l] { set => throw null; } Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(5, 32), // (5,43): error CS0631: ref and out are not valid in this context // public int this[int j, ref int k, out int l] { set => throw null; } Diagnostic(ErrorCode.ERR_IllegalRefParam, "out").WithLocation(5, 43)); } [Fact] public void Indexing_25() { // in parameters in indexer var src = """ int i = 42; i[k: 43, j: 44] = 100; static class E { extension(int i) { public int this[in int j, in int k] { set { System.Console.WriteLine($"set({j}, {k}, {value})"); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set(44, 43, 100)")).VerifyDiagnostics(); } [Fact] public void Indexing_26() { // in parameters in indexer var src = """ int i = 42; i[j: 44] = 100; static class E { extension(int i) { public int this[in int j, in int k = 43] { set { System.Console.WriteLine($"set({j}, {k}, {value})"); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set(44, 43, 100)")).VerifyDiagnostics(); } [Fact] public void Indexing_27() { // in parameters in indexer var src = """ int i = 42; i[j: 44] = 100; static class E { extension(in int i) { public int this[in int j, in int k = 43] { set { System.Console.WriteLine($"set({j}, {k}, {value})"); } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set(44, 43, 100)")).VerifyDiagnostics(); } [Fact] public void Indexing_28() { // ref-returning indexer var src = """ 42[43] = 10; System.Console.WriteLine(E.field); static class E { public static int field = 42; extension(int i) { public ref int this[int j] { get { return ref E.field; } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: ExpectedOutput("10")).VerifyDiagnostics(); } [Fact] public void Indexing_29() { var src = """ int x = 42; _ = x[43]; x[43] = 10; static class E { public static int field = 42; extension(int i) { public int this[int j] { get => 0; private set { } } public void M() { int x = 42; _ = x[43]; x[43] = 10; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,1): error CS0272: The property or indexer 'E.extension(int).this[int]' cannot be used in this context because the set accessor is inaccessible // x[43] = 10; Diagnostic(ErrorCode.ERR_InaccessibleSetter, "x[43]").WithArguments("E.extension(int).this[int]").WithLocation(3, 1)); } [Fact] public void Indexing_30() { var src = """ _ = 42[43]; static class E { extension(ref int i) { public int this[int j] { get => 0; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,5): error CS1510: A ref or out value must be an assignable variable // _ = 42[43]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "42").WithLocation(1, 5)); } [Fact] public void Indexing_31() { var src = """ _ = new Derived()[new Base()]; static class E { extension<T>(T t1) { public int this[T t2] { get => 0; } } } public class Base { } public class Derived : Base { } """; var comp = CreateCompilation(src).VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "new Derived()[new Base()]"); AssertEx.Equal("E.extension<Base>(Base).this[Base]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Indexing_32() { var src = """ new Derived()[42] = new Base(); E.set_Item(new Derived(), 42, new Base()); static class E { extension<T>(T t) { public T this[int i] { set { } } } } public class Base { } public class Derived : Base { } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,21): error CS0266: Cannot implicitly convert type 'Base' to 'Derived'. An explicit conversion exists (are you missing a cast?) // new Derived()[42] = new Base(); Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "new Base()").WithArguments("Base", "Derived").WithLocation(1, 21)); } [Fact] public void Indexing_33() { var src = """ var result = new C()[42]; System.Console.WriteLine(result); interface I { } interface Indirect : I { } class C : Indirect { } static class E { extension(I i) { public string this[int j] => "ran"; } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var indexing = GetSyntax<ElementAccessExpressionSyntax>(tree, "new C()[42]"); AssertEx.Equal("System.String E.<G>$3EADBD08A82F6ABA9495623CB335729C.this[System.Int32 j] { get; }", model.GetSymbolInfo(indexing).Symbol.ToTestDisplayString()); Assert.Empty(model.GetMemberGroup(indexing)); } [Fact] public void Indexing_34() { // inapplicable instance indexer and applicable extension indexer var source = """ _ = new C()[b: 42]; class C { public int this[int a] { get { throw null; } } } static class E1 { extension(C c) { public int this[int b] { get { System.Console.Write($"E1.get_Item({b})"); return 0; } } } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "E1.get_Item(42)"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "new C()[b: 42]"); AssertEx.Equal("E1.extension(C).this[int]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); // https://github.com/dotnet/roslyn/issues/78829 public API, should probably return both candidates (see InstanceMethodInvocation_ArgumentName) AssertEx.SequenceEqual([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); } [Fact] public void Indexing_35() { // inapplicable inner extension indexer and applicable extension indexer var source = """ using N; _ = new C()[b: 42]; class C { } static class E1 { extension(C c) { public int this[int a] { get { throw null; } } } } namespace N { static class E2 { extension(C c) { public int this[int b] { get { System.Console.Write($"E2.get_Item({b})"); return 0; } } } } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "E2.get_Item(42)"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "new C()[b: 42]"); AssertEx.Equal("N.E2.extension(C).this[int]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); // https://github.com/dotnet/roslyn/issues/78829 public API, should probably return both candidates (see InstanceMethodInvocation_ArgumentName_02) AssertEx.SequenceEqual([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); } [Fact] public void Indexing_36() { // prefer more specific extension indexer var source = """ System.Console.Write(new C()[42]); class Base { } class C : Base { } static class E1 { extension(Base b) { public int this[int i] { get { throw null; } } } } static class E2 { extension(C c) { public int this[int i] => i; } } """; var comp = CreateCompilation(source); CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "new C()[42]"); AssertEx.Equal("E2.extension(C).this[int]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); // https://github.com/dotnet/roslyn/issues/78829 public API, should probably return both candidates (see InstanceMethodInvocation_AmbiguityWithExtensionOnBaseType_PreferMoreSpecific) AssertEx.SequenceEqual([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); } [Fact] public void Indexing_37() { var source = """ struct S1(Color Color) { public void Test() { _ = Color[this]; } } class Color { } static class E { extension(Color c) { public int this[S1 x, int y = 0] { get { System.Console.WriteLine("instance"); return 0; } } } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); Assert.NotEmpty(comp.GetTypeByMetadataName("S1").InstanceConstructors.OfType<SynthesizedPrimaryConstructor>().Single().GetCapturedParameters()); } [Fact] public void Indexing_38() { var source = """ class Base { } class Derived : Base { void Main() { _ = base[0]; _ = (base)[0]; _ = this[0]; } } static class E { extension(Base b) { public int this[int i] => 0; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Base' // _ = base[0]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "base[0]").WithArguments("Base").WithLocation(8, 13), // (9,14): error CS0175: Use of keyword 'base' is not valid in this context // _ = (base)[0]; Diagnostic(ErrorCode.ERR_BaseIllegal, "base").WithLocation(9, 14)); } [Fact] public void Indexing_39() { var src = """ _ = new C()[0]; interface I<T> { } class C : I<int>, I<string> { } static class E { extension<T>(I<T> i) { public int this[int j] => 0; } } """; var comp = CreateCompilation(src); // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, could report something better, like: CS0411: The type arguments for method 'E.extension<T>(I<T>).M()' cannot be inferred from the usage. Try specifying the type arguments explicitly. comp.VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C()[0]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "new C()[0]").WithArguments("C").WithLocation(1, 5)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "new C()[0]"); Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols); Assert.Empty(model.GetMemberGroup(memberAccess)); } [Fact] public void Indexing_40() { var missingSrc = """ public class Missing { } """; var missingRef = CreateCompilation(missingSrc, assemblyName: "missing").EmitToImageReference(); var derivedSrc = """ public class Derived : Missing { } """; var derivedRef = CreateCompilation(derivedSrc, references: [missingRef]).EmitToImageReference(); var src = """ _ = new Derived()[0]; static class E { extension(Derived d) { public int this[int i] { get => 0; } } } """; var comp = CreateCompilation(src, references: [derivedRef]); comp.VerifyEmitDiagnostics( // (1,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = new Derived()[0]; Diagnostic(ErrorCode.ERR_NoTypeDef, "new Derived()[0]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 5)); } [Fact] public void Indexing_41() { var src = """ new object()[0].field = 1; public struct S { public int field; } static class E { extension(object o) { public S this[int i] { get => throw null; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (1,1): error CS1612: Cannot modify the return value of 'E.extension(object).this[int]' because it is not a variable // new object()[0].field = 1; Diagnostic(ErrorCode.ERR_ReturnNotLValue, "new object()[0]").WithArguments("E.extension(object).this[int]").WithLocation(1, 1)); } [Fact] public void Indexing_42() { // ref readonly extension parameter, constant receiver var src = """ _ = 42[0]; static class E { extension(ref readonly int i) { public int this[int j] { get { System.Console.Write(42); return 0; } } } } """; CompileAndVerify(src, expectedOutput: "42").VerifyDiagnostics( // (1,5): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = 42[0]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "42").WithArguments("0").WithLocation(1, 5)); } [Fact] public void Indexing_43() { // ref readonly extension parameter, variable receiver var src = """ int i = 42; _ = i[0]; static class E { extension(ref readonly int i) { public int this[int j] { get { System.Console.Write(42); return 0; } } } } """; CompileAndVerify(src, expectedOutput: "42").VerifyDiagnostics(); } [Fact] public void Indexing_44() { // ref readonly extension parameter, ref readonly receiver var src = """ var f = (ref readonly int i) => i[0]; int i = 42; f(ref i); static class E { extension(ref readonly int i) { public int this[int j] { get { System.Console.Write(42); return 0; } } } } """; CompileAndVerify(src, expectedOutput: "42").VerifyDiagnostics(); } [Fact] public void Indexing_45() { // inaccessible type argument var src = """ _ = new A.C()[0]; static class E1 { extension<T>(I<T> i) { public int this[int j] => 0; } } interface I<T> { } class A { private class B { } public class C : I<B> { } } """; var comp = CreateCompilation(src); // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, consider reporting something like CS0122: 'E1.extension<A.B>(I<A.B>).P' is inaccessible due to its protection level comp.VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'A.C' // _ = new A.C()[0]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "new A.C()[0]").WithArguments("A.C").WithLocation(1, 5)); } [Fact] public void Indexing_46() { // indexing on type var src = """ _ = C[0]; static class E { extension(C c) { public int this[int j] => 0; } } class C { } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,5): error CS0119: 'C' is a type, which is not valid in the given context // _ = C[0]; Diagnostic(ErrorCode.ERR_BadSKunknown, "C").WithArguments("C", "type").WithLocation(1, 5)); } [Fact] public void Indexing_47() { // extra ref var src = """ int i = 0; _ = 0[ref i]; static class E { extension(int i) { public int this[int j] => 0; } } """; // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, consider a better message for unexpected ref like CS1615: Argument 2 may not be passed with the 'ref' keyword CreateCompilation(src).VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 0[ref i]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "0[ref i]").WithArguments("int").WithLocation(2, 5)); } [Fact] public void Indexing_48() { // indexing null var src = """ _ = null[""]; static class E { extension<T>(T t1) { public int this[T t2] => 0; } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type '<null>' // _ = null[""]; Diagnostic(ErrorCode.ERR_BadIndexLHS, @"null[""""]").WithArguments("<null>").WithLocation(1, 5)); } [Fact] public void Indexing_49() { // generic vs. non-generic extension indexer var src = """ _ = 42[43]; static class E { extension<T>(T t) { public int this[int i] { get => throw null; } } extension(int i) { public int this[int j] { get { System.Console.Write("ran"); return 0; } } } } """; CompileAndVerify(src, expectedOutput: "ran").VerifyDiagnostics(); } [Fact] public void Indexing_50() { //public static class E //{ // extension(int i) // { // public void 'this[]'() // { // } // } //} var ilSrc = """ .class public auto ansi abstract sealed beforefieldinit E extends System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .class nested public auto ansi sealed specialname '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69' extends System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .class nested public auto ansi abstract sealed specialname '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372' extends System.Object { .method public hidebysig specialname static void '<Extension>$' ( int32 i ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) ret } } .method public hidebysig instance void 'this[]' () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) newobj instance void [mscorlib]System.NotSupportedException::.ctor() throw } } .method public hidebysig static void 'this[]' ( int32 i ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) ret } } """ + ExtensionMarkerAttributeIL; var src = """ _ = 42[43]; """; CreateCompilationWithIL(src, ilSrc).VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 42[43]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "42[43]").WithArguments("int").WithLocation(1, 5)); // https://github.com/dotnet/roslyn/issues/78829 verify candidates from GetMemberGroup } [Fact(Skip = "https://github.com/dotnet/roslyn/issues/83610 existing crash")] public void Indexing_51() { //public class C //{ // public void 'this[]' () { } //} var ilSrc = """ .class public auto ansi beforefieldinit C extends System.Object { .method public hidebysig instance void 'this[]' () cil managed { ret } .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: ret } } """; var src = """ _ = new C()[43]; """; CreateCompilationWithIL(src, ilSrc).VerifyEmitDiagnostics(); } [Fact] public void Indexing_52() { // two ambiguous/applicable candidates mask an outer applicable candidate var src = """ using N; _ = 42[new C()]; static class E1 { extension(int i) { public int this[I1 i1] { get => throw null; } public int this[I2 i2] { get => throw null; } } } interface I1 { } interface I2 { } class C : I1, I2 { } namespace N { static class E2 { extension(int i) { public int this[I1 i1] { get => throw null; } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 42[new C()]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "42[new C()]").WithArguments("int").WithLocation(3, 5)); } [Fact] public void Indexing_53() { // convert receiver, struct var src = """ S s = new S(); s[0] = 1; public static class E { extension(object o) { public int this[int i] { set { System.Console.Write($"set({i}, {value}) "); } } } } struct S { } """; var comp = CreateCompilation(src); var verifier = CompileAndVerify(comp, expectedOutput: "set(0, 1)").VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var setterAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "s[0]"); var typeInfo = model.GetTypeInfo(setterAccess.Expression); AssertEx.Equal("S", typeInfo.Type.ToTestDisplayString()); AssertEx.Equal("System.Object", typeInfo.ConvertedType.ToTestDisplayString()); // receiver is boxed verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 22 (0x16) .maxstack 3 .locals init (S V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloc.0 IL_0009: box "S" IL_000e: ldc.i4.0 IL_000f: ldc.i4.1 IL_0010: call "void E.set_Item(object, int, int)" IL_0015: ret } """); } [Fact] public void Indexing_54() { // inaccessible, private var src = """ var o = new object(); _ = o[42]; static class E { extension(object o) { private int this[int a] => throw null; } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o[42]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[42]").WithArguments("object").WithLocation(2, 5)); } [Fact] public void Indexing_55() { var src = """ System.Console.Write(""[""]); public static class E { extension(string s1) { public int this[string s2] { get => 42; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (1,25): error CS1503: Argument 1: cannot convert from 'string' to 'int' // System.Console.Write(""[""]); Diagnostic(ErrorCode.ERR_BadArgType, @"""""").WithArguments("1", "string", "int").WithLocation(1, 25)); } [Fact] public void Indexing_56() { // Obsolete extension this[Index] var src = """ var c = new C(); _ = c[^1]; c[^2] = 10; static class E { extension(C c) { [System.Obsolete("indexer")] public int this[System.Index i] { get { System.Console.Write($"get({i}) "); return 42; } set { System.Console.Write($"set({i}, {value}) "); } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(^1) set(^2, 10) "), verify: Verification.FailsPEVerify).VerifyDiagnostics( // (2,5): warning CS0618: 'E.extension(C).this[Index]' is obsolete: 'indexer' // _ = c[^1]; Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c[^1]").WithArguments("E.extension(C).this[System.Index]", "indexer").WithLocation(2, 5), // (3,1): warning CS0618: 'E.extension(C).this[Index]' is obsolete: 'indexer' // c[^2] = 10; Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c[^2]").WithArguments("E.extension(C).this[System.Index]", "indexer").WithLocation(3, 1)); } [Fact] public void Indexing_57() { // extension this[Index] is not available on base var src = """ class Base { } class Derived : Base { void Main() { _ = base[^1]; _ = (base)[^1]; _ = this[^1]; } } static class E { extension(Base b) { public int this[System.Index i] => 0; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (8,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Base' // _ = base[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "base[^1]").WithArguments("Base").WithLocation(8, 13), // (9,14): error CS0175: Use of keyword 'base' is not valid in this context // _ = (base)[^1]; Diagnostic(ErrorCode.ERR_BaseIllegal, "base").WithLocation(9, 14)); } [Fact] public void Indexing_58() { // extension this[Range] is not available on base var src = """ class Base { } class Derived : Base { void Main() { _ = base[1..^1]; _ = (base)[1..^1]; _ = this[1..^1]; } } static class E { extension(Base b) { public int this[System.Range r] => 0; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (8,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Base' // _ = base[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "base[1..^1]").WithArguments("Base").WithLocation(8, 13), // (9,14): error CS0175: Use of keyword 'base' is not valid in this context // _ = (base)[1..^1]; Diagnostic(ErrorCode.ERR_BaseIllegal, "base").WithLocation(9, 14)); } [Fact] public void BadContainer_01() { // lacking static enclosing type var src = """ _ = 42[43]; extension(int i) { public int this[int j] { get => 0; } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 42[43]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "42[43]").WithArguments("int").WithLocation(1, 5), // (3,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class // extension(int i) Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(3, 1)); } [Fact] public void BadContainer_02() { // nested in extension block var src = """ _ = 42[43]; static class E { extension(int i) { extension(int i) { public int this[int j] { get => 0; } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 42[43]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "42[43]").WithArguments("int").WithLocation(1, 5), // (7,9): error CS9282: This member is not allowed in an extension block // extension(int i) Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(7, 9)); } [Fact] public void BadContainer_03() { // in nested type var src = """ _ = 42[43]; static class E { static class Nested { extension(int i) { public int this[int j] { get => 0; } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 42[43]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "42[43]").WithArguments("int").WithLocation(1, 5), // (7,9): error CS9283: Extensions must be declared in a top-level, non-generic, static class // extension(int i) Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(7, 9)); } [Fact] public void BadContainer_04() { // in generic type var src = """ _ = 42[43]; static class E<T> { extension(int i) { public int this[int j] { get => 0; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 42[43]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "42[43]").WithArguments("int").WithLocation(1, 5), // (5,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class // extension(int i) Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(5, 5)); } [Fact] public void BadContainer_05() { // in non-static enclosing type var src = """ _ = 42[43]; class E { extension(int i) { public int this[int j] { get => 0; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 42[43]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "42[43]").WithArguments("int").WithLocation(1, 5), // (5,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class // extension(int i) Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(5, 5)); } [Fact] public void BadContainer_06() { var src = """ _ = 42[43]; static class E { extension(__arglist) { public int this[int j] { get => 0; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (1,5): error CS0021: Cannot apply indexing with [] to an expression of type 'int' // _ = 42[43]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "42[43]").WithArguments("int").WithLocation(1, 5), // (5,15): error CS1669: __arglist is not valid in this context // extension(__arglist) Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(5, 15)); } [Fact] public void ImplicitIndexIndexer_01() { // instance implicit indexer takes precedence over extension this[Index] var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int this[System.Index i] { get => throw null; } } } class C { public int Length => 3; public int this[int i] { get { System.Console.Write($"instance({i}) "); return 99; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instance(2) 99"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_02() { // extension Length paired with instance this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); c[^2] = 10; static class E { extension(C c) { public int Length => 3; } } class C { public int this[int i] { get { System.Console.Write($"instance.get({i}) "); return 42; } set { System.Console.Write($"instance.set({i}, {value}) "); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instance.get(2) 42instance.set(1, 10) "), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_03() { // extension this[int] paired with instance Length var src = """ var c = new C(); System.Console.Write(c[^1]); c[^2] = 10; static class E { extension(C c) { public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } set { System.Console.Write($"set({i}, {value}) "); } } } } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42set(1, 10) "), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_04() { // extension Length + extension this[int] in same scope var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Length => 3; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_05() { // extension Length in outer scope + extension this[int] in inner scope var src = """ using Outer; namespace Inner { class Program { void Main() { _ = new C()[^1]; } } static class E1 { extension(C c) { public int this[int i] => throw null; } } } namespace Outer { static class E2 { extension(C c) { public int Length => throw null; } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void ImplicitIndexIndexer_06() { // extension Count + extension this[int] in same scope var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Count => 3; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_07() { // instance Length + instance this[int] takes precedence over extension implicit pattern var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } class C { public int Length => 3; public int this[int i] { get { System.Console.Write($"instance({i}) "); return 99; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instance(2) 99"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_08() { // extension this[Index] is preferred over extension Length + extension this[int] in same scope var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } public int this[System.Index i] { get { System.Console.Write($"real({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("real(^1) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_09() { // Outer extension this[Index] is preferred over inner extension Length + inner extension this[int]. var src = """ using Outer; namespace Inner { class Program { static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => 3; public int this[int i] { get { System.Console.Write($"implicit({i}) "); return 42; } } } } } namespace Outer { static class E2 { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"real({i}) "); return 42; } } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("real(^1) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_10() { // Inner extension this[Index] is preferred over outer extension Length + outer extension this[int] var src = """ using Outer; namespace Inner { class Program { static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"real({i}) "); return 42; } } } } } namespace Outer { static class E2 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("real(^1) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics( // (1,1): hidden CS8019: Unnecessary using directive. // using Outer; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using Outer;").WithLocation(1, 1)); } [Fact] public void ImplicitIndexIndexer_11() { // Instance this[string] and extension Length + extension this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Length => 3; public int this[int i] { get { System.Console.Write($"ext({i}) "); return 42; } } } } class C { public int this[string s] { get => throw null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ext(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_12() { // extension Length + extension this[int] with no getter var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Length => throw null; public int this[int i] { set { System.Console.Write($"set({i}, {value}) "); } } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100) .VerifyEmitDiagnostics( // (2,22): error CS0154: The property or indexer 'E.extension(C).this[int]' cannot be used in this context because it lacks the get accessor // System.Console.Write(c[^1]); Diagnostic(ErrorCode.ERR_PropertyLacksGet, "c[^1]").WithArguments("E.extension(C).this[int]").WithLocation(2, 22)); } [Fact] public void ImplicitIndexIndexer_13() { // Inner extension this[Index] preferred over outer this[Index]. var src = """ using Outer; namespace Inner { class Program { static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"inner({i}) "); return 42; } } } } } namespace Outer { static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("inner(^1) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics( // (1,1): hidden CS8019: Unnecessary using directive. // using Outer; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using Outer;").WithLocation(1, 1)); } [Fact] public void ImplicitIndexIndexer_14() { // Inner extension Length and outer extension Length + outer extension this[int] var src = """ using Outer; namespace Inner { class Program { static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length { get { System.Console.Write("inner "); return 3; } } } } } namespace Outer { static class E2 { extension(C c) { public int Length => throw null; public int this[int i] { get { System.Console.Write($"outer({i}) "); return 42; } } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("inner outer(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_15() { // Instance this[string] and extension this[Index] var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"ext({i}) "); return 42; } } } } class C { public int this[string s] { get => throw null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ext(^1) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_16() { // Obsolete extension Length property var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { [System.Obsolete("obsolete")] public int Length => 3; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics( // (2,22): warning CS0618: 'C.Length' is obsolete: 'obsolete' // System.Console.Write(c[^1]); Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c[^1]").WithArguments("E.extension(C).Length", "obsolete").WithLocation(2, 22)); } [Fact] public void ImplicitIndexIndexer_17() { // Obsolete extension this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Length => 3; [System.Obsolete("old indexer")] public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics( // (2,22): warning CS0618: 'C.this[int]' is obsolete: 'old indexer' // System.Console.Write(c[^1]); Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "c[^1]").WithArguments("E.extension(C).this[int]", "old indexer").WithLocation(2, 22)); } [Fact] public void ImplicitIndexIndexer_18() { // extension implicit indexer with receiver conversion var src = """ var d = new Derived(); System.Console.Write(d[^1]); static class E { extension(Base b) { public int Length => 3; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class Base { } class Derived : Base { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_19() { // Length from extension(Base), this[int] from extension(Derived) var src = """ var d = new Derived(); System.Console.Write(d[^1]); static class E { extension(Base b) { public int Length => 3; } extension(Derived d) { public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class Base { } class Derived : Base { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_20() { // Length from extension(Derived), this[int] from extension(Base) var src = """ var d = new Derived(); System.Console.Write(d[^1]); static class E { extension(Derived d) { public int Length => 3; } extension(Base b) { public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class Base { } class Derived : Base { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_21() { // static extension Length var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public static int Length => throw null; public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_22() { // extension Length with no getter var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length { set { } } public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_23() { // extension Length returning long var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public long Length => 3; public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_24() { // extension ref-returning Length var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public ref int Length { get => throw null; } public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_25() { // extension Length with private getter var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length { private get => throw null; set { } } public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_26() { // extension Length method + extension this[int] var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length() => 3; public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_27() { // extension Count + extension this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Count => 3; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_28() { // extension Length + extension this[string] var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length => throw null; public int this[string s] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_29() { // extension Length + extension this[int, int] var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length => throw null; public int this[int i, int j] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_30() { // extension Length + extension this[int, int = 42] var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length => throw null; public int this[int i, int j = 42] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_31() { // extension Length + extension this[ref int] var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length => throw null; public int this[ref int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5), // (9,25): error CS0631: ref and out are not valid in this context // public int this[ref int i] { get => throw null; } Diagnostic(ErrorCode.ERR_IllegalRefParam, "ref").WithLocation(9, 25)); } [Fact] public void ImplicitIndexIndexer_32() { // extension Length + private extension this[int] var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length => throw null; private int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_33() { // named argument matching extension this[int] parameter name var src = """ var c = new C(); _ = c[i: ^1]; static class E { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,7): error CS8428: Invocation of implicit Index Indexer cannot name the argument. //_ = c[i: ^1]; Diagnostic(ErrorCode.ERR_ImplicitIndexIndexerWithName, "i").WithLocation(2, 7)); } [Fact] public void ImplicitIndexIndexer_34() { // named argument not matching extension this[int] parameter name var src = """ var c = new C(); _ = c[index: ^1]; static class E { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,7): error CS8428: Invocation of implicit Index Indexer cannot name the argument. //_ = c[index: ^1]; Diagnostic(ErrorCode.ERR_ImplicitIndexIndexerWithName, "index").WithLocation(2, 7)); } [Fact] public void ImplicitIndexIndexer_35() { // instance Length takes precedence over instance Count var src = """ var c = new C(); System.Console.Write(c[^1]); class C { public int Length { get { System.Console.Write("Length "); return 3; } } public int Count => throw null; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Length get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_36() { // extension Length takes precedence over extension Count var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int Length { get { System.Console.Write("Length "); return 3; } } public int Count => throw null; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Length get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_37() { // inner extension Count and outer extension Length var src = """ using Outer; namespace Inner { class Program { static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Count { get { System.Console.Write("Count "); return 3; } } public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } } namespace Outer { static class E2 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } } class C { } """; // Note: the `using` is considered used since we look for this[Index] across all scopes before looking for this[int] across all scopes var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Count get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_38() { // generic extension block with Length + this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension<T>(T t) { public int Length => 3; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_39() { // generic extension block with type parameter in return type of indexer var src = """ var c = new C<string>(); System.Console.Write(c[^1]); static class E { extension<T>(C<T> c) { public int Length => 3; public T this[int i] { get { System.Console.Write($"get({i}) "); return c.Items[i]; } } } } class C<T> { public T[] Items = new T[3]; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) "), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_40() { // extension Length + this[int] is not available on base var src = """ class Base { } class Derived : Base { void Main() { _ = base[^1]; _ = (base)[^1]; _ = this[^1]; } } static class E { extension(Base b) { public int Length => 3; public int this[int i] => 0; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (8,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Base' // _ = base[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "base[^1]").WithArguments("Base").WithLocation(8, 13), // (9,14): error CS0175: Use of keyword 'base' is not valid in this context // _ = (base)[^1]; Diagnostic(ErrorCode.ERR_BaseIllegal, "base").WithLocation(9, 14)); } [Fact] public void ImplicitIndexIndexer_41() { // constrained generic extension block with Length + this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension<T>(T t) where T : struct { public int Length => 3; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100) .VerifyEmitDiagnostics( // (2,22): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // System.Console.Write(c[^1]); Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 22)); } [Fact] public void ImplicitIndexIndexer_42() { // constrained generic extension block with Length + this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension<T>(T t) where T : class { public int Length => 3; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_43() { // extension Length applicable to receiver, but extension this[int] is not var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int Length => throw null; } extension(D d) { public int this[int i] { get => throw null; } } } class C { } class D { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_44() { // extension this[int] applicable to receiver, but extension Length is not var src = """ var c = new C(); _ = c[^1]; static class E { extension(D d) { public int Length => throw null; } extension(C c) { public int this[int i] { get => throw null; } } } class C { } class D { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_45() { // two applicable extension Length properties in the same scope var src = """ var c = new C(); _ = c[^1]; static class E1 { extension(C c) { public int Length => 5; public int this[int i] { get => throw null; } } } static class E2 { extension(C c) { public int Length => 3; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_46() { // two applicable extension this[int] indexers in the same scope var src = """ var c = new C(); _ = c[^1]; static class E1 { extension(C c) { public int Length => 5; public int this[int i] { get => throw null; } } } static class E2 { extension(C c) { public int this[int i] { get => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_47() { // Length from extension E1, this[int] from extension E2 var src = """ var c = new C(); System.Console.Write(c[^1]); static class E1 { extension(C c) { public int Length { get { System.Console.Write("Length "); return 3; } } } } static class E2 { extension(C c) { public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Length get(2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_48() { // extension implicit indexer with missing System.Int32 var src = """ System.Index i = default; _ = new object()[i]; static class E { extension(object o) { public int Length => 3; public int this[int i] { get => 42; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.MakeTypeMissing(SpecialType.System_Int32); comp.VerifyEmitDiagnostics( // (2,5): error CS0518: Predefined type 'System.Int32' is not defined or imported // _ = new object()[i]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "new object()[i]").WithArguments("System.Int32").WithLocation(2, 5), // (8,16): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int Length => 3; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(8, 16), // (8,30): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int Length => 3; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "3").WithArguments("System.Int32").WithLocation(8, 30), // (9,16): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int i] { get => 42; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(9, 16), // (9,25): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int i] { get => 42; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(9, 25), // (9,41): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int this[int i] { get => 42; } Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "42").WithArguments("System.Int32").WithLocation(9, 41)); } [Fact] public void ImplicitIndexIndexer_49() { // write to instance set-only this[int] with Length var src = """ var c = new C(); c[^1] = 42; class C { public int Length => 3; public int this[int i] { set { System.Console.Write($"set({i}, {value})"); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set(2, 42)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_50() { // read from instance set-only this[int] with Length var src = """ var c = new C(); _ = c[^1]; class C { public int Length => 3; public int this[int i] { set { } } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (2,5): error CS0154: The property or indexer 'C.this[int]' cannot be used in this context because it lacks a get accessor // _ = c[^1]; Diagnostic(ErrorCode.ERR_PropertyLacksGet, "c[^1]").WithArguments("C.this[int]").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_51() { // read from extension ref-returning this[int] with extension Length var src = """ var c = new C(); System.Console.Write(c[^1]); c[^2] = 42; static class E { extension(C c) { public int Length => 3; public ref int this[int i] { get { System.Console.Write($" get({i}) "); return ref c.Data[i]; } } } } class C { public int[] Data = { 10, 20, 30 }; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("get(2) 30 get(1)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_52() { // extension Length not applicable + extension this[int] var src = """ var c = new C(); _ = c[^1]; static class E { extension(C c) { public int this[int i] { get { System.Console.Write($"get({i})"); return i; } } } extension(D d) { public int Length => 3; } } class C { } class D { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitIndexIndexer_53() { // ambiguous Length extensions + Count extension var src = """ var c = new C(); System.Console.Write(c[^1]); static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; public int Count { get { System.Console.Write("Count "); return 3; } } public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,22): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // System.Console.Write(c[^1]); Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(2, 22)); } [Fact] public void ImplicitIndexIndexer_54() { // ambiguous inner Length extensions + inner Count extension, outer this[Index] extension var src = """ using Outer; namespace Inner { class Program { void M() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; public int Count { get => throw null; } public int this[int i] { get => throw null; } } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Index i] { get => throw null; } } } } class C { } """; // Note: the `using` is considered used since we look for `this[Index]` all the way through before looking at implicit indexers CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void ImplicitIndexIndexer_55() { // ambiguous inner Length extensions + ambiguous inner this[int] extension, outer this[Index] extension var src = """ using Outer; namespace Inner { class Program { void M() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } static class E2 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Index i] { get => throw null; } } } } class C { } """; // Note: the `using` is considered used since we look for `this[Index]` all the way through before looking at implicit indexers CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void ImplicitIndexIndexer_56() { // ambiguous inner Length extensions + one inner this[int] extension, outer this[Index] extension var src = """ using Outer; namespace Inner { class Program { void M() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Index i] { get => throw null; } } } } class C { } """; // Note: the `using` is considered used since we look for `this[Index]` all the way through before looking at implicit indexers CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void ImplicitIndexIndexer_57() { // ambiguous inner Length extensions + no inner this[int] extension, outer this[Index] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("outer 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_58() { // ambiguous inner Length extensions + ambiguous inner this[int] extension, outer Length extension + outer this[int] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } static class E2 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } } namespace Outer { static class E3 { extension(C c) { public int Length => throw null; public int this[int i] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (10,34): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // System.Console.Write(c[^1]); Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(10, 34)); } [Fact] public void ImplicitIndexIndexer_59() { // ambiguous inner Length extensions + one inner this[int] extension, outer Length extension + outer this[int] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } } namespace Outer { static class E3 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (10,34): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // System.Console.Write(c[^1]); Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(10, 34)); } [Fact] public void ImplicitIndexIndexer_60() { // ambiguous inner Length extensions + no inner this[int] extension, outer Length extension + outer this[int] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int Length => 3; public int this[int i] { get => throw null; } } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (10,34): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // System.Console.Write(c[^1]); Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(10, 34)); } [Fact] public void ImplicitIndexIndexer_61() { // ambiguous inner Length extensions + inapplicable inner this[int] extension, outer Length extension + outer this[int] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; } } static class E3 { extension(D d) { public int this[int i] { get => throw null; } } } } namespace Outer { static class E4 { extension(C c) { public int Length => 3; public int this[int i] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } class D { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (10,34): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // System.Console.Write(c[^1]); Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^1]").WithArguments("C").WithLocation(10, 34)); } [Fact] public void ImplicitIndexIndexer_62() { // inner Length extension + ambiguous inner this[int] extension, outer this[Index] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; public int this[int i] { get => throw null; } } } static class E2 { extension(C c) { public int this[int i] { get => throw null; } } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } """; // Note: the `using` is considered used since we look for `this[Index]` all the way through before looking at implicit indexers var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("outer 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_63() { // inner Length extension + inner this[int] extension, outer this[Index] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => 3; public int this[int i] { get { System.Console.Write($"inner "); return 42; } } } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); // Note: the `using` is considered used since we look for `this[Index]` all the way through before looking at implicit indexers CompileAndVerify(comp, expectedOutput: ExpectedOutput("outer 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_64() { // inner Length extension + no inner this[int] extension, outer this[Index] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("outer 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_65() { // inner Length extension + inapplicable inner this[int] extension, outer this[Index] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(D d) { public int this[int i] { get { System.Console.Write($"outer "); return 42; } } } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } class D { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("outer 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_66() { // instance Length + inapplicable instance this[int], this[Index] extension var src = """ var c = new C(); System.Console.Write(c[^1]); static class E { extension(C c) { public int this[System.Index i] { get { System.Console.Write($"outer "); return 42; } } } } class C { public int Length => 3; public int this[int i, object o = null] { get => throw null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("outer 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_67() { // Use-site diagnostic for extension this[Index] and this[Range] var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public static class E { extension(object o) { public int Length => 1; public Missing this[System.Index i] => throw null; public Missing this[System.Range r] => throw null; public int this[int i] => 42; public int Slice(int i, int j) => 43; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ var o = new object(); _ = o[^1]; _ = o[..]; """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (2,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 5), // (3,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[..]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 5) ); } [Fact] public void ImplicitIndexIndexer_68() { // Use-site diagnostic for extension this[int] and Slice(int, int) var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(object o) { public int Length => throw null; public Missing this[int i] => throw null; public Missing Slice(int i, int j) => throw null; } } namespace Outer { public static class E2 { extension(object o) { public int Length => throw null; public int this[System.Index i] => throw null; public object this[System.Range r] => throw null; } } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o[^1]; _ = o[..]; """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 5), // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[..]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5) ); } [Fact] public void ImplicitIndexIndexer_69() { // Use-site diagnostic for extension this[Missing] var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(object o) { public int Length => 1; public int this[Missing m] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o[^1]; _ = o[..]; namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] => throw null; public object this[System.Range r] => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 5), // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[..]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5) ); } [Fact] public void ImplicitIndexIndexer_70() { // Use-site on return type of incomplete implicit indexer pair var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(object o) { public Missing this[int i] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o[^1]; _ = o[..]; namespace Outer { public static class E2 { extension(object o) { public int Length => 1; public int this[int i] => throw null; public int Slice(int i, int j) => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 5), // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[..]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5) ); } [Fact] public void ImplicitIndexIndexer_71() { var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(Missing m) { public int Length => 1; } extension(object o) { public Missing this[int i] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o[^1]; _ = o[..]; namespace Outer { public static class E2 { extension(object o) { public int Length => 1; public int this[int i] => throw null; public int Slice(int i, int j) => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 5), // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[..]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5) ); } [Fact] public void ImplicitIndexIndexer_72() { // Use-site diagnostic for extension this[Index], this[Range] var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(object o) { public int Length => throw null; public Missing this[System.Index i] => throw null; public Missing this[System.Range r] => throw null; public int this[int i] => throw null; public int Slice(int i, int j) => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ var o = new object(); _ = o[^1]; _ = o[..]; """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (2,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 5), // (3,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[..]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 5)); } [Fact] public void ImplicitIndexIndexer_73() { // Use-site diagnostic for extension this[Index], this[Range], ambiguous outer extension this[Index] and this[Range] var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(object o) { public int Length => throw null; public Missing this[System.Index i] => throw null; public Missing this[System.Range r] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o[^1]; _ = o[..]; namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] => throw null; public object this[System.Range r] => throw null; } } public static class E3 { extension(object o) { public int this[System.Index i] => throw null; public object this[System.Range r] => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,5): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[^1]").WithArguments("object").WithLocation(4, 5), // (4,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[^1]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[^1]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 5), // (5,5): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o[..]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[..]").WithArguments("object").WithLocation(5, 5), // (5,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o[..]; Diagnostic(ErrorCode.ERR_NoTypeDef, "o[..]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(5, 5)); } [Fact] public void ImplicitIndexIndexer_74() { // Use-site diagnostic for extension Length + extension this[int] + extension Slice(int, int) var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(object o) { public Missing Length => throw null; public int this[int i] => throw null; public object Slice(int i, int j) => throw null; } } namespace Outer { public static class E2 { extension(object o) { public int Length => 3; public int this[System.Index i] { get { System.Console.Write($"{i}, "); return 0; } } public object this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o[^1]; _ = o[..]; """; var comp = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1, 0..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_75() { // Use-site diagnostic for instance Length + extension this[int] + extension Slice(int, int) var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing") .EmitToImageReference(); var lib2_cs = """ public class C { public Missing Length => throw null; } public static class E1 { extension(C c) { public int this[int i] => throw null; public object Slice(int i, int j) => throw null; } } namespace Outer { public static class E2 { extension(C c) { public int Length => 3; public int this[System.Index i] { get { System.Console.Write($"{i}, "); return 0; } } public object this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var c = new C(); _ = c[^1]; _ = c[..]; """; var comp = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1, 0..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_76() { // receiver side effects with instance Length and extension this[int] var src = """ int count = 0; System.Console.Write(GetC()[^1]); GetC()[^2] = 10; GetC()[^3] += 10; C GetC() { count++; System.Console.Write($" receiver({count}) "); return new C(); } static class E { extension(C c) { public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } set { System.Console.Write($"set({i}, {value}) "); } } } } class C { public int Length => 4; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("receiver(1) get(3) 42 receiver(2) set(2, 10) receiver(3) get(1) set(1, 52)"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_77() { // object initializer with instance Length and extension this[int] var src = """ _ = new C() { [^1] = 42 }; static class E { extension(C c) { public int this[int i] { set { System.Console.Write($"set({i}, {value})"); } } } } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set(2, 42)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_78() { // instance Count + extension Length + extension this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); c[^2] = 10; static class E { extension(C c) { public int Length { get { System.Console.Write("extLength "); return 100; } } public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } set { System.Console.Write($"set({i}, {value}) "); } } } } class C { public int Count { get { System.Console.Write(" instCount "); return 100; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instCount get(99) 42 instCount set(98, 10)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_79() { // instance Length + extension Count + extension this[int] var src = """ var c = new C(); System.Console.Write(c[^1]); c[^2] = 10; static class E { extension(C c) { public int Count => throw null; public int this[int i] { get { System.Console.Write($"get({i}) "); return 42; } set { System.Console.Write($"set({i}, {value}) "); } } } } class C { public int Length { get { System.Console.Write("instCount "); return 5; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instCount get(4) 42instCount set(3, 10) "), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexer_80() { // extension Length in inner scope + extension this[int] in inner scope + extension MUnused() in outer scope var src = """ using Outer; namespace Inner { class Program { void Main() { _ = new C()[^1]; } } static class E1 { extension(C c) { public int Length => throw null; public int this[int i] => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int MUnused() => throw null; } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (1,1): hidden CS8019: Unnecessary using directive. // using Outer; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using Outer;").WithLocation(1, 1)); } [Fact] public void ImplicitRangeIndexer_01() { // instance implicit indexer (instance scope) takes precedence over extension this[Range] (extension scope) var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension(C c) { public int this[System.Range r] { get => throw null; } } } class C { public int Length => 5; public C Slice(int start, int length) { System.Console.Write($"instance({start}, {length}) "); return new C(); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instance(1, 3) C"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_02() { // extension Length + instance Slice var src = """ var c = new C(); System.Console.Write(c[0..^1]); static class E { extension(C c) { public int Length => 3; } } class C { public int Slice(int i, int j) { System.Console.Write($"Slice({i}, {j}) "); return 42; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(0, 2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_03() { // extension Slice + instance Length var src = """ var c = new C(); System.Console.Write(c[0..^1]); static class E { extension(C c) { public int Slice(int i, int j) { System.Console.Write($"Slice({i}, {j}) "); return 42; } } } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(0, 2) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_04() { // extension Length + extension Slice var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension(C c) { public int Length => 5; public C Slice(int start, int length) { System.Console.Write($"Slice({start}, {length}) "); return new C(); } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(1, 3) C"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_05() { // extension Count + extension Slice var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension(C c) { public int Count => 5; public C Slice(int start, int length) { System.Console.Write($"Slice({start}, {length}) "); return new C(); } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(1, 3) C"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_06() { // outer extension Length + inner extension Slice var src = """ using Outer; namespace Inner { class Program { void Main() { _ = new C()[0..^1]; } } static class E1 { extension(C c) { public int Slice(int start, int length) => throw null; } } } namespace Outer { static class E2 { extension(C c) { public int Length => throw null; } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void ImplicitRangeIndexer_07() { // extension this[Range] is preferred over extension Length + extension Slice var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension(C c) { public int Length => throw null; public int Slice(int start, int length) => throw null; public int this[System.Range r] { get { System.Console.Write($"real({r}) "); return 42; } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("real(1..^1) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_08() { // instance Length + instance Slice preferred over extension Length + extension Slice var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension(C c) { public int Length => throw null; public C Slice(int start, int length) => throw null; } } class C { public int Length => 5; public C Slice(int start, int length) { System.Console.Write($"instance({start}, {length}) "); return new C(); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instance(1, 3) C"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_09() { // instance Length + instance this[string] and extension this[Range] var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension(C c) { public int this[System.Range r] { get { System.Console.Write($"ext({r}) "); return 42; } } } } class C { public int Length => throw null; public int this[string s] { get => throw null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ext(1..^1) 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_10() { // extension Length + extension Slice with receiver conversion var src = """ var d = new Derived(); System.Console.Write(d[1..^1]); static class E { extension(Base b) { public int Length => 5; public string Slice(int start, int length) { System.Console.Write($"slice({start},{length}) "); return "ok"; } } } class Base { } class Derived : Base { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("slice(1,3) ok"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_11() { // extension(Base) Length + extension(Derived) Slice var src = """ var d = new Derived(); System.Console.Write(d[1..^1]); static class E { extension(Base b) { public int Length => 5; } extension(Derived d) { public string Slice(int start, int length) { System.Console.Write($"slice({start},{length}) "); return "ok"; } } } class Base { } class Derived : Base { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("slice(1,3) ok"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_12() { // extension Length + static extension Slice var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public int Length => throw null; public static string Slice(int start, int length) => throw null; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_13() { // extension Length + extension property named Slice var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public int Length => throw null; public string Slice => throw null; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_14() { // extension Length + extension void-returning Slice(int, int) var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public int Length => throw null; public void Slice(int start, int length) { } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_15() { // extension Length + extension Slice(int) var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public int Length => throw null; public string Slice(int start) => throw null; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_16() { // extension Length + extension Slice(int, int = 0) var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension(C c) { public int Length => 5; public string Slice(int start, int length = 0) { System.Console.Write($"Slice({start}, {length}) "); return "ok"; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(1, 3) ok"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); // instance Length + instance Slice(int, int = 0) var src2 = """ System.Console.Write(new C()[1..^1]); class C { public int Length => 5; public string Slice(int start, int length = 0) { System.Console.Write($"Slice({start}, {length}) "); return "ok"; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); CompileAndVerify(comp2, expectedOutput: ExpectedOutput("Slice(1, 3) ok"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_17() { // extension Length + extension Slice(string, int) var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public int Length => throw null; public string Slice(string start, int length) => throw null; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_18() { // extension Length + extension Slice(int, string) var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public int Length => throw null; public string Slice(int start, string length) => throw null; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_19() { // extension Length + extension private Slice var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public int Length => throw null; private string Slice(int start, int length) => throw null; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_20() { // extension Length takes precedence over extension Count var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension(C c) { public int Length { get { System.Console.Write("Length "); return 5; } } public int Count => throw null; public string Slice(int start, int length) { System.Console.Write($"slice({start},{length}) "); return "ok"; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Length slice(1,3) ok"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_21() { // generic extension block with Length + Slice var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E { extension<T>(T t) { public int Length => 5; public T Slice(int start, int length) { System.Console.Write($"Slice({start}, {length}) "); return t; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(1, 3) C"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_22() { // extension Length + Slice is not available on base var src = """ class Base { } class Derived : Base { void Main() { _ = base[1..^1]; _ = (base)[1..^1]; _ = this[1..^1]; } } static class E { extension(Base b) { public int Length => 3; public int Slice(int start, int length) => 0; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (8,13): error CS0021: Cannot apply indexing with [] to an expression of type 'Base' // _ = base[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "base[1..^1]").WithArguments("Base").WithLocation(8, 13), // (9,14): error CS0175: Use of keyword 'base' is not valid in this context // _ = (base)[1..^1]; Diagnostic(ErrorCode.ERR_BaseIllegal, "base").WithLocation(9, 14)); } [Fact] public void ImplicitRangeIndexer_24() { // extension Length applicable to receiver, but extension Slice is not var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public int Length => throw null; } extension(D d) { public D Slice(int start, int length) => throw null; } } class C { } class D { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_25() { // extension Slice applicable to receiver, but extension Length is not var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(D d) { public int Length => throw null; } extension(C c) { public C Slice(int start, int length) => throw null; } } class C { } class D { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' //_ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_26() { // ambiguous extension Slice var src = """ var c = new C(); _ = c[1..^1]; static class E1 { extension(C c) { public int Length => 5; public C Slice(int start, int length) => throw null; } } static class E2 { extension(C c) { public C Slice(int start, int length) => throw null; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(C).Slice(int, int)' and 'E2.extension(C).Slice(int, int)' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_AmbigCall, "c[1..^1]").WithArguments("E1.extension(C).Slice(int, int)", "E2.extension(C).Slice(int, int)").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_27() { // Length from extension E1, Slice from extension E2 var src = """ var c = new C(); System.Console.Write(c[1..^1]); static class E1 { extension(C c) { public int Length => 5; } } static class E2 { extension(C c) { public C Slice(int start, int length) { System.Console.Write($"Slice({start}, {length}) "); return new C(); } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(1, 3) C"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_28() { // extension implicit range indexer with missing System.Int32 var src = """ System.Range r = default; _ = new object()[r]; static class E { extension(object o) { public int Length => 5; public object Slice(int start, int length) => null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.MakeTypeMissing(SpecialType.System_Int32); comp.VerifyEmitDiagnostics( // (2,5): error CS0518: Predefined type 'System.Int32' is not defined or imported // _ = new object()[r]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "new object()[r]").WithArguments("System.Int32").WithLocation(2, 5), // (2,5): error CS0518: Predefined type 'System.Int32' is not defined or imported // _ = new object()[r]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "new object()[r]").WithArguments("System.Int32").WithLocation(2, 5), // (2,5): error CS0518: Predefined type 'System.Int32' is not defined or imported // _ = new object()[r]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "new object()[r]").WithArguments("System.Int32").WithLocation(2, 5), // (2,5): error CS0518: Predefined type 'System.Int32' is not defined or imported // _ = new object()[r]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "new object()[r]").WithArguments("System.Int32").WithLocation(2, 5), // (8,16): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int Length => 5; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(8, 16), // (8,30): error CS0518: Predefined type 'System.Int32' is not defined or imported // public int Length => 5; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "5").WithArguments("System.Int32").WithLocation(8, 30), // (9,29): error CS0518: Predefined type 'System.Int32' is not defined or imported // public object Slice(int start, int length) => null; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(9, 29), // (9,40): error CS0518: Predefined type 'System.Int32' is not defined or imported // public object Slice(int start, int length) => null; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "int").WithArguments("System.Int32").WithLocation(9, 40)); } [Fact] public void ImplicitRangeIndexer_29() { // ambiguous inner extension Slice, extension outer Slice var src = """ using Outer; namespace Inner { class Program { void Main() { var c = new C(); _ = c[1..^1]; } } static class E1 { extension(C c) { public int Length => 5; public C Slice(int start, int length) => throw null; } } static class E2 { extension(C c) { public C Slice(int start, int length) => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int Length => throw null; public C Slice(int start, int length) => throw null; } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Inner.E1.extension(C).Slice(int, int)' and 'Inner.E2.extension(C).Slice(int, int)' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_AmbigCall, "c[1..^1]").WithArguments("Inner.E1.extension(C).Slice(int, int)", "Inner.E2.extension(C).Slice(int, int)").WithLocation(10, 17)); } [Fact] public void ImplicitRangeIndexer_30() { // inapplicable instance Slice + instance Length, outer this[Range] extension var src = """ var c = new C(); _ = c[1..^1]; static class E1 { extension(C c) { public C this[System.Range r] { get { System.Console.Write("ran"); return null; } } } } class C { public int Length => throw null; public C Slice(int i, int j, int k = 0) => throw null; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran"), verify: Verification.FailsPEVerify); } [Fact] public void ImplicitRangeIndexer_31() { // extension Slice method + extension Length var src = """ var c = new C(); _ = c[1..^1]; static class E1 { extension(C c) { public int Length => 3; } public static C Slice(this C c, int i, int j) { System.Console.Write("ran"); return c; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_32() { // inner extension Length + extension this[int] + inapplicable extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { var c = new C(); _ = c[1..^1]; } } public static class E1 { extension(D d) { public int Slice(int i, int j) => throw null; } extension(C c) { public int Length => 3; public int this[int i] { get => throw null; } } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_33() { // ambiguous inner Length extensions + ambiguous inner Slice extension, outer this[Range] extension var src = """ using Outer; namespace Inner { class Program { void M() { var c = new C(); System.Console.Write(c[1..^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; public int Count { get => throw null; } public int Slice(int start, int length) => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Range r] { get => throw null; } } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void ImplicitRangeIndexer_34() { // ambiguous inner Length extensions + ambiguous inner Slice extension, outer this[Range] extension var src = """ using Outer; namespace Inner { class Program { void M() { var c = new C(); System.Console.Write(c[1..^1]); } } static class E1 { extension(C c) { public int Length => throw null; public int Slice(int start, int length) => throw null; } } static class E2 { extension(C c) { public int Length => throw null; public int Slice(int start, int length) => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Range r] { get => throw null; } } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void ImplicitRangeIndexer_35() { // ambiguous inner Length extensions + one inner Slice extension, outer this[Range] extension var src = """ using Outer; namespace Inner { class Program { void M() { var c = new C(); System.Console.Write(c[1..^1]); } } static class E1 { extension(C c) { public int Length => throw null; } } static class E2 { extension(C c) { public int Length => throw null; public int Slice(int start, int length) => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Range r] { get => throw null; } } } } class C { } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void ImplicitRangeIndexer_36() { // inner Length extension + ambiguous inner Slice extension, outer this[Range] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[1..^1]); } } static class E1 { extension(C c) { public int Length => throw null; public int Slice(int start, int length) => throw null; } } static class E2 { extension(C c) { public int Slice(int start, int length) => throw null; } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Range r] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("outer 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_37() { // inner Length extension + inner Slice extension, outer this[Range] extension var src = """ using Outer; namespace Inner { public static class Program { public static void Main() { var c = new C(); System.Console.Write(c[1..^1]); } } static class E1 { extension(C c) { public int Length => 3; public int Slice(int start, int length) { System.Console.Write($"inner "); return 42; } } } } namespace Outer { static class E3 { extension(C c) { public int this[System.Range r] { get { System.Console.Write($"outer "); return 42; } } } } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("outer 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_38() { // receiver side effects with instance Length and extension Slice var src = """ int count = 0; System.Console.Write(GetC()[1..^1]); System.Console.Write($" count={count}"); C GetC() { count++; System.Console.Write($"receiver({count}) "); return new C(); } static class E { extension(C c) { public string Slice(int start, int length) { System.Console.Write($"slice({start}, {length}) "); return "ok"; } } } class C { public int Length => 5; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("receiver(1) slice(1, 3) ok count=1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_39() { // from-end range argument evaluation order with instance Length and extension Slice var src = """ var c = new C(); System.Console.Write(c[^M()..^N()]); int M() { System.Console.Write("M "); return 4; } int N() { System.Console.Write("N "); return 1; } static class E { extension(C c) { public string Slice(int start, int length) { System.Console.Write($"slice({start}, {length}) "); return "ok"; } } } class C { public int Length { get { System.Console.Write("Length "); return 10; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("M N Length slice(6, 3) ok"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_40() { // instance Count + extension Length + extension Slice var src = """ var c = new C(); System.Console.Write(c[0..^1]); static class E { extension(C c) { public int Length { get { System.Console.Write("extLength "); return 100; } } public string Slice(int start, int length) { System.Console.Write($"slice({start}, {length}) "); return "ok"; } } } class C { public int Count { get { System.Console.Write("instCount "); return 5; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instCount slice(0, 4) ok"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexer_41() { // instance Length + extension Count + extension Slice var src = """ var c = new C(); System.Console.Write(c[0..^1]); static class E { extension(C c) { public int Count => throw null; public string Slice(int start, int length) { System.Console.Write($"slice({start}, {length}) "); return "ok"; } } } class C { public int Length { get { System.Console.Write("instLength "); return 5; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instLength slice(0, 4) ok"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82756")] public void ImplicitRangeIndexer_42() { // array type (with and without Length), extension this[Index] + extension this[Range] var src = """ public static class E { public static void Main() { int[] i = [1, 2, 3]; M1(i); M2(i); } public static void M1(int[] i) { System.Console.Write(i[^1]); } public static void M2(int[] i) { System.Console.Write(i[1..^1][0]); } extension(int[] a) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("32"), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("E.M1", """ { // Code size 15 (0xf) .maxstack 3 IL_0000: nop IL_0001: ldarg.0 IL_0002: dup IL_0003: ldlen IL_0004: conv.i4 IL_0005: ldc.i4.1 IL_0006: sub IL_0007: ldelem.i4 IL_0008: call "void System.Console.Write(int)" IL_000d: nop IL_000e: ret } """); verifier.VerifyIL("E.M2", """ { // Code size 34 (0x22) .maxstack 4 IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.1 IL_0003: call "System.Index System.Index.op_Implicit(int)" IL_0008: ldc.i4.1 IL_0009: ldc.i4.1 IL_000a: newobj "System.Index..ctor(int, bool)" IL_000f: newobj "System.Range..ctor(System.Index, System.Index)" IL_0014: call "int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<int>(int[], System.Range)" IL_0019: ldc.i4.0 IL_001a: ldelem.i4 IL_001b: call "void System.Console.Write(int)" IL_0020: nop IL_0021: ret } """); var corlib = CreateEmptyCompilation(corlibWithNoArrayMembersSource); corlib.VerifyDiagnostics(); Assert.Null(corlib.GetSpecialTypeMember(SpecialMember.System_Array__Length)); var corlibRef = corlib.EmitToImageReference(); // array type without Length, extension this[Index] + extension this[Range] var src2 = """ public static class E { public static void M1(int[] i) { _ = i[^1]; } public static void M2(int[] i) { _ = i[1..^1]; } extension(int[] a) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } """; // Note: ldlen is used even when Array.Length is missing var comp2 = CreateEmptyCompilation(src2, references: [corlibRef]); comp2.VerifyEmitDiagnostics(); var verifier2 = CompileAndVerify(comp2, verify: Verification.FailsPEVerify); verifier2.VerifyIL("E.M1", """ { // Code size 9 (0x9) .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: pop IL_0008: ret } """); verifier2.VerifyIL("E.M2", """ { // Code size 26 (0x1a) .maxstack 4 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: call "System.Index System.Index.op_Implicit(int)" IL_0007: ldc.i4.1 IL_0008: ldc.i4.1 IL_0009: newobj "System.Index..ctor(int, bool)" IL_000e: newobj "System.Range..ctor(System.Index, System.Index)" IL_0013: call "int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<int>(int[], System.Range)" IL_0018: pop IL_0019: ret } """); // array type without Length src2 = """ public static class E { public static void M1(int[] i) { _ = i[^1]; } public static void M2(int[] i) { _ = i[1..^1]; } } """; // Tracked by https://github.com/dotnet/roslyn/issues/82756 // Missing diagnostic about missing Array.Length comp2 = CreateEmptyCompilation(src2, references: [corlibRef]); comp2.VerifyEmitDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/82756")] public void ImplicitRangeIndexer_43() { // array type (with and without Length), extension Length + extension this[Index] + extension this[Range] var src = """ public static class E { public static void Main() { int[] i = [1, 2, 3]; M1(i); M2(i); } public static void M1(int[] i) { System.Console.Write(i[^1]); } public static void M2(int[] i) { System.Console.Write(i[1..^1][0]); } extension(int[] a) { public int Length => throw null; public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("32"), verify: Verification.Skipped).VerifyDiagnostics(); var corlib = CreateEmptyCompilation(corlibWithNoArrayMembersSource); corlib.VerifyDiagnostics(); Assert.Null(corlib.GetSpecialTypeMember(SpecialMember.System_Array__Length)); var corlibRef = corlib.EmitToImageReference(); // array type without Length, extension Length + extension this[Index] + extension this[Range] var src2 = """ public static class E { public static void M1(int[] i) { _ = i[^1]; } public static void M2(int[] i) { _ = i[1..^1]; } extension(int[] a) { public int Length => throw null; public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } """; var comp2 = CreateEmptyCompilation(src2, references: [corlibRef]); comp2.VerifyDiagnostics(); // Note: ldlen is used even when Array.Length is missing var verifier = CompileAndVerify(comp2, verify: Verification.Skipped); verifier.VerifyIL("E.M1", """ { // Code size 9 (0x9) .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: pop IL_0008: ret } """); verifier.VerifyIL("E.M2", """ { // Code size 26 (0x1a) .maxstack 4 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: call "System.Index System.Index.op_Implicit(int)" IL_0007: ldc.i4.1 IL_0008: ldc.i4.1 IL_0009: newobj "System.Index..ctor(int, bool)" IL_000e: newobj "System.Range..ctor(System.Index, System.Index)" IL_0013: call "int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<int>(int[], System.Range)" IL_0018: pop IL_0019: ret } """); } [Fact] public void ImplicitRangeIndexer_44() { // static extension Slice method + instance Length var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public static C Slice(int i, int j) => throw null; } } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_45() { // extension Slice methods with wrong signatures + instance Length var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public C Slice(int i) => throw null; public C Slice(int i, int j, int k = 0) => throw null; } } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_46() { // classic extension Slice methods with wrong signatures + instance Length var src = """ var c = new C(); _ = c[1..^1]; static class E { public static C Slice(this C c, int i) => throw null; public static C Slice(this C c, int i, int j, int k = 0) => throw null; } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_47() { // classic extension Slice method with object parameters + instance Length var src = """ var c = new C(); _ = c[1..^1]; static class E { public static C Slice(this C c, object o1, object o2) => throw null; } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_48() { // extension Slice method with object parameters + instance Length var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public C Slice(object o1, object o2) => throw null; } } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_49() { // classic extension Slice method with dynamic parameters + instance Length var src = """ var c = new C(); _ = c[1..^1]; static class E { public static C Slice(this C c, dynamic d1, dynamic d2) => throw null; } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void ImplicitRangeIndexer_50() { // extension Slice method with dynamic parameters + instance Length var src = """ var c = new C(); _ = c[1..^1]; static class E { extension(C c) { public C Slice(dynamic d1, dynamic d2) => throw null; } } class C { public int Length => 3; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(2, 5)); } [Fact] public void SpecialImplicitIndexIndexer_01() { // string lacks Length and this[int] but defines this[Index] indexers var corlibSource = """ namespace System { public class String { public string this[Index i] { get => throw null; } } public class Object { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class Attribute { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } """; var corlib = CreateEmptyCompilation(corlibSource); corlib.VerifyDiagnostics(); var corlibRef = corlib.EmitToImageReference(); // index into string var src1 = """ public class C { public static void M(string s) { _ = s[^1]; } } """; var comp1 = CreateEmptyCompilation(src1, references: [corlibRef]); var verifier1 = CompileAndVerify(comp1, verify: Verification.Skipped).VerifyDiagnostics(); verifier1.VerifyIL("C.M", """ { // Code size 15 (0xf) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: ldc.i4.1 IL_0003: newobj "System.Index..ctor(int, bool)" IL_0008: callvirt "string string.this[System.Index].get" IL_000d: pop IL_000e: ret } """); } [Fact] public void SpecialImplicitIndexIndexer_02() { // string and Array lack members, but various extensions var corlibSource = """ namespace System { public class String { } public class Array { } public struct Char { } public class Object { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class Attribute { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } """; var corlib = CreateEmptyCompilation(corlibSource); corlib.VerifyDiagnostics(); var corlibRef = corlib.EmitToImageReference(); // extension this[Index] on string var src = """ public class C { public static void M(string s) { _ = s[^1]; } } public static class E { extension(string s) { public char this[System.Index i] => throw null; } } """; var comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[^1]").WithArguments("string").WithLocation(5, 13)); // extension this[int] and Length on string src = """ public class C { public static void M(string s) { _ = s[^1]; } } public static class E { extension(string s) { public int Length => throw null; public char this[int i] => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[^1]").WithArguments("string").WithLocation(5, 13)); // extension this[Index] on Array src = """ public class C { public static void M(int[] arr) { _ = arr[^1]; } } public static class E { extension(System.Array arr) { public char this[System.Index i] => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 9 (0x9) .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: pop IL_0008: ret } """); // extension this[Index] on int[] src = """ public class C { public static void M(int[] arr) { _ = arr[^1]; } } public static class E { extension(int[] arr) { public char this[System.Index i] => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 9 (0x9) .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: pop IL_0008: ret } """); } [Fact] public void SpecialImplicitRangeIndexer_01() { // string and Array lack Length and GetSubArray/GetSubstring but define this[Range] indexers var corlibSource = """ namespace System { public class String { public string this[Range r] { get => throw null; } } public class Array { public object this[Range r] { get => throw null; } } public class Object { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class Attribute { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } """; var corlib = CreateEmptyCompilation(corlibSource); corlib.VerifyDiagnostics(); var corlibRef = corlib.EmitToImageReference(); // slice string var src1 = """ public class C { public static void M(string s) { _ = s[1..^1]; } } """; var comp1 = CreateEmptyCompilation(src1, references: [corlibRef]); var verifier1 = CompileAndVerify(comp1, verify: Verification.Skipped).VerifyDiagnostics(); verifier1.VerifyIL("C.M", """ { // Code size 26 (0x1a) .maxstack 4 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: call "System.Index System.Index.op_Implicit(int)" IL_0007: ldc.i4.1 IL_0008: ldc.i4.1 IL_0009: newobj "System.Index..ctor(int, bool)" IL_000e: newobj "System.Range..ctor(System.Index, System.Index)" IL_0013: callvirt "string string.this[System.Range].get" IL_0018: pop IL_0019: ret } """); // slice array var src2 = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } """; var comp2 = CreateEmptyCompilation(src2, references: [corlibRef]); comp2.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); } [Fact] public void SpecialImplicitRangeIndexer_02() { // slice pattern, string and Array lack GetSubArray/GetSubstring but define Length, this[int], and this[Range] var corlibSource = """ namespace System { public class String { public int Length => throw null; public char this[int i] => throw null; public string this[Range r] { get => throw null; } } public class Array { public int Length => throw null; public object this[Range r] { get => throw null; } } public class Object { } public struct Char { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class Attribute { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } """; var corlib = CreateEmptyCompilation(corlibSource); corlib.VerifyDiagnostics(); var corlibRef = corlib.EmitToImageReference(); // slice pattern on string var src1 = """ public class C { public static void M(string s) { _ = s is [_, .. var x]; } } """; var comp1 = CreateEmptyCompilation(src1, references: [corlibRef]); var verifier1 = CompileAndVerify(comp1, verify: Verification.Skipped).VerifyDiagnostics(); verifier1.VerifyIL("C.M", """ { // Code size 44 (0x2c) .maxstack 4 IL_0000: ldarg.0 IL_0001: brfalse.s IL_0029 IL_0003: ldarg.0 IL_0004: callvirt "int string.Length.get" IL_0009: ldc.i4.1 IL_000a: blt.s IL_0029 IL_000c: ldarg.0 IL_000d: ldc.i4.1 IL_000e: ldc.i4.0 IL_000f: newobj "System.Index..ctor(int, bool)" IL_0014: ldc.i4.0 IL_0015: ldc.i4.1 IL_0016: newobj "System.Index..ctor(int, bool)" IL_001b: newobj "System.Range..ctor(System.Index, System.Index)" IL_0020: callvirt "string string.this[System.Range].get" IL_0025: pop IL_0026: ldc.i4.1 IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ret } """); // slice pattern on array var src2 = """ public class C { public static void M(int[] arr) { _ = arr is [_, .. var x]; } } """; var comp2 = CreateEmptyCompilation(src2, references: [corlibRef]); comp2.VerifyEmitDiagnostics( // (5,24): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr is [_, .. var x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var x").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 24)); } [Fact] public void SpecialImplicitRangeIndexer_03() { // slice pattern, string and Array lack GetSubArray/GetSubstring, but various extensions are available var corlibSource = """ namespace System { public class String { } public class Array { } public class Object { } public struct Char { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class Attribute { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } """; var corlib = CreateEmptyCompilation(corlibSource); corlib.VerifyDiagnostics(); var corlibRef = corlib.EmitToImageReference(); // slice pattern on string with extension indexers (real) var src = """ public class C { public static void M(string s) { _ = s is [_, .. var x]; } } public static class E { extension(string s) { public int Length => throw null; public char this[System.Index i] => throw null; public string this[System.Range r] { get => throw null; } } } """; CreateEmptyCompilation(src, references: [corlibRef]).VerifyEmitDiagnostics( // (5,18): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s is [_, .. var x]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[_, .. var x]").WithArguments("string").WithLocation(5, 18), // (5,22): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s is [_, .. var x]; Diagnostic(ErrorCode.ERR_BadIndexLHS, ".. var x").WithArguments("string").WithLocation(5, 22)); // slice pattern on string with extension indexers (implicit) src = """ public class C { public static void M(string s) { _ = s is [_, .. var x]; } } public static class E { extension(string s) { public int Length => throw null; public char this[int i] => throw null; public string Slice(int i, int j) => throw null; } } """; CreateEmptyCompilation(src, references: [corlibRef]).VerifyEmitDiagnostics( // (5,18): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s is [_, .. var x]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[_, .. var x]").WithArguments("string").WithLocation(5, 18), // (5,22): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s is [_, .. var x]; Diagnostic(ErrorCode.ERR_BadIndexLHS, ".. var x").WithArguments("string").WithLocation(5, 22)); // slice pattern on array, extension indexers (real) src = """ public class C { public static void M(int[] arr) { _ = arr is [_, .. var x]; } } public static class E { extension(int[] arr) { public int Length => throw null; public object this[System.Range r] { get => throw null; } } } """; CreateEmptyCompilation(src, references: [corlibRef]).VerifyEmitDiagnostics( // (5,24): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr is [_, .. var x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var x").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 24)); // slice pattern on array, extension indexers (implicit) src = """ public class C { public static void M(int[] arr) { _ = arr is [_, .. var x]; } } public static class E { extension(int[] arr) { public int Length => throw null; public object Slice(int i, int j) => throw null; } } """; CreateEmptyCompilation(src, references: [corlibRef]).VerifyEmitDiagnostics( // (5,24): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr is [_, .. var x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, ".. var x").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 24)); } [Fact] public void SpecialImplicitRangeIndexer_04() { // string and Array lack Length and GetSubArray/GetSubstring, but various extensions are available var corlibSource = """ namespace System { public class String { } public class Array { } public class Object { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class Attribute { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } """; var corlib = CreateEmptyCompilation(corlibSource); corlib.VerifyDiagnostics(); var corlibRef = corlib.EmitToImageReference(); // no extension var src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } """; var comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(string).this[Range] indexer src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } public static class E { extension(string s) { public string this[System.Range r] { get => throw null; } } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(string).GetSubstring and Length src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } public static class E { extension(string s) { public int Length => throw null; public string GetSubstring(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(string).Slice and Length src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } public static class E { extension(string s) { public int Length => throw null; public string Slice(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(Array).this[Range] indexer src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(System.Array a) { public string this[System.Range r] { get => throw null; } } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); // extension(Array).GetSubArray and Length src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(System.Array a) { public int Length => throw null; public string GetSubArray(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); // extension(Array).Slice and Length src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(System.Array a) { public int Length => throw null; public string Slice(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); // extension(int[]).this[Range] indexer src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(int[] a) { public string this[System.Range r] { get => throw null; } } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); // extension(int[]).GetSubArray and Length src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(int[] a) { public int Length => throw null; public string GetSubArray(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); // extension(int[]).Slice and Length src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(int[] a) { public int Length => throw null; public string Slice(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); } [Fact] public void SpecialImplicitRangeIndexer_05() { // string and Array lack GetSubArray/GetSubstring, but various extensions are available var corlibSource = """ namespace System { public class String { public int Length => throw null; } public class Array { public int Length => throw null; } public class Object { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class Attribute { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } """; var corlib = CreateEmptyCompilation(corlibSource); corlib.VerifyDiagnostics(); var corlibRef = corlib.EmitToImageReference(); // no extension on string var src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } """; var comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(string).this[Range] indexer src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } public static class E { extension(string s) { public string this[System.Range r] { get => throw null; } } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(string).GetSubstring src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } public static class E { extension(string s) { public string GetSubstring(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(string).Slice src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } public static class E { extension(string s) { public string Slice(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(Array).this[Range] indexer src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(System.Array a) { public string this[System.Range r] { get => throw null; } } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); // extension(Array).GetSubArray src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(System.Array a) { public string GetSubArray(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); // extension(Array).Slice src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(System.Array a) { public string Slice(int i, int j) => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); } [Fact] public void SpecialImplicitRangeIndexer_06() { // string and Array lack Length, but various extensions are available var corlibSource = """ namespace System { public class String { public string GetSubstring(int i, int j) => throw null; } public class Array { public object GetSubArray(int i, int j) => throw null; } public class Object { } public class ValueType { } public struct Void { } public struct Int32 { } public struct Boolean { } public class Attribute { } public class Enum { } public class Exception { } public class NotSupportedException : Exception { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) => throw null; public bool AllowMultiple { get => throw null; set => throw null; } public bool Inherited { get => throw null; set => throw null; } } public enum AttributeTargets { All = 0x7fff } public readonly struct Index { public Index(int value, bool fromEnd = false) => throw null; public int Value => throw null; public bool IsFromEnd => throw null; public int GetOffset(int length) => throw null; public static implicit operator Index(int value) => throw null; } public readonly struct Range { public Index Start => throw null; public Index End => throw null; public Range(Index start, Index end) => throw null; } } namespace System.Reflection { public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string memberName) => throw null; } } namespace System.Runtime.CompilerServices { public class ExtensionAttribute : Attribute { } } """; var corlib = CreateEmptyCompilation(corlibSource); corlib.VerifyDiagnostics(); var corlibRef = corlib.EmitToImageReference(); // no extension on string var src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } """; var comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(string).this[Range] indexer src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } public static class E { extension(string s) { public string this[System.Range r] { get => throw null; } } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(string).Length src = """ public class C { public static void M(string s) { _ = s[1..^1]; } } public static class E { extension(string s) { public int Length => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,13): error CS0021: Cannot apply indexing with [] to an expression of type 'string' // _ = s[1..^1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "s[1..^1]").WithArguments("string").WithLocation(5, 13)); // extension(Array).this[Range] indexer src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(System.Array a) { public string this[System.Range r] { get => throw null; } } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); // extension(Array).Length src = """ public class C { public static void M(int[] arr) { _ = arr[1..^1]; } } public static class E { extension(System.Array a) { public int Length => throw null; } } """; comp = CreateEmptyCompilation(src, references: [corlibRef]); comp.VerifyEmitDiagnostics( // (5,17): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray' // _ = arr[1..^1]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "1..^1").WithArguments("System.Runtime.CompilerServices.RuntimeHelpers", "GetSubArray").WithLocation(5, 17)); } [Fact] public void ObjectInitializer_01() { var src = """ var c = new C() { [0] = 1 }; static class E { extension(C c) { public int this[int i] { set { System.Console.Write($"set({i}, {value}) "); } } } } class C { } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "set(0, 1)").VerifyDiagnostics(); } [Fact] public void ObjectInitializer_02() { // boxed receiver var src = """ var c = new S() { [0] = 1 }; static class E { extension(object o) { public int this[int i] { set { System.Console.Write($"set({i}, {value}) "); } } } } struct S { } """; var comp = CreateCompilation(src); var verifier = CompileAndVerify(comp, expectedOutput: "set(0, 1)").VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 24 (0x18) .maxstack 4 .locals init (S V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloc.0 IL_0009: dup IL_000a: box "S" IL_000f: ldc.i4.0 IL_0010: ldc.i4.1 IL_0011: call "void E.set_Item(object, int, int)" IL_0016: pop IL_0017: ret } """); } [Fact] public void ObjectInitializer_ExtensionIndexer_StructReceiver() { // sibling of ObjectInitializer_01 // Extension indexer with by-val struct receiver, set in an object initializer. var src = """ class Program { static void Main() { _ = new S { [GetIndex()] = GetValue() }; } static int GetIndex() { System.Console.Write("idx "); return 0; } static int GetValue() { System.Console.Write("val "); return 5; } } public struct S { public int F; } public static class E { extension(S s) { public int this[int i] { set { System.Console.Write($"set:F={s.F},i={i},v={value} "); } } } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: "idx val set:F=0,i=0,v=5 ").VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 31 (0x1f) .maxstack 3 .locals init (S V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: initobj "S" IL_0009: ldloc.0 IL_000a: call "int Program.GetIndex()" IL_000f: stloc.1 IL_0010: call "int Program.GetValue()" IL_0015: stloc.2 IL_0016: ldloc.1 IL_0017: ldloc.2 IL_0018: call "void E.set_Item(S, int, int)" IL_001d: nop IL_001e: ret } """); } [Fact] public void ObjectInitializer_ExtensionProperty_StructReceiver() { // sibling of ObjectInitializer_01 // Extension property with by-val struct receiver, set in an object initializer. var src = """ class Program { static void Main() { _ = new S { Prop = GetValue() }; } static int GetValue() { System.Console.Write("val "); return 5; } } public struct S { public int F; } public static class E { extension(S s) { public int Prop { set { System.Console.Write($"set:F={s.F},v={value} "); } } } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: "val set:F=0,v=5 ").VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 24 (0x18) .maxstack 2 .locals init (S V_0, int V_1) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: initobj "S" IL_0009: ldloc.0 IL_000a: call "int Program.GetValue()" IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: call "void E.set_Prop(S, int)" IL_0016: nop IL_0017: ret } """); } [Fact] public void WithInitializer_01() { var src = """ var c = new S() with { [0] = 1 }; public static class E { extension(S s) { public int this[int i] { set { } } } } public struct S { } """; // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members CreateCompilation(src).VerifyEmitDiagnostics( // (1,24): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // var c = new S() with { [0] = 1 }; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[0]").WithLocation(1, 24), // (1,24): error CS0747: Invalid initializer member declarator // var c = new S() with { [0] = 1 }; Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, "[0] = 1").WithLocation(1, 24)); src = """ var c = new S() with { [0] = 1 }; public struct S { public int this[int i] { set { } } } """; // The first diagnostic is unexpected // Tracked by https://github.com/dotnet/roslyn/issues/81666 CreateCompilation(src).VerifyEmitDiagnostics( // (1,24): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // var c = new S() with { [0] = 1 }; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[0]").WithLocation(1, 24), // (1,24): error CS0747: Invalid initializer member declarator // var c = new S() with { [0] = 1 }; Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, "[0] = 1").WithLocation(1, 24)); } [Theory, CombinatorialData] public void ListPattern_01(bool useCompilationReference) { var libSrc = """ public class C { } public static class E { extension(C c) { public int Length => 3; public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } """; var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net70); var libRef = AsReference(libComp, useCompilationReference); var src = """ _ = new C() is [.., 1]; """; var comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (1,16): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // _ = new C() is [.., 1]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "[.., 1]").WithArguments("extension indexers", "15.0").WithLocation(1, 16)); comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular15); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_02() { // boxed receiver var src = """ _ = new S() is [.., 1]; public struct S { public int Length => 3; } public static class E { extension(object o) { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 44 (0x2c) .maxstack 3 .locals init (S V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloca.s V_0 IL_000a: call "int S.Length.get" IL_000f: ldc.i4.1 IL_0010: blt.s IL_0029 IL_0012: ldloc.0 IL_0013: box "S" IL_0018: ldc.i4.1 IL_0019: ldc.i4.1 IL_001a: newobj "System.Index..ctor(int, bool)" IL_001f: call "int E.get_Item(object, System.Index)" IL_0024: ldc.i4.1 IL_0025: ceq IL_0027: br.s IL_002a IL_0029: ldc.i4.0 IL_002a: pop IL_002b: ret } """); } [Fact] public void ListPattern_03() { // nullable value type receiver var src = """ System.Console.Write(E.Test(null)); System.Console.Write(E.Test(new S())); public struct S { public int Length => 3; } public static class E { public static bool Test(S? s) { return s is [.., 1]; } extension(object o) { public int this[System.Index i] { get { System.Console.Write($" {i} "); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("False ^1 False"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("E.Test", """ { // Code size 51 (0x33) .maxstack 3 .locals init (S V_0) IL_0000: ldarga.s V_0 IL_0002: call "readonly bool S?.HasValue.get" IL_0007: brfalse.s IL_0031 IL_0009: ldarga.s V_0 IL_000b: call "readonly S S?.GetValueOrDefault()" IL_0010: stloc.0 IL_0011: ldloca.s V_0 IL_0013: call "int S.Length.get" IL_0018: ldc.i4.1 IL_0019: blt.s IL_0031 IL_001b: ldloc.0 IL_001c: box "S" IL_0021: ldc.i4.1 IL_0022: ldc.i4.1 IL_0023: newobj "System.Index..ctor(int, bool)" IL_0028: call "int E.get_Item(object, System.Index)" IL_002d: ldc.i4.1 IL_002e: ceq IL_0030: ret IL_0031: ldc.i4.0 IL_0032: ret } """); } [Fact] public void ListPattern_04() { // optional parameter var src = """ _ = new S() is [.., 1]; public struct S { public int Length => 3; } public static class E { extension(object o) { public int this[System.Index i, int i2 = 42] { get { System.Console.WriteLine((i, i2)); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("(^1, 42)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_05() { // instance Length, extension Length + extension this[int] var src = """ _ = new S() is [.., 1]; public struct S { public int Length => 3; } public static class E { extension(object o) { public int Length => throw null; public int this[int i] { get { System.Console.WriteLine(i); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("2"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 42 (0x2a) .maxstack 3 .locals init (S V_0, int V_1) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloca.s V_0 IL_000a: call "int S.Length.get" IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: ldc.i4.1 IL_0012: blt.s IL_0027 IL_0014: ldloc.0 IL_0015: box "S" IL_001a: ldloc.1 IL_001b: ldc.i4.1 IL_001c: sub IL_001d: call "int E.get_Item(object, int)" IL_0022: ldc.i4.1 IL_0023: ceq IL_0025: br.s IL_0028 IL_0027: ldc.i4.0 IL_0028: pop IL_0029: ret } """); } [Fact] public void ListPattern_06() { // instance Length, extension this[int] var src = """ _ = new S() is [.., 1]; public struct S { public int Length => 3; } public static class E { extension(object o) { public int this[int i] { get { System.Console.WriteLine(i); return 1; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("2"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_07() { // ambiguous extension Length + extension this[int] var src = """ _ = new S() is [.., 1]; public struct S { } public static class E1 { extension(object o) { public int Length => 3; public int this[int i] { get => throw null; } } } public static class E2 { extension(object o) { public int Length => 3; } } """; // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, consider improving the second diagnostic to mention System.Index var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (1,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[.., 1]").WithArguments("S").WithLocation(1, 16), // (1,16): error CS0021: Cannot apply indexing with [] to an expression of type 'S' // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[.., 1]").WithArguments("S").WithLocation(1, 16)); } [Fact] public void ListPattern_08() { // extension Length + ambiguous extension this[int] var src = """ _ = new S() is [.., 1]; public struct S { } public static class E1 { extension(object o) { public int Length => 3; public int this[int i] { get => throw null; } } } public static class E2 { extension(object o) { public int this[int i] { get => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (1,16): error CS0021: Cannot apply indexing with [] to an expression of type 'S' // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[.., 1]").WithArguments("S").WithLocation(1, 16)); } [Fact] public void ListPattern_09() { // instance Length + ambiguous extension this[Index] var src = """ _ = new S() is [.., 1]; public struct S { public int Length => 3; } public static class E1 { extension(object o) { public int this[System.Index i] { get => throw null; } } } public static class E2 { extension(object o) { public int this[System.Index i] { get => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (1,16): error CS0021: Cannot apply indexing with [] to an expression of type 'S' // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[.., 1]").WithArguments("S").WithLocation(1, 16)); } [Fact] public void ListPattern_10() { // extension Length + instance this[Index] var src = """ _ = new S() is [.., 1]; public struct S { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } public static class E { extension(object o) { public int Length => 3; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_11() { // ambiguous extension Length + ambiguous extension this[int] var src = """ _ = new S() is [.., 1]; public struct S { } public static class E1 { extension(object o) { public int Length => 3; public int this[int i] { get => throw null; } } } public static class E2 { extension(object o) { public int Length => 3; public int this[int i] { get => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (1,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[.., 1]").WithArguments("S").WithLocation(1, 16), // (1,16): error CS0021: Cannot apply indexing with [] to an expression of type 'S' // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[.., 1]").WithArguments("S").WithLocation(1, 16)); } [Fact] public void ListPattern_12() { // ambiguous extension Length + ambiguous extension this[Index] var src = """ _ = new S() is [.., 1]; public struct S { } public static class E1 { extension(object o) { public int Length => 3; public int this[System.Index i] { get => throw null; } } } public static class E2 { extension(object o) { public int Length => 3; public int this[System.Index i] { get => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (1,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[.., 1]").WithArguments("S").WithLocation(1, 16), // (1,16): error CS0021: Cannot apply indexing with [] to an expression of type 'S' // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[.., 1]").WithArguments("S").WithLocation(1, 16)); } [Fact] public void ListPattern_13() { // generic extension Length + generic extension this[Index] var src = """ _ = new S() is [.., 1]; public struct S { } public static class E { extension<T>(T t) { public int Length => 3; public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_14() { // separate generic extension Length + generic extension this[Index] var src = """ _ = new S() is [.., 1]; public struct S { } public static class E1 { extension<T>(T t) { public int Length => 3; } } public static class E2 { extension<T>(T t) { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_15() { // extension Length var src = """ _ = new S() is [.., 1]; public struct S { } public static class E { extension(object o) { public int Length => 3; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (1,16): error CS0021: Cannot apply indexing with [] to an expression of type 'S' // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[.., 1]").WithArguments("S").WithLocation(1, 16)); } [Fact] public void ListPattern_16() { // instance Length, inner extension Length, outer extension this[int] var src = """ using Outer; namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } public static class E1 { extension(object o) { public int Length => throw null; } } } public struct S { public int Length => 3; } namespace Outer { public static class E2 { extension(object o) { public int this[int i] { get { System.Console.WriteLine(i); return 1; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("2"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_17() { // instance Length, inner extension Length, extension this[Index] var src = """ using Outer; public struct S { public int Length => 3; } namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } public static class E1 { extension(object o) { public int Length => throw null; } } } namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_18() { // instance Length, inner ambiguous extension Length, outer extension this[Index] var src = """ using Outer; public struct S { public int Length => 3; } namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } public static class E1 { extension(object o) { public int Length => throw null; } } public static class E2 { extension(object o) { public int Length => throw null; } } } namespace Outer { public static class E3 { extension(object o) { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_19() { // instance Length, inner ambiguous extension Length + extension this[int], outer extension this[Index] var src = """ using Outer; public struct S { public int Length => 3; } namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } public static class E1 { extension(object o) { public int Length => throw null; } } public static class E2 { extension(object o) { public int Length => throw null; public int this[int i] { get { System.Console.WriteLine(i); return 0; } } } } } namespace Outer { public static class E3 { extension(object o) { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_20() { // instance Length, inner extension Length + ambiguous extension this[int], outer extension this[Index] var src = """ using Outer; public struct S { public int Length => 3; } namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } public static class E1 { extension(object o) { public int Length => throw null; public int this[int i] { get => throw null; } } } public static class E2 { extension(object o) { public int this[int i] { get => throw null; } } } } namespace Outer { public static class E3 { extension(object o) { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_21() { // instance Length, outer extension this[Index] var src = """ using Outer; public struct S { public int Length => 3; } namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } } namespace Outer { public static class E { extension(object o) { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_22() { // inner ambiguous extension Length, outer extension Length + extension this[Index] var src = """ using Outer; public struct S { } namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } public static class E1 { extension(object o) { public int Length => throw null; } } public static class E2 { extension(object o) { public int Length => throw null; } } } namespace Outer { public static class E3 { extension(object o) { public int Length => throw null; public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( // (11,28): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. // _ = new S() is [.., 1]; Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[.., 1]").WithArguments("S").WithLocation(11, 28)); } [Fact] public void ListPattern_23() { // inner inapplicable extension Length, outer extension Length + extension this[Index] var src = """ using Outer; public struct S { } public class D { } namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } public static class E1 { extension(D d) { public int Length => throw null; } } } namespace Outer { public static class E3 { extension(object o) { public int Length => 3; public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_24() { // inner inapplicable extension this[Index], outer extension Length + extension this[Index] var src = """ using Outer; public struct S { } public class D { } namespace Inner { public class C { public static void Main() { _ = new S() is [.., 1]; } } public static class E1 { extension(D d) { public int this[System.Index i] { get => throw null; } } } } namespace Outer { public static class E3 { extension(object o) { public int Length => 3; public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ListPattern_25() { // extension Length + classic extension Slice(int, int) var src = """ _ = new C() is [_, .. var y]; public class C { } public static class E { extension(C c) { public int Length => 2; public int this[int i] => throw null; } public static int[] Slice(this C c, int i, int j) { System.Console.Write("ran"); return [1, 2]; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Theory, CombinatorialData] public void SlicePattern_01(bool useCompilationReference) { // extension Length + extension this[Index] + extension this[Range] var libSrc = """ public class C { } public static class E { extension(C c) { public int Length => 3; public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } public int this[System.Range r] { get { System.Console.WriteLine(r); return 0; } } } } """; var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net70); var libRef = AsReference(libComp, useCompilationReference); var src = """ _ = new C() is [_, .. var x]; """; var comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (1,16): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "[_, .. var x]").WithArguments("extension indexers", "15.0").WithLocation(1, 16), // (1,20): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, ".. var x").WithArguments("extension indexers", "15.0").WithLocation(1, 20)); comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular15); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (8,20): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "this").WithArguments("extension indexers", "15.0").WithLocation(8, 20), // (9,20): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // public int this[System.Range r] { get { System.Console.WriteLine(r); return 0; } } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "this").WithArguments("extension indexers", "15.0").WithLocation(9, 20)); } [Fact] public void SlicePattern_02() { // boxed receiver var src = """ _ = new S() is [_, .. var x]; public struct S { public int Length => 3; } public static class E { extension(object o) { public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } public int this[System.Range r] { get { System.Console.WriteLine(r); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 55 (0x37) .maxstack 4 .locals init (S V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloca.s V_0 IL_000a: call "int S.Length.get" IL_000f: ldc.i4.1 IL_0010: blt.s IL_0034 IL_0012: ldloc.0 IL_0013: box "S" IL_0018: ldc.i4.1 IL_0019: ldc.i4.0 IL_001a: newobj "System.Index..ctor(int, bool)" IL_001f: ldc.i4.0 IL_0020: ldc.i4.1 IL_0021: newobj "System.Index..ctor(int, bool)" IL_0026: newobj "System.Range..ctor(System.Index, System.Index)" IL_002b: call "int E.get_Item(object, System.Range)" IL_0030: pop IL_0031: ldc.i4.1 IL_0032: br.s IL_0035 IL_0034: ldc.i4.0 IL_0035: pop IL_0036: ret } """); } [Theory, CombinatorialData] public void SlicePattern_03(bool useCompilationReference) { // extension Length + extension this[int] + extension Slice(int, int) var libSrc = """ public class C { } public static class E { extension(C c) { public int Length => 3; public int this[int i] { get { System.Console.Write(i); return 0; } } public int Slice(int i, int j) { System.Console.Write((i, j)); return 0; } } } """; var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net70); var libRef = AsReference(libComp, useCompilationReference); var src = """ _ = new C() is [_, .. var x]; """; var comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular13); comp.VerifyEmitDiagnostics( // (1,16): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "[_, .. var x]").WithArguments("extensions", "14.0").WithLocation(1, 16), // (1,16): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "[_, .. var x]").WithArguments("extensions", "14.0").WithLocation(1, 16), // (1,16): error CS9260: Feature 'extension indexers' is not available in C# 13.0. Please use language version 15.0 or greater. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "[_, .. var x]").WithArguments("extension indexers", "15.0").WithLocation(1, 16), // (1,20): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, ".. var x").WithArguments("extensions", "14.0").WithLocation(1, 20), // (1,20): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, ".. var x").WithArguments("extensions", "14.0").WithLocation(1, 20)); comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (1,16): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "[_, .. var x]").WithArguments("extension indexers", "15.0").WithLocation(1, 16)); comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("(1, 2)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); comp = CreateCompilation(src, references: [libRef], targetFramework: TargetFramework.Net70, parseOptions: TestOptions.Regular15); CompileAndVerify(comp, expectedOutput: ExpectedOutput("(1, 2)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_04() { // instance Length, inner extension this[int] + extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { public int Length => 3; } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(C c) { public int this[int i] { get => throw null; } public int Slice(int i, int j) => throw null; } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get { System.Console.Write(i); return 0; } } public int this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_05() { // inner extension Length + extension this[int] + extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(C c) { public int Length => 3; public int this[int i] { get => throw null; } public int Slice(int i, int j) => throw null; } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_06() { // inner ambiguous extension Length + extension this[int] + extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(C c) { public int Length => 3; public int this[int i] { get => throw null; } public int Slice(int i, int j) => throw null; } } public static class E2 { extension(C c) { public int Length => 3; } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( // (13,28): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[_, .. var x]").WithArguments("C").WithLocation(13, 28)); } [Fact] public void SlicePattern_07() { // inner extension Length + extension Count + extension this[int] + extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(C c) { public int Length => 3; public int Count => throw null; public int this[int i] { get => throw null; } public int Slice(int i, int j) => throw null; } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_08() { // inner extension Length + ambiguous extension this[int] + extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(C c) { public int Length => 3; public int this[int i] { get => throw null; } public int Slice(int i, int j) => throw null; } } public static class E2 { extension(C c) { public int this[int i] { get => throw null; } } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_09() { // inner extension Length + extension this[int] + ambiguous extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(C c) { public int Length => 3; public int this[int i] { get => throw null; } public int Slice(int i, int j) => throw null; } } public static class E2 { extension(C c) { public int Slice(int i, int j) => throw null; } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_10() { // inner inapplicable extension Length + extension this[int] + extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(D d) { public int Length => 3; } extension(C c) { public int this[int i] { get => throw null; } public int Slice(int i, int j) => throw null; } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( // (13,28): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. // _ = new C() is [_, .. var x]; Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[_, .. var x]").WithArguments("C").WithLocation(13, 28)); } [Fact] public void SlicePattern_11() { // inner extension Length + inapplicable extension this[int] + extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(D d) { public int this[int i] { get => throw null; } } extension(C c) { public int Length => 3; public int Slice(int i, int j) => throw null; } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get { System.Console.Write($"this[{r}]"); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("this[1..^0]"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_12() { // inner extension Length + extension this[int] + inapplicable extension Slice(int, int), outer extension this[Index] + extension this[Range] var src = """ using Outer; public class C { } namespace Inner { public class D { public static void Main() { _ = new C() is [_, .. var x]; } } public static class E1 { extension(D d) { public int Slice(int i, int j) => throw null; } extension(C c) { public int Length => 3; public int this[int i] { get => throw null; } } } } namespace Outer { public static class E2 { extension(C c) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get { System.Console.Write(r); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("1..^0"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_13() { // inner inapplicable extension this[Range], outer extension Length + extension this[Index] + extension this[Range] var src = """ using Outer; public struct S { } public class D { } namespace Inner { public class C { public static void Main() { _ = new S() is [.. var x, 1]; } } public static class E1 { extension(D d) { public int this[System.Range r] { get => throw null; } } } } namespace Outer { public static class E3 { extension(object o) { public int Length { get { System.Console.Write("Length, "); return 3; } } public int this[System.Index i] { get { System.Console.Write(i); return 0; } } public int this[System.Range r] { get { System.Console.Write($"{r}, "); return 0; } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Length, 0..^1, ^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_14() { // generic extension Length + generic extension this[Index] + generic extension Slice(int, int) var src = """ _ = new S() is [.. var x, 1]; public struct S { } public static class E { extension<T>(T t) { public int Length => 3; public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } public int Slice(int i, int j) { System.Console.Write($"Slice({i},{j}), "); return 0; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(0,2), ^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_15() { // generic extension Length + generic extension this[Index] + generic extension this[Range] var src = """ _ = new S() is [.. var x, 1]; public struct S { } public static class E { extension<T>(T t) { public int Length => 3; public int this[System.Index i] { get { System.Console.WriteLine(i); return 0; } } public int this[System.Range r] { get { System.Console.Write($"{r}, "); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("0..^1, ^1"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_16() { // extension Length + extension this[Index] + extension Slice<T>(T, T) var src = """ _ = new S() is [.. var x, 1]; public struct S { } public static class E { extension(object o) { public int Length => 3; public int this[System.Index i] { get => throw null; } public int Slice<T>(T i, T j) => throw null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (1,17): error CS0021: Cannot apply indexing with [] to an expression of type 'S' // _ = new S() is [.. var x, 1]; Diagnostic(ErrorCode.ERR_BadIndexLHS, ".. var x").WithArguments("S").WithLocation(1, 17)); } [Fact] public void SlicePattern_17() { // array type, extension this[Index] + extension this[Range] var src = """ public static class E { public static void Main() { int[] i = [1, 2, 3]; System.Console.Write(M(i)); } public static bool M(int[] i) { return i is [.. var x, 3]; } extension(int[] a) { public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("True"), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("E.M", """ { // Code size 54 (0x36) .maxstack 4 .locals init (int[] V_0, //x int V_1, bool V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: brfalse.s IL_0030 IL_0004: ldarg.0 IL_0005: ldlen IL_0006: conv.i4 IL_0007: stloc.1 IL_0008: ldloc.1 IL_0009: ldc.i4.1 IL_000a: blt.s IL_0030 IL_000c: ldarg.0 IL_000d: ldc.i4.0 IL_000e: ldc.i4.0 IL_000f: newobj "System.Index..ctor(int, bool)" IL_0014: ldc.i4.1 IL_0015: ldc.i4.1 IL_0016: newobj "System.Index..ctor(int, bool)" IL_001b: newobj "System.Range..ctor(System.Index, System.Index)" IL_0020: call "int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<int>(int[], System.Range)" IL_0025: stloc.0 IL_0026: ldarg.0 IL_0027: ldloc.1 IL_0028: ldc.i4.1 IL_0029: sub IL_002a: ldelem.i4 IL_002b: ldc.i4.3 IL_002c: ceq IL_002e: br.s IL_0031 IL_0030: ldc.i4.0 IL_0031: stloc.2 IL_0032: br.s IL_0034 IL_0034: ldloc.2 IL_0035: ret } """); } [Fact] public void SlicePattern_17_WithExtensionLength() { // array type (with and without Length), extension Length + extension this[Index] + extension this[Range] var src = """ public static class E { public static void Main() { int[] i = [1, 2, 3]; System.Console.Write(M(i)); } public static bool M(int[] i) { return i is [.. var x, 3]; } extension(int[] a) { public int Length { get { System.Console.Write("Length "); return 3; } } public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("True"), verify: Verification.Skipped).VerifyDiagnostics(); var corlib = CreateEmptyCompilation(corlibWithNoArrayMembersSource); corlib.VerifyDiagnostics(); Assert.Null(corlib.GetSpecialTypeMember(SpecialMember.System_Array__Length)); var corlibRef = corlib.EmitToImageReference(); // array type without Length, extension Length + extension this[Index] + extension this[Range] var src2 = """ public static class E { public static bool M(int[] i) { return i is [.. var x, 3]; } extension(int[] a) { public int Length => throw null; public int this[System.Index i] { get => throw null; } public int this[System.Range r] { get => throw null; } } } """; var comp2 = CreateEmptyCompilation(src2, references: [corlibRef]); comp2.VerifyDiagnostics(); var verifier2 = CompileAndVerify(comp2, verify: Verification.Skipped); verifier2.VerifyIL("E.M", """ { // Code size 51 (0x33) .maxstack 4 .locals init (int V_0) IL_0000: ldarg.0 IL_0001: brfalse.s IL_0031 IL_0003: ldarg.0 IL_0004: call "int E.get_Length(int[])" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: blt.s IL_0031 IL_000e: ldarg.0 IL_000f: ldc.i4.0 IL_0010: ldc.i4.0 IL_0011: newobj "System.Index..ctor(int, bool)" IL_0016: ldc.i4.1 IL_0017: ldc.i4.1 IL_0018: newobj "System.Index..ctor(int, bool)" IL_001d: newobj "System.Range..ctor(System.Index, System.Index)" IL_0022: call "int[] System.Runtime.CompilerServices.RuntimeHelpers.GetSubArray<int>(int[], System.Range)" IL_0027: pop IL_0028: ldarg.0 IL_0029: ldloc.0 IL_002a: ldc.i4.1 IL_002b: sub IL_002c: ldelem.i4 IL_002d: ldc.i4.3 IL_002e: ceq IL_0030: ret IL_0031: ldc.i4.0 IL_0032: ret } """); } [Fact] public void SlicePattern_18() { // instance Length, inner extension Length + extension this[int], outer extension Length + extension Slice(int, int) var src = """ using Outer; public struct S { public int Length => 3; } namespace Inner { public class C { public static void Main() { _ = new S() is [.. var x, 1]; } } public static class E1 { extension(S s) { public int Length => 3; public int this[int i] { get { System.Console.WriteLine(i); return 0; } } } } } namespace Outer { public static class E2 { extension(object o) { public int Length => throw null; public object Slice(int i, int j) { System.Console.Write($"{i},{j}, "); return o; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("0,2, 2"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Inner.C.Main", """ { // Code size 56 (0x38) .maxstack 4 .locals init (object V_0, //x S V_1, int V_2, int V_3) IL_0000: nop IL_0001: ldloca.s V_1 IL_0003: initobj "S" IL_0009: ldloca.s V_1 IL_000b: call "int S.Length.get" IL_0010: stloc.2 IL_0011: ldloc.2 IL_0012: ldc.i4.1 IL_0013: blt.s IL_0035 IL_0015: ldloc.1 IL_0016: box "S" IL_001b: ldc.i4.0 IL_001c: ldloc.2 IL_001d: ldc.i4.1 IL_001e: sub IL_001f: call "object Outer.E2.Slice(object, int, int)" IL_0024: stloc.0 IL_0025: ldloc.2 IL_0026: ldc.i4.1 IL_0027: sub IL_0028: stloc.3 IL_0029: ldloc.1 IL_002a: ldloc.3 IL_002b: call "int Inner.E1.get_Item(S, int)" IL_0030: ldc.i4.1 IL_0031: ceq IL_0033: br.s IL_0036 IL_0035: ldc.i4.0 IL_0036: pop IL_0037: ret } """); } [Fact] public void SlicePattern_18_2() { // instance Length + extension this[int] + extension Slice(int, int) var src = """ _ = new S() is [.. var x, 1]; public struct S { public int Length => 3; } public static class E { extension(S s) { public int this[int i] { get { System.Console.Write($"this[{i}], "); return 0; } } public S Slice(int i, int j) { System.Console.Write($"Slice({i},{j}), "); return s; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("Slice(0,2), this[2], "), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 54 (0x36) .maxstack 3 .locals init (S V_0, int V_1, int V_2, int V_3) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloca.s V_0 IL_000a: call "int S.Length.get" IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: ldc.i4.1 IL_0012: blt.s IL_0033 IL_0014: ldc.i4.0 IL_0015: stloc.2 IL_0016: ldloc.1 IL_0017: ldc.i4.1 IL_0018: sub IL_0019: stloc.3 IL_001a: ldloc.0 IL_001b: ldloc.2 IL_001c: ldloc.3 IL_001d: call "S E.Slice(S, int, int)" IL_0022: pop IL_0023: ldloc.1 IL_0024: ldc.i4.1 IL_0025: sub IL_0026: stloc.3 IL_0027: ldloc.0 IL_0028: ldloc.3 IL_0029: call "int E.get_Item(S, int)" IL_002e: ldc.i4.1 IL_002f: ceq IL_0031: br.s IL_0034 IL_0033: ldc.i4.0 IL_0034: pop IL_0035: ret } """); } [Fact] public void SlicePattern_18_3() { // instance Length + extension this[Index] + extension this[Range] var src = """ _ = new S() is [.. var x, 1]; public struct S { public int Length => 3; } public static class E { extension(S s) { public int this[System.Index i] { get { System.Console.Write($"this[{i}], "); return 0; } } public S this[System.Range r] { get { System.Console.Write($"this[{r}], "); return s; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("this[0..^1], this[^1], "), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 83 (0x53) .maxstack 5 .locals init (S V_0, System.Range V_1, System.Index V_2) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloca.s V_0 IL_000a: call "int S.Length.get" IL_000f: ldc.i4.1 IL_0010: blt.s IL_0050 IL_0012: ldloca.s V_0 IL_0014: ldloca.s V_1 IL_0016: ldc.i4.0 IL_0017: ldc.i4.0 IL_0018: newobj "System.Index..ctor(int, bool)" IL_001d: ldc.i4.1 IL_001e: ldc.i4.1 IL_001f: newobj "System.Index..ctor(int, bool)" IL_0024: call "System.Range..ctor(System.Index, System.Index)" IL_0029: ldobj "S" IL_002e: ldloc.1 IL_002f: call "S E.get_Item(S, System.Range)" IL_0034: pop IL_0035: ldloca.s V_0 IL_0037: ldloca.s V_2 IL_0039: ldc.i4.1 IL_003a: ldc.i4.1 IL_003b: call "System.Index..ctor(int, bool)" IL_0040: ldobj "S" IL_0045: ldloc.2 IL_0046: call "int E.get_Item(S, System.Index)" IL_004b: ldc.i4.1 IL_004c: ceq IL_004e: br.s IL_0051 IL_0050: ldc.i4.0 IL_0051: pop IL_0052: ret } """); } [Fact] public void SlicePattern_19() { // instance Length + this[int], inner extension Length + inner Slice(int, int), outer extension Length var src = """ using Outer; public struct S { public int Length => 3; public int this[int i] { get { System.Console.WriteLine(i); return 0; } } } namespace Inner { public class C { public static void Main() { _ = new S() is [.. var x, 1]; } } public static class E1 { extension(S s) { public int Length => throw null; public S Slice(int i, int j) { System.Console.Write($"{i},{j}, "); return s; } } } } namespace Outer { public static class E2 { extension(S s) { public int Length => throw null; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("0,2, 2"), verify: Verification.FailsPEVerify).VerifyDiagnostics( // (1,1): hidden CS8019: Unnecessary using directive. // using Outer; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using Outer;").WithLocation(1, 1)); verifier.VerifyIL("Inner.C.Main", """ { // Code size 56 (0x38) .maxstack 3 .locals init (S V_0, //x S V_1, int V_2, int V_3, int V_4) IL_0000: nop IL_0001: ldloca.s V_1 IL_0003: initobj "S" IL_0009: ldloca.s V_1 IL_000b: call "int S.Length.get" IL_0010: stloc.2 IL_0011: ldloc.2 IL_0012: ldc.i4.1 IL_0013: blt.s IL_0035 IL_0015: ldc.i4.0 IL_0016: stloc.3 IL_0017: ldloc.2 IL_0018: ldc.i4.1 IL_0019: sub IL_001a: stloc.s V_4 IL_001c: ldloc.1 IL_001d: ldloc.3 IL_001e: ldloc.s V_4 IL_0020: call "S Inner.E1.Slice(S, int, int)" IL_0025: stloc.0 IL_0026: ldloca.s V_1 IL_0028: ldloc.2 IL_0029: ldc.i4.1 IL_002a: sub IL_002b: call "int S.this[int].get" IL_0030: ldc.i4.1 IL_0031: ceq IL_0033: br.s IL_0036 IL_0035: ldc.i4.0 IL_0036: pop IL_0037: ret } """); } [Fact] public void SlicePattern_20() { // extension Length + extension this[int] + classic extension Slice(int, int) var src = """ _ = new C() is [_, .. var x]; public class C { } public static class E { extension(C c) { public int Length => 3; public int this[int i] { get => 0; } } public static int Slice(this C c, int i, int j) { System.Console.Write((i, j)); return 0; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); CompileAndVerify(comp, expectedOutput: ExpectedOutput("(1, 2)"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void SlicePattern_21() { // struct receiver passed by value, extension Length + extension this[Index] + extension this[Range] var src = """ _ = new S() is [.. var x, 1]; public struct S { } public static class E { extension(S s) { public int Length => 3; public int this[System.Index i] { get { System.Console.Write($"{i}, "); return 0; } } public int this[System.Range r] { get { System.Console.Write($"{r}, "); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.DebugExe); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("0..^1, ^1, "), verify: Verification.FailsPEVerify).VerifyDiagnostics(); // Verify that the receiver (pattern temp local) is not unnecessarily captured. verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 82 (0x52) .maxstack 5 .locals init (int V_0, //x S V_1, System.Range V_2, System.Index V_3) IL_0000: ldloca.s V_1 IL_0002: initobj "S" IL_0008: ldloc.1 IL_0009: call "int E.get_Length(S)" IL_000e: ldc.i4.1 IL_000f: blt.s IL_004f IL_0011: ldloca.s V_1 IL_0013: ldloca.s V_2 IL_0015: ldc.i4.0 IL_0016: ldc.i4.0 IL_0017: newobj "System.Index..ctor(int, bool)" IL_001c: ldc.i4.1 IL_001d: ldc.i4.1 IL_001e: newobj "System.Index..ctor(int, bool)" IL_0023: call "System.Range..ctor(System.Index, System.Index)" IL_0028: ldobj "S" IL_002d: ldloc.2 IL_002e: call "int E.get_Item(S, System.Range)" IL_0033: stloc.0 IL_0034: ldloca.s V_1 IL_0036: ldloca.s V_3 IL_0038: ldc.i4.1 IL_0039: ldc.i4.1 IL_003a: call "System.Index..ctor(int, bool)" IL_003f: ldobj "S" IL_0044: ldloc.3 IL_0045: call "int E.get_Item(S, System.Index)" IL_004a: ldc.i4.1 IL_004b: ceq IL_004d: br.s IL_0050 IL_004f: ldc.i4.0 IL_0050: pop IL_0051: ret } """); } [Theory, CombinatorialData] public void ConditionalAssignment_01(bool useCompilationReference) { var libSrc = """ public class C { } public static class E { extension(C c) { public int this[int i] { set { System.Console.WriteLine($"set_Item({i} {value})"); } } } } """; var libComp = CreateCompilation(libSrc); var libRef = AsReference(libComp, useCompilationReference); var src = """ C c = null; c?[42] = 0; c = new C(); c?[43] = 100; """; var comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (2,3): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // c?[42] = 0; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "[42]").WithArguments("extension indexers", "15.0").WithLocation(2, 3), // (5,3): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // c?[43] = 100; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "[43]").WithArguments("extension indexers", "15.0").WithLocation(5, 3)); comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular15); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src, references: [libRef]); CompileAndVerify(comp, expectedOutput: "set_Item(43 100)").VerifyDiagnostics(); comp = CreateCompilation([src, libSrc], parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (7,20): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // public int this[int i] Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "this").WithArguments("extension indexers", "15.0").WithLocation(7, 20)); } [Fact] public void ConditionalAssignment_02() { // nullable value type receiver var src = """ E.Test(null, 42, 0); E.Test(new S(), 43, 100); public struct S { } public static class E { public static void Test(S? s, int index, int value) { s?[index] = value; } extension(S s) { public int this[int i] { set { System.Console.WriteLine($"set_Item({i} {value})"); } } } } """; // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (10,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // s?[index] = value; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[index]").WithLocation(10, 11)); //var verifier = CompileAndVerify(comp, expectedOutput: "set_Item(43 100)").VerifyDiagnostics(); //verifier.VerifyIL("E.Test", ""); } [Theory, CombinatorialData] public void ConditionalAccess_01(bool useCompilationReference) { var libSrc = """ public class C { } public static class E { extension(C c) { public int this[int i] { get { System.Console.Write($" get_Item({i}) "); return 100; } } } } """; var libComp = CreateCompilation(libSrc); var libRef = AsReference(libComp, useCompilationReference); var src = """ C c = null; System.Console.Write(c?[42] is null); c = new C(); System.Console.Write(c?[43] is 100); """; var comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (2,24): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // System.Console.Write(c?[42] is null); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "[42]").WithArguments("extension indexers", "15.0").WithLocation(2, 24), // (5,24): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // System.Console.Write(c?[43] is 100); Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "[43]").WithArguments("extension indexers", "15.0").WithLocation(5, 24)); comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular15); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(src, references: [libRef]); CompileAndVerify(comp, expectedOutput: "True get_Item(43) True").VerifyDiagnostics(); comp = CreateCompilation([src, libSrc], parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (7,20): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // public int this[int i] Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "this").WithArguments("extension indexers", "15.0").WithLocation(7, 20)); } [Fact] public void ConditionalAccess_02() { // nullable value type receiver var src = """ System.Console.Write(E.Test(null, 42) is null); System.Console.Write(E.Test(new S(), 43) is 100); public struct S { } public static class E { public static int? Test(S? s, int index) { return s?[index]; } extension(S s) { public int this[int i] { get { System.Console.Write($" get_Item({i}) "); return 100; } } } } """; var comp = CreateCompilation(src); var verifier = CompileAndVerify(comp, expectedOutput: "True get_Item(43) True").VerifyDiagnostics(); verifier.VerifyIL("E.Test", """ { // Code size 38 (0x26) .maxstack 2 .locals init (int? V_0) IL_0000: ldarga.s V_0 IL_0002: call "bool S?.HasValue.get" IL_0007: brtrue.s IL_0013 IL_0009: ldloca.s V_0 IL_000b: initobj "int?" IL_0011: ldloc.0 IL_0012: ret IL_0013: ldarga.s V_0 IL_0015: call "S S?.GetValueOrDefault()" IL_001a: ldarg.1 IL_001b: call "int E.get_Item(S, int)" IL_0020: newobj "int?..ctor(int)" IL_0025: ret } """); } [Fact] public void Nameof_01() { var src = """ object o = new object(); _ = nameof(o[0]); public static class E { extension(object o) { public int this[int i] => 0; } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,12): error CS8081: Expression does not have a name. // _ = nameof(o[0]); Diagnostic(ErrorCode.ERR_ExpressionHasNoName, "o[0]").WithLocation(2, 12)); } [Fact] public void Nameof_02() { // setter-only indexer var src = """ object o = new object(); _ = nameof(o[0]); public static class E { extension(object o) { public int this[int i] { set { } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,12): error CS8081: Expression does not have a name. // _ = nameof(o[0]); Diagnostic(ErrorCode.ERR_ExpressionHasNoName, "o[0]").WithLocation(2, 12), // (2,12): error CS0154: The property or indexer 'E.extension(object).this[int]' cannot be used in this context because it lacks the get accessor // _ = nameof(o[0]); Diagnostic(ErrorCode.ERR_PropertyLacksGet, "o[0]").WithArguments("E.extension(object).this[int]").WithLocation(2, 12)); } [Fact] public void ORPA_01() { var source = """ _ = 42[43]; static class E { extension(int i) { public int this[int j] => throw null; [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] public int this[long l] { get { System.Console.Write("ran"); return 0; } } } } """; var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); } [Fact] public void ORPA_02() { var source = """ _ = 42[43]; static class E { extension(int i) { [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] public int this[int j] { get { System.Console.Write("ran"); return 0; } } public int this[long l] => throw null; } } """; var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); } [Fact] public void ORPA_03() { var source = """ _ = 42[43]; static class E1 { extension(int i) { public int this[int j] { get { System.Console.Write("ran"); return 0; } } } } static class E2 { extension(int i) { [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] public int this[long l] => throw null; } } """; var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); } [Fact] public void Metadata_01() { var source = """ public static class E { extension(int i) { public int this[int j] { get { return 42; } } } } """; var comp = CreateCompilation(source); var verifier = CompileAndVerify(comp).VerifyDiagnostics(); verifier.VerifyTypeIL("E", """ .class public auto ansi abstract sealed beforefieldinit E extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Nested Types .class nested public auto ansi sealed specialname '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69' extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [netstandard]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) // Nested Types .class nested public auto ansi abstract sealed specialname '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372' extends [netstandard]System.Object { // Methods .method public hidebysig specialname static void '<Extension>$' ( int32 i ) cil managed { .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2089 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372'::'<Extension>$' } // end of class <M>$F4B4FFE41AB49E80A4ECF390CF6EB372 // Methods .method public hidebysig specialname instance int32 get_Item ( int32 j ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x2082 // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item // Properties .property instance int32 Item( int32 j ) { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) .get instance int32 E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item(int32) } } // end of class <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69 // Methods .method public hidebysig static int32 get_Item ( int32 i, int32 j ) cil managed { // Method begins at RVA 0x207e // Code size 3 (0x3) .maxstack 8 IL_0000: ldc.i4.s 42 IL_0002: ret } // end of method E::get_Item } // end of class E """.Replace("[netstandard]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } [Theory, CombinatorialData] public void Metadata_02(bool useCompilationReference) { var libSrc = """ public static class E { extension(int i) { public int this[int j] { get { System.Console.Write($"get({j}) "); return 42; } set { System.Console.Write($"set({j}, {value}) "); } } } } """; var libComp = CreateCompilation(libSrc); var libRef = AsReference(libComp, useCompilationReference); var src = """ int i = 0; _ = i[43]; i[101] = 102; """; var comp = CreateCompilation(src, [libRef]); CompileAndVerify(comp, expectedOutput: "get(43) set(101, 102)").VerifyDiagnostics(); var indexer = comp.GlobalNamespace.GetTypeMember("E").GetTypeMember("").GetMembers().OfType<PropertySymbol>().Single(); AssertEx.Equal("E.extension(int).this[int]", indexer.ToDisplayString()); Assert.True(indexer.IsIndexer); AssertEx.Equal("E.extension(int).this[int].get", indexer.GetMethod.ToDisplayString()); AssertEx.Equal("E.extension(int).this[int].set", indexer.SetMethod.ToDisplayString()); comp = CreateCompilation(src, [libRef], parseOptions: TestOptions.Regular14); comp.VerifyEmitDiagnostics( // (2,5): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // _ = i[43]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "i[43]").WithArguments("extension indexers", "15.0").WithLocation(2, 5), // (3,1): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // i[101] = 102; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "i[101]").WithArguments("extension indexers", "15.0").WithLocation(3, 1)); } [Fact] public void Metadata_02_VisualBasic() { var libSrc = """ public static class E { extension(int i) { public int this[int j] { get { System.Console.Write($"get({j}) "); return 42; } set { System.Console.Write($"set({j}, {value}) "); } } } } """; var libComp = CreateCompilation(libSrc); var libRef = libComp.EmitToImageReference(); var source = """ Module Program Sub Main() Dim i As Integer = 0 System.Console.Write(E.get_Item(i, 43)) E.set_Item(i, 101, 102) End Sub End Module """; var references = TargetFrameworkUtil.StandardAndVBRuntimeReferences; var comp = CreateVisualBasicCompilation("Program", source, referencedAssemblies: references.Concat([libRef]), compilationOptions: new VisualBasicCompilationOptions(OutputKind.ConsoleApplication)); CompileAndVerify(comp, expectedOutput: "get(43) 42set(101, 102) ").VerifyDiagnostics(); source = """ Module Program Sub Main() Dim i As Integer = 0 System.Console.Write(i(43)) i(101) = 102 End Sub End Module """; comp = CreateVisualBasicCompilation("Program", source, referencedAssemblies: references.Concat([libRef]), compilationOptions: new VisualBasicCompilationOptions(OutputKind.ConsoleApplication)); comp.VerifyEmitDiagnostics( // (4,30): error BC30690: Structure 'Integer' cannot be indexed because it has no default property. // System.Console.Write(i(43)) Diagnostic(30690 /*ERRID.ERR_StructureNoDefault1*/, "i").WithArguments("Integer").WithLocation(4, 30), // (5,9): error BC30690: Structure 'Integer' cannot be indexed because it has no default property. // i(101) = 102 Diagnostic(30690 /*ERRID.ERR_StructureNoDefault1*/, "i").WithArguments("Integer").WithLocation(5, 9)); } [Theory, CombinatorialData] public void Metadata_03(bool useCompilationReference) { // IndexerName attribute on single indexer var libSrc = """ public static class E { extension(int i) { [System.Runtime.CompilerServices.IndexerName("MyIndexer")] public int this[int j] { get { System.Console.Write($"get({j}) "); return 42; } set { System.Console.Write($"set({j}, {value}) "); } } } } """; var libComp = CreateCompilation(libSrc); var verifier = CompileAndVerify(libComp).VerifyDiagnostics(); verifier.VerifyTypeIL("E", """ .class public auto ansi abstract sealed beforefieldinit E extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Nested Types .class nested public auto ansi sealed specialname '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69' extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [netstandard]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 09 4d 79 49 6e 64 65 78 65 72 00 00 ) // Nested Types .class nested public auto ansi abstract sealed specialname '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372' extends [netstandard]System.Object { // Methods .method public hidebysig specialname static void '<Extension>$' ( int32 i ) cil managed { .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x20bb // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372'::'<Extension>$' } // end of class <M>$F4B4FFE41AB49E80A4ECF390CF6EB372 // Methods .method public hidebysig specialname instance int32 get_MyIndexer ( int32 j ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x20b4 // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_MyIndexer .method public hidebysig specialname instance void set_MyIndexer ( int32 j, int32 'value' ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x20b4 // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::set_MyIndexer // Properties .property instance int32 MyIndexer( int32 j ) { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) .get instance int32 E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_MyIndexer(int32) .set instance void E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::set_MyIndexer(int32, int32) } } // end of class <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69 // Methods .method public hidebysig static int32 get_MyIndexer ( int32 i, int32 j ) cil managed { // Method begins at RVA 0x207e // Code size 24 (0x18) .maxstack 8 IL_0000: ldstr "get({0}) " IL_0005: ldarg.1 IL_0006: box [netstandard]System.Int32 IL_000b: call string [netstandard]System.String::Format(string, object) IL_0010: call void [netstandard]System.Console::Write(string) IL_0015: ldc.i4.s 42 IL_0017: ret } // end of method E::get_MyIndexer .method public hidebysig static void set_MyIndexer ( int32 i, int32 j, int32 'value' ) cil managed { // Method begins at RVA 0x2097 // Code size 28 (0x1c) .maxstack 8 IL_0000: ldstr "set({0}, {1}) " IL_0005: ldarg.1 IL_0006: box [netstandard]System.Int32 IL_000b: ldarg.2 IL_000c: box [netstandard]System.Int32 IL_0011: call string [netstandard]System.String::Format(string, object, object) IL_0016: call void [netstandard]System.Console::Write(string) IL_001b: ret } // end of method E::set_MyIndexer } // end of class E """.Replace("[netstandard]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); var src = """ int i = 0; _ = i[43]; i[101] = 102; E.get_MyIndexer(i, 43); E.set_MyIndexer(i, 101, 102); """; var comp = CreateCompilation(src, [AsReference(libComp, useCompilationReference)]); CompileAndVerify(comp, expectedOutput: "get(43) set(101, 102) get(43) set(101, 102)").VerifyDiagnostics(); var indexer = comp.GlobalNamespace.GetTypeMember("E").GetTypeMember("").GetMembers().OfType<PropertySymbol>().Single(); AssertEx.Equal("E.extension(int).this[int]", indexer.ToDisplayString()); Assert.True(indexer.IsIndexer); AssertEx.Equal("E.extension(int).this[int].get", indexer.GetMethod.ToDisplayString()); AssertEx.Equal("E.extension(int).this[int].set", indexer.SetMethod.ToDisplayString()); } [Theory, CombinatorialData] public void Metadata_04(bool useCompilationReference) { // Matching IndexerName attributes on both indexers var libSrc = """ public static class E { extension(int i) { [System.Runtime.CompilerServices.IndexerName("MyIndexer")] public int this[int j] { get { System.Console.Write($"get({j}) "); return 42; } set { System.Console.Write($"set({j}, {value}) "); } } [System.Runtime.CompilerServices.IndexerName("MyIndexer")] public int this[string s] { get { System.Console.Write($"get2({s}) "); return 42; } set { System.Console.Write($"set2({s}, {value}) "); } } } } """; var libComp = CreateCompilation(libSrc); var verifier = CompileAndVerify(libComp).VerifyDiagnostics(); var src = """ int i = 0; _ = i[43]; i[101] = 102; E.get_MyIndexer(i, 43); E.set_MyIndexer(i, 101, 102); _ = i["A"]; i["B"] = 102; E.get_MyIndexer(i, "A"); E.set_MyIndexer(i, "B", 102); """; var comp = CreateCompilation(src, [AsReference(libComp, useCompilationReference)]); CompileAndVerify(comp, expectedOutput: "get(43) set(101, 102) get(43) set(101, 102) get2(A) set2(B, 102) get2(A) set2(B, 102)").VerifyDiagnostics(); var indexers = comp.GlobalNamespace.GetTypeMember("E").GetTypeMember("").GetMembers().OfType<PropertySymbol>().ToArray(); AssertEx.Equal("E.extension(int).this[int]", indexers[0].ToDisplayString()); Assert.True(indexers[0].IsIndexer); AssertEx.Equal("E.extension(int).this[string]", indexers[1].ToDisplayString()); Assert.True(indexers[1].IsIndexer); } [Fact] public void IndexerName_01() { // IndexerName attribute on one of the indexers var source = """ public static class E { extension((int a, int b) t) { public int this[string s] { get => throw null; set => throw null; } } extension((int c, int d) t) { [System.Runtime.CompilerServices.IndexerName("MyIndexer")] public int this[int j] { get => throw null; set => throw null; } } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (15,20): error CS0668: Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type // public int this[int j] Diagnostic(ErrorCode.ERR_InconsistentIndexerNames, "this").WithLocation(15, 20)); } [Fact] public void IndexerName_02() { // IndexerName attribute uses same name as extension parameter var source = """ public static class E { extension(int parameter) { [System.Runtime.CompilerServices.IndexerName("parameter")] public int this[int i] { get => throw null; set => throw null; } } } """; CreateCompilation(source).VerifyEmitDiagnostics(); } [Fact] public void IndexerName_03() { // IndexerName attribute uses same name as static enclosing class var source = """ public static class E { extension(int i) { [System.Runtime.CompilerServices.IndexerName("E")] public int this[int j] { get => throw null; set => throw null; } } } """; CreateCompilation(source).VerifyEmitDiagnostics(); } [Fact] public void Extern_03() { var source = """ using System.Runtime.InteropServices; static class E { extension(int i) { extern int this[int j] { [DllImport("something.dll")] get; [DllImport("something.dll")] set; } } } """; var verifier = CompileAndVerify(source).VerifyDiagnostics(); // Note: skeleton methods have "throw" bodies and lack pinvokeimpl/preservesig. Implementation methods have pinvokeimpl/preservesig and no body. verifier.VerifyTypeIL("E", """ .class private auto ansi abstract sealed beforefieldinit E extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Nested Types .class nested public auto ansi sealed specialname '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69' extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [netstandard]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) // Nested Types .class nested public auto ansi abstract sealed specialname '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372' extends [netstandard]System.Object { // Methods .method private hidebysig specialname static void '<Extension>$' ( int32 i ) cil managed { .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2085 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372'::'<Extension>$' } // end of class <M>$F4B4FFE41AB49E80A4ECF390CF6EB372 // Methods .method private hidebysig specialname instance int32 get_Item ( int32 j ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x207e // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item .method private hidebysig specialname instance void set_Item ( int32 j, int32 'value' ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x207e // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::set_Item // Properties .property instance int32 Item( int32 j ) { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) .get instance int32 E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item(int32) .set instance void E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::set_Item(int32, int32) } } // end of class <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69 // Methods .method private hidebysig static pinvokeimpl("something.dll" winapi) int32 get_Item ( int32 i, int32 j ) cil managed preservesig { } // end of method E::get_Item .method private hidebysig static pinvokeimpl("something.dll" winapi) void set_Item ( int32 i, int32 j, int32 'value' ) cil managed preservesig { } // end of method E::set_Item } // end of class E """.Replace("[netstandard]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } [Fact] public void Extern_05() { var source = """ static class E { extension(int i) { extern int this[int j] { get; set; } } } """; var verifier = CompileAndVerify(source, verify: Verification.FailsPEVerify with { PEVerifyMessage = """ Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. Type load failed. """ }); verifier.VerifyDiagnostics( // (5,34): warning CS0626: Method, operator, or accessor 'E.extension(int).this[int].get' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. // extern int this[int j] { get; set; } Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "get").WithArguments("E.extension(int).this[int].get").WithLocation(5, 34), // (5,39): warning CS0626: Method, operator, or accessor 'E.extension(int).this[int].set' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. // extern int this[int j] { get; set; } Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "set").WithArguments("E.extension(int).this[int].set").WithLocation(5, 39)); verifier.VerifyTypeIL("E", """ .class private auto ansi abstract sealed beforefieldinit E extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Nested Types .class nested public auto ansi sealed specialname '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69' extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [netstandard]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) // Nested Types .class nested public auto ansi abstract sealed specialname '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372' extends [netstandard]System.Object { // Methods .method private hidebysig specialname static void '<Extension>$' ( int32 i ) cil managed { .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2085 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372'::'<Extension>$' } // end of class <M>$F4B4FFE41AB49E80A4ECF390CF6EB372 // Methods .method private hidebysig specialname instance int32 get_Item ( int32 j ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x207e // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item .method private hidebysig specialname instance void set_Item ( int32 j, int32 'value' ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x207e // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::set_Item // Properties .property instance int32 Item( int32 j ) { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) .get instance int32 E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item(int32) .set instance void E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::set_Item(int32, int32) } } // end of class <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69 // Methods .method private hidebysig static int32 get_Item ( int32 i, int32 j ) cil managed { } // end of method E::get_Item .method private hidebysig static void set_Item ( int32 i, int32 j, int32 'value' ) cil managed { } // end of method E::set_Item } // end of class E """.Replace("[netstandard]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); source = """ class C { extern int this[int j] { get; set; } } """; verifier = CompileAndVerify(source, verify: Verification.FailsPEVerify with { PEVerifyMessage = """ Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. Type load failed. """ }); verifier.VerifyDiagnostics( // (3,30): warning CS0626: Method, operator, or accessor 'C.this[int].get' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. // extern int this[int j] { get; set; } Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "get").WithArguments("C.this[int].get").WithLocation(3, 30), // (3,35): warning CS0626: Method, operator, or accessor 'C.this[int].set' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. // extern int this[int j] { get; set; } Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "set").WithArguments("C.this[int].set").WithLocation(3, 35)); verifier.VerifyTypeIL("C", """ .class private auto ansi beforefieldinit C extends [netstandard]System.Object { .custom instance void [netstandard]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) // Methods .method private hidebysig specialname instance int32 get_Item ( int32 j ) cil managed { } // end of method C::get_Item .method private hidebysig specialname instance void set_Item ( int32 j, int32 'value' ) cil managed { } // end of method C::set_Item .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2067 // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [netstandard]System.Object::.ctor() IL_0006: ret } // end of method C::.ctor // Properties .property instance int32 Item( int32 j ) { .get instance int32 C::get_Item(int32) .set instance void C::set_Item(int32, int32) } } // end of class C """.Replace("[netstandard]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } [Fact] public void Extern_09() { var source = """ static class E { extension(int i) { extern int this[int j] { [method: System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] get; [method: System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] set; } } } """; var comp = CreateCompilation(source); var verifier = CompileAndVerify(comp).VerifyDiagnostics(); verifier.VerifyTypeIL("E", """ .class private auto ansi abstract sealed beforefieldinit E extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) // Nested Types .class nested public auto ansi sealed specialname '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69' extends [netstandard]System.Object { .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [netstandard]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) // Nested Types .class nested public auto ansi abstract sealed specialname '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372' extends [netstandard]System.Object { // Methods .method private hidebysig specialname static void '<Extension>$' ( int32 i ) cil managed { .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Method begins at RVA 0x2085 // Code size 1 (0x1) .maxstack 8 IL_0000: ret } // end of method '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372'::'<Extension>$' } // end of class <M>$F4B4FFE41AB49E80A4ECF390CF6EB372 // Methods .method private hidebysig specialname instance int32 get_Item ( int32 j ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x207e // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item .method private hidebysig specialname instance void set_Item ( int32 j, int32 'value' ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) // Method begins at RVA 0x207e // Code size 6 (0x6) .maxstack 8 IL_0000: newobj instance void [netstandard]System.NotSupportedException::.ctor() IL_0005: throw } // end of method '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::set_Item // Properties .property instance int32 Item( int32 j ) { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) .get instance int32 E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item(int32) .set instance void E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::set_Item(int32, int32) } } // end of class <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69 // Methods .method private hidebysig static int32 get_Item ( int32 i, int32 j ) cil managed internalcall { } // end of method E::get_Item .method private hidebysig static void set_Item ( int32 i, int32 j, int32 'value' ) cil managed internalcall { } // end of method E::set_Item } // end of class E """.Replace("[netstandard]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } [Fact] public void Extern_10() { var source = """ using System.Runtime.InteropServices; static class E { extension(int i) { int this[int j] { [DllImport("something.dll")] get => 0; [DllImport("something.dll")] set { } } } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,14): error CS0601: The DllImport attribute must be specified on a method marked 'extern' that is either 'static' or an extension member // [DllImport("something.dll")] Diagnostic(ErrorCode.ERR_DllImportOnInvalidMethod, "DllImport").WithLocation(8, 14), // (10,14): error CS0601: The DllImport attribute must be specified on a method marked 'extern' that is either 'static' or an extension member // [DllImport("something.dll")] Diagnostic(ErrorCode.ERR_DllImportOnInvalidMethod, "DllImport").WithLocation(10, 14)); } [Fact] public void Extern_11() { var source = """ using System.Runtime.InteropServices; static class E { extension(int i) { extern int this[int j] { [DllImport("something.dll")] get => 0; [DllImport("something.dll")] set { } } } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,13): error CS0179: 'E.extension(int).this[int].get' cannot be extern and declare a body // get => 0; Diagnostic(ErrorCode.ERR_ExternHasBody, "get").WithArguments("E.extension(int).this[int].get").WithLocation(9, 13), // (11,13): error CS0179: 'E.extension(int).this[int].set' cannot be extern and declare a body // set { } Diagnostic(ErrorCode.ERR_ExternHasBody, "set").WithArguments("E.extension(int).this[int].set").WithLocation(11, 13)); } [Fact] public void LookupSymbols_01() { var src = """ _ = new object()[0]; public static class E { extension(object o) { public int this[int i] => throw null; } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var e = ((Compilation)comp).GlobalNamespace.GetTypeMember("E"); AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, e, name: "this[]").ToTestDisplayStrings()); AssertEqualAndNoDuplicates(["System.Int32 E.<G>$C43E2675C7BBF9284AF22FB8A9BF0280.this[System.Int32 i] { get; }"], model.LookupSymbols(position: 0, e, name: "this[]", includeReducedExtensionMethods: true).ToTestDisplayStrings()); Assert.Empty(model.LookupSymbols(position: 0, e, name: "Item").ToTestDisplayStrings()); Assert.Empty(model.LookupSymbols(position: 0, e, name: "Item", includeReducedExtensionMethods: true).ToTestDisplayStrings()); AssertEqualAndNoDuplicates(["System.Int32 E.get_Item(System.Object o, System.Int32 i)"], model.LookupSymbols(position: 0, e, name: "get_Item").ToTestDisplayStrings()); AssertEqualAndNoDuplicates(["System.Int32 E.get_Item(System.Object o, System.Int32 i)"], model.LookupSymbols(position: 0, e, name: "get_Item", includeReducedExtensionMethods: true).ToTestDisplayStrings()); var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "this[]").ToTestDisplayStrings()); AssertEqualAndNoDuplicates(["System.Int32 E.<G>$C43E2675C7BBF9284AF22FB8A9BF0280.this[System.Int32 i] { get; }"], model.LookupSymbols(position: 0, o, name: "this[]", includeReducedExtensionMethods: true).ToTestDisplayStrings()); Assert.Empty(model.LookupSymbols(position: 0, o, name: "Item").ToTestDisplayStrings()); Assert.Empty(model.LookupSymbols(position: 0, o, name: "Item", includeReducedExtensionMethods: true).ToTestDisplayStrings()); // Indexer cannot be referenced by name Assert.DoesNotContain("System.Int32 E.<G>$C43E2675C7BBF9284AF22FB8A9BF0280.this[System.Int32 i] { get; }", model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); Assert.Empty(model.LookupNamespacesAndTypes(position: 0, o, name: null)); } [Fact] public void LookupSymbols_02() { // with generic extension block var src = """ _ = 0[1]; public static class E { extension<T>(T t) { public int this[int i] => throw null; } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "this[]").ToDisplayStrings()); AssertEqualAndNoDuplicates(["E.extension<object>(object).this[int]"], model.LookupSymbols(position: 0, o, name: "this[]", includeReducedExtensionMethods: true).ToDisplayStrings()); AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "Item").ToTestDisplayStrings()); AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "Item", includeReducedExtensionMethods: true).ToDisplayStrings()); AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "get_Item").ToTestDisplayStrings()); var s = ((Compilation)comp).GetSpecialType(SpecialType.System_String); AssertEqualAndNoDuplicates(["string.this[int]"], model.LookupSymbols(position: 0, s, name: "this[]").ToDisplayStrings()); AssertEqualAndNoDuplicates(["string.this[int]", "E.extension<string>(string).this[int]"], model.LookupSymbols(position: 0, s, name: "this[]", includeReducedExtensionMethods: true).ToDisplayStrings()); } [Fact] public void LookupSymbols_03() { // didn't fully infer var src = """ public static class E { extension<T, U>(T t) { public int this[int i] => 0; } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (5,20): error CS9295: The type parameter `U` is not referenced by either the extension parameter or a parameter of this member // public int this[int i] => 0; Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "this").WithArguments("U").WithLocation(5, 20)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); AssertEqualAndNoDuplicates(["E.extension<object, U>(object).this[int]"], model.LookupSymbols(position: 0, o, name: "this[]", includeReducedExtensionMethods: true).ToDisplayStrings()); } [Fact] public void Nullability_Indexing_01() { // maybe-null receiver with generic indexer string source = """ #nullable enable object? o = null; _ = o[0]; static class E { extension<T>(T t) { public int this[int i] { get { System.Console.Write(t is null); return 0; } } } } """; var comp = CreateCompilation(source); CompileAndVerify(comp, expectedOutput: "True").VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "o[0]"); AssertEx.Equal("E.extension<object?>(object?).this[int]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Nullability_Indexing_02() { // string indexer string source = """ #nullable enable string? o = null; try { _ = o[0]; } catch (System.NullReferenceException) { System.Console.Write("ran"); } """; var comp = CreateCompilation(source); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics( // (6,9): warning CS8602: Dereference of a possibly null reference. // _ = o[0]; Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(6, 9)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "o[0]"); AssertEx.Equal("string.this[int]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Nullability_Indexing_03() { // maybe-null argument with generic indexer string source = """ #nullable enable C c = new C(); _ = c[null]; class C { } static class E { extension<T>(T t) { public int this[T t2] { get { System.Console.Write(t2 is null); return 0; } } } } """; var comp = CreateCompilation(source); CompileAndVerify(comp, expectedOutput: "True").VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "c[null]"); AssertEx.Equal("E.extension<C?>(C?).this[C?]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Nullability_Indexing_04() { // with named arguments string source = """ #nullable enable C c = new C(); _ = c[t3: null, t2: null]; class C { } static class E { extension<T>(T t) { public int this[T t2, T t3] { get { System.Console.Write((t2 is null, t3 is null)); return 0; } } } } """; var comp = CreateCompilation(source); CompileAndVerify(comp, expectedOutput: "(True, True)").VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "c[t3: null, t2: null]"); AssertEx.Equal("E.extension<C?>(C?).this[C?, C?]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Nullability_Indexing_05() { // warning in receiver string source = """ #nullable enable _ = M(null)[0]; object? M(object o) => o; static class E { extension<T>(T t) { public int this[int i] { get { System.Console.Write(t is null); return 0; } } } } """; var comp = CreateCompilation(source); CompileAndVerify(comp, expectedOutput: "True").VerifyDiagnostics( // (3,7): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = M(null)[0]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 7)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "M(null)[0]"); AssertEx.Equal("E.extension<object?>(object?).this[int]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Nullability_Indexing_06() { // warning in argument string source = """ #nullable enable _ = new object()[M(null)]; int M(object o) => 0; static class E { extension(object o) { public int this[int i] { get { return 0; } } } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (3,20): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new object()[M(null)]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 20)); } [Fact] public void Nullability_Indexing_07() { // chained string source = """ #nullable enable object? o = null; o[0][1].ToString(); static class E { extension<T>(T t) { public T this[int i] => throw null!; } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (4,1): warning CS8602: Dereference of a possibly null reference. // o[0][1].ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o[0][1]").WithLocation(4, 1)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "o[0][1]"); AssertEx.Equal("E.extension<object?>(object?).this[int]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Nullability_Indexing_09() { // indexer parameter disallows null string source = """ #nullable enable object? oNull = null; _ = 42[oNull]; object? oNotNull = new object(); _ = 42[oNotNull]; static class E { extension(int i) { public int this[object o] { get { return 0; } } } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,8): warning CS8604: Possible null reference argument for parameter 'o' in 'int E.extension(int).this[object o]'. // _ = 42[oNull]; Diagnostic(ErrorCode.WRN_NullReferenceArgument, "oNull").WithArguments("o", "int E.extension(int).this[object o]").WithLocation(4, 8)); } [Fact] public void Nullability_Indexing_10() { string source = """ static class E { extension(ref object o) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (3,19): error CS9300: The 'ref' receiver parameter of an extension block must be a value type or a generic type constrained to struct. // extension(ref object o) Diagnostic(ErrorCode.ERR_RefExtensionParameterMustBeValueTypeOrConstrainedToOne, "object").WithLocation(3, 19)); } [Fact] public void Nullability_Indexing_11() { // generic, check returned value string source = """ #nullable enable object? oNull = null; oNull[0].ToString(); // 1 object? oNotNull = new object(); oNotNull[0].ToString(); static class E { extension<T>(T t) { public T this[int i] { get => throw null!; } } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,1): warning CS8602: Dereference of a possibly null reference. // oNull[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNull[0]").WithLocation(4, 1)); } [Fact] public void Nullability_Indexing_12() { // `ref` extension parameter var src = """ #nullable enable S1<object?> s1 = default; _ = s1[0]; S1<object> s2 = default; _ = s2[0]; // 1 S2<object?> s3 = default; _ = s3[0]; // 2 S2<object> s4 = default; _ = s4[0]; struct S1<T> { } struct S2<T> { } static class E { extension(ref S1<object?> o) { public int this[int i] { get => throw null!; } } extension(ref S2<object> o) { public int this[int i] { get => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (7,5): warning CS8620: Argument of type 'S1<object>' cannot be used for parameter 'o' of type 'S1<object?>' in 'E.extension(ref S1<object?>)' due to differences in the nullability of reference types. // _ = s2[0]; // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "s2").WithArguments("S1<object>", "S1<object?>", "o", "E.extension(ref S1<object?>)").WithLocation(7, 5), // (10,5): warning CS8620: Argument of type 'S2<object?>' cannot be used for parameter 'o' of type 'S2<object>' in 'E.extension(ref S2<object>)' due to differences in the nullability of reference types. // _ = s3[0]; // 2 Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "s3").WithArguments("S2<object?>", "S2<object>", "o", "E.extension(ref S2<object>)").WithLocation(10, 5)); } [Fact] public void Nullability_Indexing_13() { // `in` extension parameter var src = """ #nullable enable S1<object?> s1 = default; _ = s1[0]; S1<object> s2 = default; _ = s2[0]; // 1 S2<object?> s3 = default; _ = s3[0]; // 2 S2<object> s4 = default; _ = s4[0]; """; var libSrc = """ #nullable enable public struct S1<T> { } public struct S2<T> { } public static class E { extension(in S1<object?> o) { public int this[int i] { get => throw null!; } } extension(in S2<object> o) { public int this[int i] { get => throw null!; } } } """; DiagnosticDescription[] expected = [ // (7,5): warning CS8620: Argument of type 'S1<object>' cannot be used for parameter 'o' of type 'S1<object?>' in 'E.extension(in S1<object?>)' due to differences in the nullability of reference types. // _ = s2[0]; // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "s2").WithArguments("S1<object>", "S1<object?>", "o", "E.extension(in S1<object?>)").WithLocation(7, 5), // (10,5): warning CS8620: Argument of type 'S2<object?>' cannot be used for parameter 'o' of type 'S2<object>' in 'E.extension(in S2<object>)' due to differences in the nullability of reference types. // _ = s3[0]; // 2 Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "s3").WithArguments("S2<object?>", "S2<object>", "o", "E.extension(in S2<object>)").WithLocation(10, 5) ]; var comp = CreateCompilation([src, libSrc]); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()]); comp2.VerifyEmitDiagnostics(expected); } [Fact] public void Nullability_Indexing_14() { // NotNullIfNotNull var src = """ #nullable enable object? oNull = null; oNull[0].ToString(); // 1 object? oNull2 = null; E.get_Item(oNull2, 0).ToString(); // 2 object oNotNull = new object(); oNotNull[0].ToString(); E.get_Item(oNotNull, 0).ToString(); """; var libSrc = """ #nullable enable public static class E { extension(object? o) { [property: System.Diagnostics.CodeAnalysis.NotNullIfNotNull(nameof(o))] public object? this[int i] { get => throw null!; } } } """; DiagnosticDescription[] expected = [ // (4,1): warning CS8602: Dereference of a possibly null reference. // oNull[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNull[0]").WithLocation(4, 1), // (7,1): warning CS8602: Dereference of a possibly null reference. // E.get_Item(oNull2, 0).ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "E.get_Item(oNull2, 0)").WithLocation(7, 1), // Tracked by https://github.com/dotnet/roslyn/issues/37238 : NotNullIfNotNull not yet supported on indexers. The last two warnings are spurious // (10,1): warning CS8602: Dereference of a possibly null reference. // oNotNull[0].ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNotNull[0]").WithLocation(10, 1), // (12,1): warning CS8602: Dereference of a possibly null reference. // E.get_Item(oNotNull, 0).ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "E.get_Item(oNotNull, 0)").WithLocation(12, 1) ]; var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics(expected); src = """ #nullable enable object? oNull = null; new C()[oNull].ToString(); // 1 object oNotNull = new object(); new C()[oNotNull].ToString(); class C { [property: System.Diagnostics.CodeAnalysis.NotNullIfNotNull(nameof(o))] public object? this[object? o] { get => throw null!; } } """; comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); // Tracked by https://github.com/dotnet/roslyn/issues/37238 : NotNullIfNotNull not yet supported on indexers. comp.VerifyEmitDiagnostics( // (4,1): warning CS8602: Dereference of a possibly null reference. // new C()[oNull].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "new C()[oNull]").WithLocation(4, 1), // (7,1): warning CS8602: Dereference of a possibly null reference. // new C()[oNotNull].ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "new C()[oNotNull]").WithLocation(7, 1)); } [Fact] public void Nullability_Indexing_15() { // NotNull var src = """ #nullable enable new object()[0].ToString(); new object()[0] = null; E.get_Item(new object(), 0).ToString(); E.set_Item(new object(), 0, null); """; var libSrc = """ #nullable enable public static class E { extension(object o) { [property: System.Diagnostics.CodeAnalysis.NotNull] public object? this[int i] { get => throw null!; set => throw null!; } } } """; var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics(); CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: Verification.Skipped); static void validate(ModuleSymbol m) { AssertEx.SetEqual(m is SourceModuleSymbol ? new string[] { } : ["System.Diagnostics.CodeAnalysis.NotNullAttribute", "System.Runtime.CompilerServices.NullableAttribute(2)"], m.GlobalNamespace.GetMember<MethodSymbol>("E.get_Item").GetReturnTypeAttributes().ToStrings()); Assert.Empty(m.GlobalNamespace.GetMember<MethodSymbol>("E.set_Item").GetReturnTypeAttributes()); Assert.Empty(m.GlobalNamespace.GetMember<MethodSymbol>("E.set_Item").Parameters[0].GetAttributes()); } } [Fact] public void Nullability_Indexing_16() { // MaybeNull var src = """ #nullable enable new object()[0].ToString(); // 1 new object()[0] = null; // 2 new object()[0] = ""; E.get_Item(new object(), 0).ToString(); // 3 E.set_Item(new object(), 0, null); // 4 """; var libSrc = """ #nullable enable public static class E { extension(object o) { [property: System.Diagnostics.CodeAnalysis.MaybeNull] public object this[int i] { get => throw null!; set => throw null!; } } } """; DiagnosticDescription[] expected = [ // (3,1): warning CS8602: Dereference of a possibly null reference. // new object()[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "new object()[0]").WithLocation(3, 1), // (4,19): warning CS8625: Cannot convert null literal to non-nullable reference type. // new object()[0] = null; // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 19), // (7,1): warning CS8602: Dereference of a possibly null reference. // E.get_Item(new object(), 0).ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "E.get_Item(new object(), 0)").WithLocation(7, 1), // (8,29): warning CS8625: Cannot convert null literal to non-nullable reference type. // E.set_Item(new object(), 0, null); // 4 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 29) ]; var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics(expected); CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: Verification.Skipped); static void validate(ModuleSymbol m) { AssertEx.SetEqual(m is SourceModuleSymbol ? new string[] { } : ["System.Diagnostics.CodeAnalysis.MaybeNullAttribute"], m.GlobalNamespace.GetMember<MethodSymbol>("E.get_Item").GetReturnTypeAttributes().ToStrings()); Assert.Empty(m.GlobalNamespace.GetMember<MethodSymbol>("E.set_Item").GetReturnTypeAttributes()); Assert.Empty(m.GlobalNamespace.GetMember<MethodSymbol>("E.set_Item").Parameters[0].GetAttributes()); } } [Fact] public void Nullability_Indexing_17() { // AllowNull var src = """ #nullable enable new object()[0].ToString(); new object()[0] = null; E.get_Item(new object(), 0).ToString(); E.set_Item(new object(), 0, null); """; var libSrc = """ public static class E { extension(object o) { [property: System.Diagnostics.CodeAnalysis.AllowNull] public object this[int i] { get => throw null!; set => throw null!; } } } """; var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics(); CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: Verification.Skipped); static void validate(ModuleSymbol m) { AssertEx.SetEqual(m is SourceModuleSymbol ? new string[] { } : ["System.Diagnostics.CodeAnalysis.AllowNullAttribute"], m.GlobalNamespace.GetMember<MethodSymbol>("E.set_Item").Parameters[2].GetAttributes().ToStrings()); Assert.Empty(m.GlobalNamespace.GetMember<MethodSymbol>("E.set_Item").GetReturnTypeAttributes()); } } [Fact] public void Nullability_Indexing_18() { // DisallowNull var src = """ #nullable enable new object()[0].ToString(); new object()[0] = null; E.get_Item(new object(), 0).ToString(); E.set_Item(new object(), 0, null); """; var libSrc = """ #nullable enable public static class E { extension(object o) { [property: System.Diagnostics.CodeAnalysis.DisallowNull] public object? this[int i] { get => throw null!; set => throw null!; } } } """; DiagnosticDescription[] expected = [ // (3,1): warning CS8602: Dereference of a possibly null reference. // new object()[0].ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "new object()[0]").WithLocation(3, 1), // (4,19): warning CS8625: Cannot convert null literal to non-nullable reference type. // new object()[0] = null; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 19), // (6,1): warning CS8602: Dereference of a possibly null reference. // E.get_Item(new object(), 0).ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "E.get_Item(new object(), 0)").WithLocation(6, 1), // (7,29): warning CS8625: Cannot convert null literal to non-nullable reference type. // E.set_Item(new object(), 0, null); Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 29) ]; var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics(expected); CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: Verification.Skipped); static void validate(ModuleSymbol m) { AssertEx.SetEqual(m is SourceModuleSymbol ? new string[] { } : ["System.Diagnostics.CodeAnalysis.DisallowNullAttribute", "System.Runtime.CompilerServices.NullableAttribute(2)"], m.GlobalNamespace.GetMember<MethodSymbol>("E.set_Item").Parameters[2].GetAttributes().ToStrings()); Assert.Empty(m.GlobalNamespace.GetMember<MethodSymbol>("E.set_Item").GetReturnTypeAttributes()); } } [Fact] public void Nullability_Indexing_19() { // DoesNotReturn var src = """ #nullable enable bool b = false; object? o = null; if (b) { _ = new object()[0]; o.ToString(); // incorrect } if (b) { new object()[0] = 0; o.ToString(); // incorrect } if (b) { E.get_Item(new object(), 0); o.ToString(); } if (b) { E.set_Item(new object(), 0, 0); o.ToString(); } """; var libSrc = """ #nullable enable public static class E { extension(object o) { public int this[int i] { [System.Diagnostics.CodeAnalysis.DoesNotReturn] get => throw null!; [System.Diagnostics.CodeAnalysis.DoesNotReturn] set => throw null!; } } } """; // Tracked by https://github.com/dotnet/roslyn/issues/50018 : DoesNotReturn not yet supported on indexers. var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (9,5): warning CS8602: Dereference of a possibly null reference. // o.ToString(); // incorrect Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(9, 5), // (15,5): warning CS8602: Dereference of a possibly null reference. // o.ToString(); // incorrect Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(15, 5)); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics( // (9,5): warning CS8602: Dereference of a possibly null reference. // o.ToString(); // incorrect Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(9, 5), // (15,5): warning CS8602: Dereference of a possibly null reference. // o.ToString(); // incorrect Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(15, 5)); } [Fact] public void Nullability_Indexing_20() { // NotNullWhen var src = """ #nullable enable object? o = null; if (o[0]) o.ToString(); else o.ToString(); // 1 object? o2 = null; if (E.get_Item(o2, 0)) o2.ToString(); else o2.ToString(); // 2 """; var libSrc = """ #nullable enable public static class E { extension([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] object? o) { public bool this[int i] => throw null!; } } """; DiagnosticDescription[] expected = [ // (7,5): warning CS8602: Dereference of a possibly null reference. // o.ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(7, 5), // (13,5): warning CS8602: Dereference of a possibly null reference. // o2.ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o2").WithLocation(13, 5) ]; var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics(expected); } [Fact] public void Nullability_Indexing_21() { // MaybeNullWhen var src = """ #nullable enable object o = new object(); if (o[0]) o.ToString(); // 1 else o.ToString(); object o2 = new object(); if (E.get_Item(o2, 0)) o2.ToString(); // 2 else o2.ToString(); """; var libSrc = """ #nullable enable public static class E { extension([System.Diagnostics.CodeAnalysis.MaybeNullWhen(true)] object? o) { public bool this[int i] => throw null!; } } """; DiagnosticDescription[] expected = [ // (5,5): warning CS8602: Dereference of a possibly null reference. // o.ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(5, 5), // (11,5): warning CS8602: Dereference of a possibly null reference. // o2.ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o2").WithLocation(11, 5) ]; var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics(expected); } [Fact] public void Nullability_Indexing_22() { // MemberNotNull var src = """ #nullable enable if (new object()[0]) object.P2.ToString(); // 1 else object.P2.ToString(); if (E.get_Item(new object(), 0)) E.get_P2().ToString(); // 2 else E.get_P2().ToString(); """; var libSrc = """ #nullable enable public static class E { extension(object o) { [System.Diagnostics.CodeAnalysis.MemberNotNull("P2")] public bool this[int i] => throw null!; public static object? P2 => throw null!; } } """; // Tracked by https://github.com/dotnet/roslyn/issues/78828 : nullability, should we extend member post-conditions to work with extension members? DiagnosticDescription[] expected = [ // (4,5): warning CS8602: Dereference of a possibly null reference. // object.P2.ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "object.P2").WithLocation(4, 5), // (6,5): warning CS8602: Dereference of a possibly null reference. // object.P2.ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "object.P2").WithLocation(6, 5), // (9,5): warning CS8602: Dereference of a possibly null reference. // E.get_P2().ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "E.get_P2()").WithLocation(9, 5), // (11,5): warning CS8602: Dereference of a possibly null reference. // E.get_P2().ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "E.get_P2()").WithLocation(11, 5) ]; var comp = CreateCompilation([src, libSrc], targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc, targetFramework: TargetFramework.Net100); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()], targetFramework: TargetFramework.Net100); comp2.VerifyEmitDiagnostics(expected); } [Fact] public void Nullability_Indexing_23() { // value of type parameter as receiver var src = """ #nullable enable public static class E { extension<T>(T t) { public int this[int i] { get { _ = t[0]; return 42; } } public int M() { _ = t.M(); return 42; } } } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Indexing_24() { // nullability check on the receiver, un-annotated extension parameter var src = """ #nullable enable object? oNull = null; _ = oNull[0]; object? oNull2 = null; E.get_Item(oNull2, 0); object? oNotNull = new object(); _ = oNotNull[0]; E.get_Item(oNotNull, 0); """; var libSrc = """ #nullable enable public static class E { extension(object o) { public int this[int i] { get => throw null!; } } } """; DiagnosticDescription[] expected = [ // (4,5): warning CS8604: Possible null reference argument for parameter 'o' in 'E.extension(object)'. // _ = oNull[0]; Diagnostic(ErrorCode.WRN_NullReferenceArgument, "oNull").WithArguments("o", "E.extension(object)").WithLocation(4, 5), // (7,12): warning CS8604: Possible null reference argument for parameter 'o' in 'int E.get_Item(object o, int i)'. // E.get_Item(oNull2, 0); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "oNull2").WithArguments("o", "int E.get_Item(object o, int i)").WithLocation(7, 12) ]; var comp = CreateCompilation([src, libSrc]); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()]); comp2.VerifyEmitDiagnostics(expected); } [Fact] public void Nullability_Indexing_25() { // nullability check on the receiver, annotated extension parameter var src = """ #nullable enable object? oNull = null; _ = oNull[0]; object? oNull2 = null; E.get_Item(oNull2, 0); object? oNotNull = new object(); _ = oNotNull[0]; E.get_Item(oNotNull, 0); """; var libSrc = """ #nullable enable public static class E { extension(object? o) { public int this[int i] { get => throw null!; } } } """; DiagnosticDescription[] expected = []; var comp = CreateCompilation([src, libSrc]); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()]); comp2.VerifyEmitDiagnostics(expected); } [Fact] public void Nullability_Indexing_26() { // nullability check on the return value var src = """ #nullable enable object o1 = new C()[0]; // 1 object? o2 = new C()[0]; object o3 = E.get_Item(new C(), 0); // 2 object? o4 = E.get_Item(new C(), 0); object o5 = new D()[0]; object? o6 = new D()[0]; object o7 = E.get_Item(new D(), 0); object? o8 = E.get_Item(new D(), 0); """; var libSrc = """ #nullable enable public class C { } public class D { } public static class E { extension(C c) { public object? this[int i] { get => throw null!; } } extension(D d) { public object this[int i] { get => throw null!; } } } """; DiagnosticDescription[] expected = [ // (3,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // object o1 = new C()[0]; // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "new C()[0]").WithLocation(3, 13), // (6,13): warning CS8600: Converting null literal or possible null value to non-nullable type. // object o3 = E.get_Item(new C(), 0); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "E.get_Item(new C(), 0)").WithLocation(6, 13) ]; var comp = CreateCompilation([src, libSrc]); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()]); comp2.VerifyEmitDiagnostics(expected); } [Fact] public void Nullability_Indexing_27() { // nullability check on the set value var src = """ #nullable enable new C()[0] = null; new C()[0] = new object(); E.set_Item(new C(), 0, null); E.set_Item(new C(), 0, new object()); new D()[0] = null; // 1 new D()[0] = new object(); E.set_Item(new D(), 0, null); // 2 E.set_Item(new D(), 0, new object()); """; var libSrc = """ #nullable enable public class C { } public class D { } public static class E { extension(C c) { public object? this[int i] { set => throw null!; } } extension(D d) { public object this[int i] { set => throw null!; } } } """; DiagnosticDescription[] expected = [ // (9,14): warning CS8625: Cannot convert null literal to non-nullable reference type. // new D()[0] = null; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(9, 14), // (12,24): warning CS8625: Cannot convert null literal to non-nullable reference type. // E.set_Item(new D(), 0, null); // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(12, 24) ]; var comp = CreateCompilation([src, libSrc]); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()]); comp2.VerifyEmitDiagnostics(expected); } [Fact] public void Nullability_Indexing_28() { // nullability check on compound assignment var src = """ #nullable enable new C()[0] ??= null; new C()[0] ??= new object(); new D()[0] ??= null; // 1 new D()[0] ??= new object(); public class C { } public class D { } static class E { extension(C c) { public object? this[int i] { get => throw null!; set => throw null!; } } extension(D d) { public object this[int i] { get => throw null!; set => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (6,16): warning CS8625: Cannot convert null literal to non-nullable reference type. // new D()[0] ??= null; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(6, 16)); } [Fact] public void Nullability_Indexing_29() { // generic extension parameter, property read access var src = """ #nullable enable object? oNull = null; oNull[0].ToString(); // 1 object? oNotNull = new object(); oNotNull[0].ToString(); static class E { extension<T>(T t) { public T this[int i] { get => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (4,1): warning CS8602: Dereference of a possibly null reference. // oNull[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNull[0]").WithLocation(4, 1)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var elementAccess1 = GetSyntax<ElementAccessExpressionSyntax>(tree, "oNull[0]"); AssertEx.Equal("System.Object? E.extension<System.Object?>(System.Object?).this[System.Int32 i] { get; }", model.GetSymbolInfo(elementAccess1).Symbol.ToTestDisplayString(includeNonNullable: true)); var elementAccess2 = GetSyntax<ElementAccessExpressionSyntax>(tree, "oNotNull[0]"); AssertEx.Equal("System.Object! E.extension<System.Object!>(System.Object!).this[System.Int32 i] { get; }", model.GetSymbolInfo(elementAccess2).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_Indexing_30() { // generic extension parameter, instance member, property write access var src = """ #nullable enable object? oNull = null; oNull[0] = null; object? oNull2 = null; oNull2[0] = new object(); object? oNotNull = new object(); oNotNull[0] = null; // 1 E.set_Item(oNotNull, 0, null); oNotNull[0] = new object(); oNotNull?[0] = null; // 2 static class E { extension<T>(T t) { public T this[int i] { set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (10,15): warning CS8625: Cannot convert null literal to non-nullable reference type. // oNotNull[0] = null; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 15), // (15,16): warning CS8625: Cannot convert null literal to non-nullable reference type. // oNotNull?[0] = null; // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(15, 16)); } [Fact] public void Nullability_Indexing_31() { // notnull constraint var src = """ #nullable enable object? oNull = null; _ = oNull[0]; // 1 object? oNull2 = null; _ = oNull2?[0]; object? oNotNull = new object(); _ = oNotNull[0]; static class E { extension<T>(T t) where T : notnull { public T this[int i] { get => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (4,5): warning CS8714: The type 'object?' cannot be used as type parameter 'T' in the generic type or method 'E.extension<T>(T)'. Nullability of type argument 'object?' doesn't match 'notnull' constraint. // _ = oNull[0]; // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "oNull[0]").WithArguments("E.extension<T>(T)", "T", "object?").WithLocation(4, 5)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var elementAccess1 = GetSyntax<ElementAccessExpressionSyntax>(tree, "oNull[0]"); AssertEx.Equal("System.Object? E.extension<System.Object?>(System.Object?).this[System.Int32 i] { get; }", model.GetSymbolInfo(elementAccess1).Symbol.ToTestDisplayString(includeNonNullable: true)); var elementAccess2 = GetSyntax<ElementAccessExpressionSyntax>(tree, "oNotNull[0]"); AssertEx.Equal("System.Object! E.extension<System.Object!>(System.Object!).this[System.Int32 i] { get; }", model.GetSymbolInfo(elementAccess2).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_Indexing_32() { // notnull constraint, in tuple var src = """ #nullable enable object? oNull = null; _ = (1, oNull[0]); static class E { extension<T>(T t) where T : notnull { public T this[int i] { get => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (4,9): warning CS8714: The type 'object?' cannot be used as type parameter 'T' in the generic type or method 'E.extension<T>(T)'. Nullability of type argument 'object?' doesn't match 'notnull' constraint. // _ = (1, oNull[0]); Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "oNull[0]").WithArguments("E.extension<T>(T)", "T", "object?").WithLocation(4, 9)); } [Fact] public void Nullability_Indexing_34() { // implicit reference conversion on the receiver var src = """ #nullable enable C? cNull = null; _ = cNull[0]; C? cNotNull = new C(); _ = cNotNull[0]; public class C { } static class E { extension(object? o) { public int this[int i] { get => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); } [Fact] public void Nullability_Indexing_35() { // implicit reference conversion on the receiver var src = """ #nullable enable C? cNull = null; _ = cNull[0]; // 1 C? cNotNull = new C(); _ = cNotNull[0]; public class C { } static class E { extension(object o) { public int this[int i] { get => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (4,5): warning CS8604: Possible null reference argument for parameter 'o' in 'E.extension(object)'. // _ = cNull[0]; // 1 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "cNull").WithArguments("o", "E.extension(object)").WithLocation(4, 5)); } [Fact] public void Nullability_Indexing_36() { // optional parameter var src = """ #nullable enable object o = new object(); _ = o[0]; o[0] = 0; static class E { extension<T>(T t) { public int this[int i1, int i2 = 42] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Indexing_37() { // optional parameter with nullability warning var src = """ #nullable enable object o = new object(); _ = o[0]; o[0] = 0; static class E { extension(object o1) { public int this[int i1, object o2 = null] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (11,45): warning CS8625: Cannot convert null literal to non-nullable reference type. // public int this[int i1, object o2 = null] { get => throw null!; set => throw null!; } Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(11, 45)); } [Fact] public void Nullability_ObjectInitializer_01() { string source = """ #nullable enable _ = new object() { [null] = 1 }; // 1 E.set_Item(new object(), null, 1); _ = new object() { [null!] = 1 }; static class E { extension<T>(T t) { public int this[T t2] { set { System.Console.Write(t2 is null); } } } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ImplicitElementAccessSyntax>(tree, "[null]"); AssertEx.Equal("E.extension<object?>(object?).this[object?]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Nullability_ObjectInitializer_02() { string source = """ #nullable enable _ = new object() { [t2: (object?)null, i1: 42] = "" }; static class E { extension<T>(object o) { public string this[int i1, T t2] { set { System.Console.Write((i1, t2 is null)); } } } } """; var comp = CreateCompilation(source); CompileAndVerify(comp, expectedOutput: "(42, True)").VerifyDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ImplicitElementAccessSyntax>(tree, "[t2: (object?)null, i1: 42]"); AssertEx.Equal("E.extension<object?>(object).this[int, object?]", model.GetSymbolInfo(memberAccess).Symbol.ToDisplayString()); } [Fact] public void Nullability_ObjectInitializer_03() { // nested initializer string source = """ #nullable enable _ = new object() { [null] = { [null] = new object() } }; static class E { extension(object o1) { public object this[object o2] { get => throw null!; set { } } } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (3,21): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new object() { [null] = { [null] = new object() } }; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 21), // (3,32): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new object() { [null] = { [null] = new object() } }; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 32)); } [Fact] public void Nullability_ObjectInitializer_04() { var src = """ #nullable enable object? oNull = null; _ = new object() { [0] = oNull }; // 1 object oNotNull = new object(); _ = new object() { [0] = oNotNull }; static class E { extension(object o) { public object this[int i] { set { } } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (4,26): warning CS8601: Possible null reference assignment. // _ = new object() { [0] = oNull }; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNull").WithLocation(4, 26)); } [Fact] public void Nullability_ObjectInitializer_05() { var src = """ #nullable enable _ = new object() { [0] = 42 }; static class E { extension<T>(T t) { public int this[int i] { set { } } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, "[0] = 42"); AssertEx.Equal("System.Int32 E.extension<System.Object!>(System.Object!).this[System.Int32 i] { set; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_ObjectInitializer_06() { var src = """ #nullable enable object? oNull = null; _ = new object() { [0] = oNull }; // 1 object oNotNull = new object(); _ = new object() { [0] = oNotNull }; static class E { extension<T>(T t) { public T this[int i] { set { } } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (4,26): warning CS8601: Possible null reference assignment. // _ = new object() { [0] = oNull }; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNull").WithLocation(4, 26)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, "[0] = oNull"); AssertEx.Equal("System.Object! E.extension<System.Object!>(System.Object!).this[System.Int32 i] { set; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); assignment = GetSyntax<AssignmentExpressionSyntax>(tree, "[0] = oNotNull"); AssertEx.Equal("System.Object! E.extension<System.Object!>(System.Object!).this[System.Int32 i] { set; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_ObjectInitializer_07() { var src = """ #nullable enable _ = new C<string>() { [0] = null }; // 1 _ = new C<string>() { [0] = "a" }; _ = new C<string?>() { [0] = null }; _ = new C<string?>() { [0] = "a" }; class C<T> { } static class E { extension<T>(C<T> c) { public T this[int i] { set { } } } } """; var comp = CreateCompilation(src); comp.VerifyTypes(comp.SyntaxTrees[0]); comp.VerifyEmitDiagnostics( // (3,29): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new C<string>() { [0] = null }; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 29)); } [Fact] public void Nullability_ObjectInitializer_08() { var src = """ #nullable enable var s = "a"; Use(s, (new(s) { [0] = null })/*T:C<string!>!*/); // 1 Use(s, (new(s) { [0] = "a" })/*T:C<string!>!*/); Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 2 Use("a", (new(s) { [0] = "a" })/*T:C<string!>!*/); Use(s, (new("a") { [0] = null })/*T:C<string!>!*/); // 3 Use(s, (new("a") { [0] = "a" })/*T:C<string!>!*/); Use("a", (new("a") { [0] = null })/*T:C<string!>!*/); // 4 Use("a", (new("a") { [0] = "a" })/*T:C<string!>!*/); if (s != null) return; Use(s, (new(s) { [0] = null })/*T:C<string?>!*/); if (s != null) return; Use(s, (new(s) { [0] = "a" })/*T:C<string?>!*/); if (s != null) return; Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 5 if (s != null) return; Use("a", (new(s) { [0] = "a" })/*T:C<string!>!*/); // 6 if (s != null) return; Use(s, (new("a") { [0] = null })/*T:C<string?>!*/); if (s != null) return; Use(s, (new("a") { [0] = "a" })/*T:C<string?>!*/); if (s != null) return; Use("a", (new("a") { [0] = null })/*T:C<string!>!*/); // 7 if (s != null) return; Use("a", (new("a") { [0] = "a" })/*T:C<string!>!*/); void Use<T>(T value, C<T> c) => throw null!; record C<T>(T Value) { } static class E { extension<T>(C<T> c) { public T this[int i] { set { } } } } """; var comp = CreateCompilation([src, IsExternalInitTypeDefinition]); comp.VerifyTypes(comp.SyntaxTrees[0]); comp.VerifyEmitDiagnostics( // (4,24): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use(s, (new(s) { [0] = null })/*T:C<string!>!*/); // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 24), // (7,26): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 26), // (10,26): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use(s, (new("a") { [0] = null })/*T:C<string!>!*/); // 3 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 26), // (13,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use("a", (new("a") { [0] = null })/*T:C<string!>!*/); // 4 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(13, 28), // (23,15): warning CS8604: Possible null reference argument for parameter 'Value' in 'C<string>.C(string Value)'. // Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 5 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("Value", "C<string>.C(string Value)").WithLocation(23, 15), // (23,26): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 5 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(23, 26), // (26,15): warning CS8604: Possible null reference argument for parameter 'Value' in 'C<string>.C(string Value)'. // Use("a", (new(s) { [0] = "a" })/*T:C<string!>!*/); // 6 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("Value", "C<string>.C(string Value)").WithLocation(26, 15), // (35,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use("a", (new("a") { [0] = null })/*T:C<string!>!*/); // 7 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(35, 28)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntaxes<AssignmentExpressionSyntax>(tree, "[0] = null").First(); AssertEx.Equal("System.String! E.extension<System.String!>(C<System.String!>!).this[System.Int32 i] { set; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_ObjectInitializer_09() { // notnull constraint var src = """ #nullable enable var s = "a"; Use(s, (new(s) { [0] = null })/*T:C<string!>!*/); // 1 Use(s, (new(s) { [0] = "a" })/*T:C<string!>!*/); Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 2 Use("a", (new(s) { [0] = "a" })/*T:C<string!>!*/); Use(s, (new("a") { [0] = null })/*T:C<string!>!*/); // 3 Use(s, (new("a") { [0] = "a" })/*T:C<string!>!*/); Use("a", (new("a") { [0] = null })/*T:C<string!>!*/); // 4 Use("a", (new("a") { [0] = "a" })/*T:C<string!>!*/); if (s != null) return; Use(s, (new(s) { [0] = null })/*T:C<string?>!*/); // 5 if (s != null) return; Use(s, (new(s) { [0] = "a" })/*T:C<string?>!*/); // 6 if (s != null) return; Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 7 if (s != null) return; Use("a", (new(s) { [0] = "a" })/*T:C<string!>!*/); // 8 if (s != null) return; Use(s, (new("a") { [0] = null })/*T:C<string?>!*/); // 9 if (s != null) return; Use(s, (new("a") { [0] = "a" })/*T:C<string?>!*/); // 10 if (s != null) return; Use("a", (new("a") { [0] = null })/*T:C<string!>!*/); // 11 if (s != null) return; Use("a", (new("a") { [0] = "a" })/*T:C<string!>!*/); void Use<T>(T value, C<T> c) => throw null!; record C<T>(T Value) { } static class E { extension<T>(C<T> c) where T : notnull { public T this[int i] { set { } } } } """; var comp = CreateCompilation([src, IsExternalInitTypeDefinition]); comp.VerifyTypes(comp.SyntaxTrees[0]); comp.VerifyEmitDiagnostics( // (4,24): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use(s, (new(s) { [0] = null })/*T:C<string!>!*/); // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 24), // (7,26): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 26), // (10,26): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use(s, (new("a") { [0] = null })/*T:C<string!>!*/); // 3 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 26), // (13,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use("a", (new("a") { [0] = null })/*T:C<string!>!*/); // 4 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(13, 28), // (17,18): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'E.extension<T>(C<T>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // Use(s, (new(s) { [0] = null })/*T:C<string?>!*/); // 5 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "[0]").WithArguments("E.extension<T>(C<T>)", "T", "string?").WithLocation(17, 18), // (20,18): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'E.extension<T>(C<T>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // Use(s, (new(s) { [0] = "a" })/*T:C<string?>!*/); // 6 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "[0]").WithArguments("E.extension<T>(C<T>)", "T", "string?").WithLocation(20, 18), // (23,15): warning CS8604: Possible null reference argument for parameter 'Value' in 'C<string>.C(string Value)'. // Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 7 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("Value", "C<string>.C(string Value)").WithLocation(23, 15), // (23,26): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use("a", (new(s) { [0] = null })/*T:C<string!>!*/); // 7 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(23, 26), // (26,15): warning CS8604: Possible null reference argument for parameter 'Value' in 'C<string>.C(string Value)'. // Use("a", (new(s) { [0] = "a" })/*T:C<string!>!*/); // 8 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("Value", "C<string>.C(string Value)").WithLocation(26, 15), // (29,20): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'E.extension<T>(C<T>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // Use(s, (new("a") { [0] = null })/*T:C<string?>!*/); // 9 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "[0]").WithArguments("E.extension<T>(C<T>)", "T", "string?").WithLocation(29, 20), // (32,20): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'E.extension<T>(C<T>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // Use(s, (new("a") { [0] = "a" })/*T:C<string?>!*/); // 10 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "[0]").WithArguments("E.extension<T>(C<T>)", "T", "string?").WithLocation(32, 20), // (35,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // Use("a", (new("a") { [0] = null })/*T:C<string!>!*/); // 11 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(35, 28)); } [Fact] public void Nullability_ObjectInitializer_10() { var src = """ #nullable enable var s = "a"; Create(s).Use(new() { [0] = null }); Create(s).Use(new() { [0] = "a" }); if (s != null) return; Create(s).Use(new() { [0] = null }); if (s != null) return; Create(s).Use(new() { [0] = "a" }); Consumer<T> Create<T>(T value) => throw null!; class Consumer<T> { public void Use(C<T> c) => throw null!; } record C<T> { } static class E { extension<T>(C<T> c) { public T this[int i] { set { } } } } """; var comp = CreateCompilation([src, IsExternalInitTypeDefinition]); comp.VerifyEmitDiagnostics( // (5,29): warning CS8625: Cannot convert null literal to non-nullable reference type. // Create(s).Use(new() { [0] = null }); Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 29)); } [Fact] public void Nullability_ObjectInitializer_11() { // nested var src = """ #nullable enable _ = new object() { [0] = { F = "" } }; public static class E { extension<T>(T t) { public C this[int i] { get => new C(); } } } public class C { public string? F; } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, """[0] = { F = "" }"""); AssertEx.Equal("C! E.extension<System.Object!>(System.Object!).this[System.Int32 i] { get; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_ObjectInitializer_12() { // nested var src = """ #nullable enable object o = new() { [0] = { F = "" } }; public static class E { extension<T>(T t) { public C this[int i] { get => new C(); } } } public class C { public string? F; } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, """[0] = { F = "" }"""); AssertEx.Equal("C! E.extension<System.Object!>(System.Object!).this[System.Int32 i] { get; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_ObjectInitializer_13() { // nested var src = """ #nullable enable _ = new C<string>() { [0] = { F = null } }; // 1 public static class E { extension<T>(C<T> c) { public C<T> this[int i] { get => c; } } } public class C<T> { public T F = default!; } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,35): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new C<string>() { [0] = { F = null } }; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 35)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, "[0] = { F = null }"); AssertEx.Equal("C<System.String!>! E.extension<System.String!>(C<System.String!>!).this[System.Int32 i] { get; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_ObjectInitializer_14() { // nested, target-typed var src = """ #nullable enable C<string> c = new() { [0] = { F = null } }; // 1 public static class E { extension<T>(C<T> c) { public C<T> this[int i] { get => c; } } } public class C<T> { public T F = default!; } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,35): warning CS8625: Cannot convert null literal to non-nullable reference type. // C<string> c = new() { [0] = { F = null } }; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 35)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, "[0] = { F = null }"); AssertEx.Equal("C<System.String!>! E.extension<System.String!>(C<System.String!>!).this[System.Int32 i] { get; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_ObjectInitializer_15() { // nested, target-typed, no getter var src = """ #nullable enable C<string> c = new() { [0] = { F = null } }; public static class E { extension<T>(C<T> c) { public C<T> this[int i] { set { } } // no getter } } public class C<T> { public T F = default!; } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,23): error CS0154: The property or indexer 'E.extension<string>(C<string>).this[int]' cannot be used in this context because it lacks the get accessor // C<string> c = new() { [0] = { F = null } }; Diagnostic(ErrorCode.ERR_PropertyLacksGet, "[0]").WithArguments("E.extension<string>(C<string>).this[int]").WithLocation(3, 23), // (3,35): warning CS8625: Cannot convert null literal to non-nullable reference type. // C<string> c = new() { [0] = { F = null } }; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 35)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, "[0] = { F = null }"); Assert.Null(model.GetSymbolInfo(assignment.Left).Symbol); } [Fact] public void Nullability_ObjectInitializer_16() { // nested, no setter var src = """ #nullable enable _ = new C<string>() { P = { [0] = null } }; public static class E { extension<T>(C<T> c) { public T this[int i] => default!; // no setter } } public class C<T> { public C<T> P { get => this; } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,29): error CS0200: Property or indexer 'E.extension<string>(C<string>).this[int]' cannot be assigned to -- it is read only // _ = new C<string>() { P = { [0] = null } }; Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "[0]").WithArguments("E.extension<string>(C<string>).this[int]").WithLocation(3, 29), // (3,35): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new C<string>() { P = { [0] = null } }; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 35)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, "[0] = null"); Assert.Null(model.GetSymbolInfo(assignment.Left).Symbol); } [Fact] public void Nullability_ObjectInitializer_17() { // nested var src = """ #nullable enable _ = new C<string>() { P = { [0] = null } }; public static class E { extension<T>(C<T> c) { public T this[int i] { set { } } } } public class C<T> { public C<T> P { get => this; } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,35): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new C<string>() { P = { [0] = null } }; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 35)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var assignment = GetSyntax<AssignmentExpressionSyntax>(tree, "[0] = null"); AssertEx.Equal("System.String! E.extension<System.String!>(C<System.String!>!).this[System.Int32 i] { set; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_ObjectInitializer_18() { string source = """ #nullable enable _ = new object() { [null] = 1 }; // 1 """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (3,20): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = new object() { [null] = 1 }; // 1 Diagnostic(ErrorCode.ERR_BadIndexLHS, "[null]").WithArguments("object").WithLocation(3, 20)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax<ImplicitElementAccessSyntax>(tree, "[null]"); Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); } [Fact] public void Nullability_ObjectInitializer_19() { // optional parameter var src = """ #nullable enable _ = new C<string>() { P = { [0] = null } }; public static class E { extension<T>(C<T> c) { public T this[int i1, int i2 = 42] { set { } } } } public class C<T> { public C<T> P { get => this; } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,35): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new C<string>() { P = { [0] = null } }; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 35)); } [Fact] public void Nullability_ObjectInitializer_20() { // target-typed, warning in indexer arguments var src = """ #nullable enable object c = new() { [null, 0, null] = 10 }; // 1, 2 public static class E { extension(object o1) { public int this[object o2, int i, object o3] { set => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,21): warning CS8625: Cannot convert null literal to non-nullable reference type. // object c = new() { [null, 0, null] = 10 }; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 21), // (3,30): warning CS8625: Cannot convert null literal to non-nullable reference type. // object c = new() { [null, 0, null] = 10 }; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 30)); } [Fact] public void Nullability_ObjectInitializer_21() { var src = """ #nullable enable object c = new() { [0] = 10, [1] = 20 }; public static class E { extension(ref object? o) { public int this[int i] { set { o = null; } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,20): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // object c = new() { [0] = 10, [1] = 20 }; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[0]").WithArguments("object").WithLocation(3, 20), // (3,30): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // object c = new() { [0] = 10, [1] = 20 }; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[1]").WithArguments("object").WithLocation(3, 30), // (7,19): error CS9300: The 'ref' receiver parameter of an extension block must be a value type or a generic type constrained to struct. // extension(ref object? o) Diagnostic(ErrorCode.ERR_RefExtensionParameterMustBeValueTypeOrConstrainedToOne, "object?").WithLocation(7, 19)); } [Fact] public void Nullability_Increment_01() { string source = """ #nullable enable new object()[0]++; public class C { public static C? operator ++(C? c) => throw null!; } public static class E { extension(object o) { public C? this[int i] { get => throw null!; set { } } } } """; CreateCompilation(source).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Increment_02() { string source = """ #nullable enable new object()[0]++; public class C { public static C? operator ++(C? c) => throw null!; } public static class E { extension(object o) { [property: System.Diagnostics.CodeAnalysis.DisallowNull] public C? this[int i] { get => throw null!; set { } } } } """; CreateCompilation(source, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Increment_03() { string source = """ #nullable enable new object()[0]++; public class C { public static C operator ++(C c) => throw null!; } public static class E { extension(object o) { public C this[int i] { get => throw null!; set { } } } } """; CreateCompilation(source, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Increment_04() { string source = """ #nullable enable new object()[0]++; public class C { public static C operator ++(C c) => throw null!; } public static class E { extension(object o) { [property: System.Diagnostics.CodeAnalysis.MaybeNull] public C this[int i] { get => throw null!; set { } } } } """; CreateCompilation(source, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (3,1): warning CS8604: Possible null reference argument for parameter 'c' in 'C C.operator ++(C c)'. // new object()[0]++; Diagnostic(ErrorCode.WRN_NullReferenceArgument, "new object()[0]").WithArguments("c", "C C.operator ++(C c)").WithLocation(3, 1)); } [Fact] public void Nullability_Increment_05() { // with supression string source = """ #nullable enable new object()[0]!++; public class C { public static C operator ++(C c) => throw null!; } public static class E { extension(object o) { [property: System.Diagnostics.CodeAnalysis.MaybeNull] public C this[int i] { get => throw null!; set { } } } } """; CreateCompilation(source, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); } [Fact] public void Nullability_ListPattern_01() { string source = """ #nullable enable if (new C<string?>() is [var x1]) { string y1 = x1; } if (new C<string>() is [var x2]) { string y2 = x2; } public class C<T> { public int Length => throw null!; } public static class E { extension<T>(C<T> c) { public T this[System.Index i] { get => throw null!; } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (5,17): warning CS8600: Converting null literal or possible null value to non-nullable type. // string y1 = x1; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x1").WithLocation(5, 17)); } [Fact] public void Cref_01() { var src = """ /// <see cref="E.extension(int).this[string]"/> /// <see cref="E.extension(int).get_Item(string)"/> /// <see cref="E.extension(int).get_Item"/> /// <see cref="E.extension(int).set_Item(string, int)"/> /// <see cref="E.extension(int).set_Item"/> /// <see cref="E.get_Item(int, string)"/> /// <see cref="E.get_Item"/> /// <see cref="E.set_Item(int, string, int)"/> /// <see cref="E.set_Item"/> /// <see cref="E.extension(int).this[]"/> /// <see cref="E.extension(int).Item(string)"/> public static class E { extension(int i) { /// <summary></summary> public int this[string s] { get => throw null; set => throw null; } } } """; var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics( // (10,16): warning CS1574: XML comment has cref attribute 'extension(int).this[]' that could not be resolved // /// <see cref="E.extension(int).this[]"/> Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).this[]").WithArguments("extension(int).this[]").WithLocation(10, 16), // (11,16): warning CS1574: XML comment has cref attribute 'extension(int).Item(string)' that could not be resolved // /// <see cref="E.extension(int).Item(string)"/> Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).Item(string)").WithArguments("extension(int).Item(string)").WithLocation(11, 16)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ "(E.extension(int).this[string], System.Int32 E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s] { get; set; })", "(E.extension(int).get_Item(string), System.Int32 E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].get)", "(E.extension(int).get_Item, System.Int32 E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].get)", "(E.extension(int).set_Item(string, int), void E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].set)", "(E.extension(int).set_Item, void E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].set)", "(E.get_Item(int, string), System.Int32 E.get_Item(System.Int32 i, System.String s))", "(E.get_Item, System.Int32 E.get_Item(System.Int32 i, System.String s))", "(E.set_Item(int, string, int), void E.set_Item(System.Int32 i, System.String s, System.Int32 value))", "(E.set_Item, void E.set_Item(System.Int32 i, System.String s, System.Int32 value))", "(E.extension(int).this[], null)", "(E.extension(int).Item(string), null)"], PrintXmlCrefSymbols(tree, model)); src = """ /// <see cref="E.extension(int).this[string]"/> /// <see cref="E.extension(int).get_Item(string)"/> public static class E { extension(int i) { /// <summary></summary> public int this[string s] { get => throw null; set => throw null; } } } """; // Tracked by https://github.com/dotnet/roslyn/issues/78830 : diagnostic quality, it would be better if the location for the "feature not available" errors pointed to the extension keyword only CreateCompilation(src, parseOptions: TestOptions.Regular13.WithDocumentationMode(DocumentationMode.Diagnose)).VerifyEmitDiagnostics( // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).this[string]' that could not be resolved // /// <see cref="E.extension(int).this[string]"/> Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).this[string]").WithArguments("extension(int).this[string]").WithLocation(1, 16), // (1,18): error CS9260: Feature 'extension indexers' is not available in C# 13.0. Please use language version 15.0 or greater. // /// <see cref="E.extension(int).this[string]"/> Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "extension(int).this[string]").WithArguments("extension indexers", "15.0").WithLocation(1, 18), // (2,16): warning CS1574: XML comment has cref attribute 'extension(int).get_Item(string)' that could not be resolved // /// <see cref="E.extension(int).get_Item(string)"/> Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).get_Item(string)").WithArguments("extension(int).get_Item(string)").WithLocation(2, 16), // (2,18): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. // /// <see cref="E.extension(int).get_Item(string)"/> Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "extension(int).get_Item(string)").WithArguments("extensions", "14.0").WithLocation(2, 18), // (5,5): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. // extension(int i) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, @"extension(int i) { ").WithArguments("extensions", "14.0").WithLocation(5, 5), // (5,5): error CS0710: Static classes cannot have instance constructors // extension(int i) Diagnostic(ErrorCode.ERR_ConstructorInStaticClass, "extension").WithLocation(5, 5), // (6,6): error CS1513: } expected // { Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(6, 6), // (8,20): error CS0720: 'E.this[string]': cannot declare indexers in a static class // public int this[string s] Diagnostic(ErrorCode.ERR_IndexerInStaticClass, "this").WithArguments("E.this[string]").WithLocation(8, 20), // (14,1): error CS1022: Type or namespace definition, or end-of-file expected // } Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(14, 1)); } [Fact] public void Cref_02() { // LangVer of consuming compilation var libSrc = """ public static class E { extension(int i) { public int this[string s] { get => throw null; set => throw null; } } } """; var libComp = CreateCompilation(libSrc); var src = """ /// <see cref="E.extension(int).this[string]"/> /// <see cref="E.extension(int).get_Item(string)"/> class C { } """; var comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], parseOptions: TestOptions.Regular14.WithDocumentationMode(DocumentationMode.Diagnose)); comp.VerifyEmitDiagnostics( // (1,18): error CS9327: Feature 'extension indexers' is not available in C# 14.0. Please use language version 15.0 or greater. // /// <see cref="E.extension(int).this[string]"/> Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion14, "extension(int).this[string]").WithArguments("extension indexers", "15.0").WithLocation(1, 18)); validateCrefSymbols(comp); comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], parseOptions: TestOptions.Regular15.WithDocumentationMode(DocumentationMode.Diagnose)); comp.VerifyEmitDiagnostics(); validateCrefSymbols(comp); comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], parseOptions: TestOptions.RegularPreview.WithDocumentationMode(DocumentationMode.Diagnose)); comp.VerifyEmitDiagnostics(); validateCrefSymbols(comp); static void validateCrefSymbols(CSharpCompilation comp) { var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ "(E.extension(int).this[string], System.Int32 E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s] { get; set; })", "(E.extension(int).get_Item(string), System.Int32 E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].get)"], PrintXmlCrefSymbols(tree, model)); } } [Fact] public void Cref_03() { // [IndexerName] var libSrc = """ static class E { extension(int i) { [System.Runtime.CompilerServices.IndexerName("MyIndexer")] public int this[string s] { get => throw null; set => throw null; } } } """; var src = """ /// <see cref="E.extension(int).this[string]"/> /// <see cref="E.extension(int).get_MyIndexer(string)"/> /// <see cref="E.extension(int).get_MyIndexer"/> /// <see cref="E.extension(int).set_MyIndexer(string, int)"/> /// <see cref="E.extension(int).set_MyIndexer"/> /// <see cref="E.get_MyIndexer(int, string)"/> /// <see cref="E.get_MyIndexer"/> /// <see cref="E.set_MyIndexer(int, string, int)"/> /// <see cref="E.set_MyIndexer"/> static class C { } """; var libComp = CreateCompilation(libSrc); var comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ "(E.extension(int).this[string], System.Int32 E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s] { get; set; })", "(E.extension(int).get_MyIndexer(string), System.Int32 E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].get)", "(E.extension(int).get_MyIndexer, System.Int32 E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].get)", "(E.extension(int).set_MyIndexer(string, int), void E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].set)", "(E.extension(int).set_MyIndexer, void E.<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69.this[System.String s].set)", "(E.get_MyIndexer(int, string), System.Int32 E.get_MyIndexer(System.Int32 i, System.String s))", "(E.get_MyIndexer, System.Int32 E.get_MyIndexer(System.Int32 i, System.String s))", "(E.set_MyIndexer(int, string, int), void E.set_MyIndexer(System.Int32 i, System.String s, System.Int32 value))", "(E.set_MyIndexer, void E.set_MyIndexer(System.Int32 i, System.String s, System.Int32 value))"], PrintXmlCrefSymbols(tree, model)); } [Fact] public void Cref_04() { // generic var src = """ /// <see cref="E.extension{U}(int).this[U]"/> /// <see cref="E.extension{U}(int).get_Item(U)"/> /// <see cref="E.extension{U}(int).get_Item"/> /// <see cref="E.extension{U}(int).set_Item(U, int)"/> /// <see cref="E.extension{U}(int).set_Item"/> /// <see cref="E.get_Item{U}(int, U)"/> /// <see cref="E.get_Item"/> /// <see cref="E.set_Item{U}(int, U, int)"/> /// <see cref="E.set_Item"/> public static class E { extension<T>(int i) { /// <summary></summary> public int this[T t] { get => throw null; set => throw null; } } } """; var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ "(E.extension{U}(int).this[U], System.Int32 E.<G>$B8D310208B4544F25EEBACB9990FC73B<U>.this[U t] { get; set; })", "(E.extension{U}(int).get_Item(U), System.Int32 E.<G>$B8D310208B4544F25EEBACB9990FC73B<U>.this[U t].get)", "(E.extension{U}(int).get_Item, System.Int32 E.<G>$B8D310208B4544F25EEBACB9990FC73B<U>.this[U t].get)", "(E.extension{U}(int).set_Item(U, int), void E.<G>$B8D310208B4544F25EEBACB9990FC73B<U>.this[U t].set)", "(E.extension{U}(int).set_Item, void E.<G>$B8D310208B4544F25EEBACB9990FC73B<U>.this[U t].set)", "(E.get_Item{U}(int, U), System.Int32 E.get_Item<U>(System.Int32 i, U t))", "(E.get_Item, System.Int32 E.get_Item<T>(System.Int32 i, T t))", "(E.set_Item{U}(int, U, int), void E.set_Item<U>(System.Int32 i, U t, System.Int32 value))", "(E.set_Item, void E.set_Item<T>(System.Int32 i, T t, System.Int32 value))"], PrintXmlCrefSymbols(tree, model)); } [Fact] public void XmlDoc_01() { var src = """ static class E { /// <summary>Summary for extension block</summary> /// <typeparam name="T">Description for T</typeparam> /// <param name="t">Description for t</param> extension<T>(T t) { /// <summary>Summary for indexer with references to <typeparamref name="T"/> and <paramref name="t"/> and <paramref name="s"/>.</summary> /// <param name="s">Description for s</param> public int this[string s] { get => throw null; set => throw null; } } } """; var comp = CreateCompilation(src, assemblyName: "assembly", parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics(); var expected = """ <?xml version="1.0"?> <doc> <assembly> <name>assembly</name> </assembly> <members> <member name="M:E.get_Item``1(``0,System.String)"> <inheritdoc cref="P:E.<G>$8048A6C8BE30A622530249B904B537EB`1.Item(System.String)"/> </member> <member name="M:E.set_Item``1(``0,System.String,System.Int32)"> <inheritdoc cref="P:E.<G>$8048A6C8BE30A622530249B904B537EB`1.Item(System.String)"/> </member> <member name="T:E.<G>$8048A6C8BE30A622530249B904B537EB`1.<M>$D1693D81A12E8DED4ED68FE22D9E856F"> <summary>Summary for extension block</summary> <typeparam name="T">Description for T</typeparam> <param name="t">Description for t</param> </member> <member name="P:E.<G>$8048A6C8BE30A622530249B904B537EB`1.Item(System.String)"> <summary>Summary for indexer with references to <typeparamref name="T"/> and <paramref name="t"/> and <paramref name="s"/>.</summary> <param name="s">Description for s</param> </member> </members> </doc> """; AssertEx.Equal(expected, GetDocumentationCommentText(comp)); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.SequenceEqual(["(T, T)", "(t, T t)", "(T, T)", "(t, T t)", "(s, System.String s)", "(s, System.String s)"], PrintXmlNameSymbols(tree, model)); } [Fact] public void XmlDoc_02() { var src = """ static class E { extension<T>(T t) { public int this[string s] { /// <summary>Summary for getter</summary> get => throw null; /// <summary>Summary for setter</summary> set => throw null; } } } """; var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); comp.VerifyEmitDiagnostics( // (7,13): warning CS1587: XML comment is not placed on a valid language element // /// <summary>Summary for getter</summary> Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/").WithLocation(7, 13), // (10,13): warning CS1587: XML comment is not placed on a valid language element // /// <summary>Summary for setter</summary> Diagnostic(ErrorCode.WRN_UnprocessedXMLComment, "/").WithLocation(10, 13)); } [Theory, CombinatorialData] public void CallerArgumentExpression_01(bool useCompilationReference) { var callerSrc = """ _ = 42[0]; E.get_Item(43, 0); """; var src = callerSrc + """ public static class E { extension(int i) { public int this[int j, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(i))] string caller = ""] { get { System.Console.Write($"{caller}={i} "); return 0; } } } } """ + CallerArgumentExpressionAttributeDefinition; var expectedOutput = """ 42=42 43=43 """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); var comp2 = CreateCompilation(callerSrc, references: [AsReference(comp, useCompilationReference)]); CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] public void CallerArgumentExpression_02() { var src = """ static class E { extension(string s) { public int this[int i, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(caller))] string caller = ""] => 0; } } """ + CallerArgumentExpressionAttributeDefinition; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (5,33): warning CS8965: The CallerArgumentExpressionAttribute applied to parameter 'caller' will have no effect because it's self-referential. // public int this[int i, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(caller))] string caller = ""] => 0; Diagnostic(ErrorCode.WRN_CallerArgumentExpressionAttributeSelfReferential, "System.Runtime.CompilerServices.CallerArgumentExpression").WithArguments("caller").WithLocation(5, 33)); } [Theory, CombinatorialData] public void CallerArgumentExpression_06(bool useCompilationReference) { // non-static extension, caller expression refers to last parameter var callerSrc = """ _ = 42[43, s2: "A"]; _ = E.get_Item(42, 43, s2: "B"); """; var src = callerSrc + """ public static class E { extension(int i) { public int this[int j, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(s2))] string caller = "", string s2 = ""] { get { System.Console.Write($"{caller}={s2} "); return 0; } } } } """ + CallerArgumentExpressionAttributeDefinition; var expectedOutput = """ "A"=A "B"=B """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); var comp2 = CreateCompilation(callerSrc, references: [AsReference(comp, useCompilationReference)]); CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Theory, CombinatorialData] public void CallerArgumentExpression_16(bool useCompilationReference) { var callerSrc = """ _ = 42[43, s2: "C"]; """; var src = """ public static class E { extension(int i) { public int this[int j, string s1 = "B", string s2 = "", [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(i))] string expr_s0 = "", [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(s1))] string expr_s1 = "", [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(s2))] string expr_s2 = ""] { get { System.Console.Write($"{expr_s0}={i} {expr_s1}={s1} {expr_s2}={s2}"); return 0; } } } } """ + CallerArgumentExpressionAttributeDefinition; var expectedOutput = """ 42=42 =B "C"=C """; var comp = CreateCompilation([src, callerSrc]); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); var libComp = CreateCompilation(src); var comp2 = CreateCompilation(callerSrc, references: [AsReference(libComp, useCompilationReference)]); CompileAndVerify(comp2, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] public void CallerArgumentExpression_17() { var src = """ public static class E { extension(string s) { public int this[int j, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string expr_value = ""] { set { } } } } """ + CallerArgumentExpressionAttributeDefinition; CreateCompilation(src).VerifyEmitDiagnostics( // (5,97): error CS0103: The name 'value' does not exist in the current context // public int this[int j, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string expr_value = ""] Diagnostic(ErrorCode.ERR_NameNotInContext, "value").WithArguments("value").WithLocation(5, 97)); } [Fact] public void CallerArgumentExpression_18() { // in indexer access var src = """ _ = new object()[0]; public static class E { extension(object o) { public int this[int j, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(o))] string expr_o = ""] { get { System.Console.Write($"{expr_o}={o}"); return 0; } } } } """ + CallerArgumentExpressionAttributeDefinition; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "new object()=System.Object").VerifyDiagnostics(); } [Fact] public void CallerArgumentExpression_19() { // in object initializer var src = """ _ = new C() { [0] = 1 }; public static class E { extension(C c) { public int this[int j, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(c))] string expr_c = ""] { set { System.Console.Write($"{expr_c}={c}"); } } } } public class C { } """ + CallerArgumentExpressionAttributeDefinition; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "C=C").VerifyDiagnostics(); } [Fact] public void CallerArgumentExpression_20() { // in list pattern var src = """ _ = new C() is [0]; public static class E { extension(C c) { public int this[System.Index i, [System.Runtime.CompilerServices.CallerArgumentExpression(nameof(c))] string expr_c = ""] { get { System.Console.Write($"{expr_c}={c}"); return 0; } } } } public class C { public int Length => 1; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("[0]=C"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void CallerMemberName_01() { var src = """ _ = 42[43]; public static class E { extension(int i) { public int this[int j, [System.Runtime.CompilerServices.CallerMemberName] string s = ""] { get { System.Console.Write(s); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("<Main>$"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void CallerMemberName_02() { var src = """ _ = 42[43]; public static class E { extension(int i) { public int this[int j] { get { local(); return 0; void local([System.Runtime.CompilerServices.CallerMemberName] string s = "") { System.Console.Write(s); } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Item"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void CallerMemberName_03() { var src = """ _ = 42[43]; public static class E { extension(int i) { [System.Runtime.CompilerServices.IndexerName("MyIndexer")] public int this[int j] { get { local(); return 0; void local([System.Runtime.CompilerServices.CallerMemberName] string s = "") { System.Console.Write(s); } } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("MyIndexer"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void CallerMemberName_04() { var src = """ int i = 43; _ = 42[in i]; public static class E { extension(int i) { public int this[in int j, [System.Runtime.CompilerServices.CallerMemberName] string s = ""] { get { System.Console.Write(s); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("<Main>$"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ElementAccess_01() { var src = """ public unsafe class C { public static void Main() { int[] a = [1, 2, 3]; fixed (int* i = a) { System.Console.Write(i[1]); } } } public static unsafe class E { extension(int* p) { public int this[int i] { get => throw null; } } } """; CreateCompilation(src, options: TestOptions.UnsafeDebugExe).VerifyEmitDiagnostics( // (15,15): error CS1103: The receiver parameter of an extension cannot be of type 'int*' // extension(int* p) Diagnostic(ErrorCode.ERR_BadTypeforThis, "int*").WithArguments("int*").WithLocation(15, 15)); } [Fact] public void ElementAccess_02() { var src = """ int[] a = [1, 2, 3]; System.Console.Write(a[1]); public static class E { extension(int[] a) { public int this[int i] { get => throw null; } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); } [Fact] public void ElementAccess_03() { var src = """ string s = "abc"; System.Console.Write(s[1]); public static class E { extension(string s) { public int this[int i] { get => throw null; } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "b").VerifyDiagnostics(); } [Fact] public void Usings_01() { var src = """ using N; _ = 42[43]; namespace N { public static class E { extension(int i) { public int this[int j] { get { System.Console.Write("ran"); return 10; } } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); } [Fact] public void Usings_02() { var src = """ using static N.E; _ = 42[43]; namespace N { public static class E { extension(int i) { public int this[int j] { get { System.Console.Write("ran"); return 10; } } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); } [Fact] public void Usings_03() { var src = """ using N1; using N2; _ = 42[43]; namespace N1 { public static class E { extension(int i) { public int this[int j] { get { System.Console.Write("ran"); return 10; } } } } } namespace N2 { public static class E { extension(long i) { public int this[int j] => throw null; } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); } [Fact] public void Usings_04() { var src = """ using N1; using N2; _ = 42[43]; namespace N1 { public static class E { extension(int i) { public int this[int j] { get { System.Console.Write("ran"); return 10; } } } } } namespace N2 { public static class E { extension(int i) { public void M() { } } } } """; var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics( // (2,1): hidden CS8019: Unnecessary using directive. // using N2; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N2;").WithLocation(2, 1)); } [Fact] public void RefOmittedComCall_01() { // For COM import type, omitting the ref is allowed, but indexers with ref/out parameters are disallowed // [ComImport, Guid("1234C65D-1234-447A-B786-64682CBEF136")] //public class C { } // //public static class E //{ // extension(C c) // { // public int this[ref short p] { get { return 0; } } // } //} var ilSrc = """ .class public auto ansi import beforefieldinit C extends System.Object { .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 31 32 33 34 43 36 35 44 2d 31 32 33 34 2d 34 34 37 41 2d 42 37 38 36 2d 36 34 36 38 32 43 42 45 46 31 33 36 00 00 ) .method public hidebysig specialname rtspecialname instance void .ctor () runtime managed internalcall { } } .class public auto ansi abstract sealed beforefieldinit E extends System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .class nested public auto ansi sealed specialname '<G>$9794DAFCCB9E752B29BFD6350ADA77F2' extends System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .class nested public auto ansi abstract sealed specialname '<M>$73AD9F89912BC4337338E3DE7182B785' extends System.Object { // Methods .method public hidebysig specialname static void '<Extension>$' ( class C c ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) ret } } .method public hidebysig specialname instance int32 get_Item ( int16& p ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 37 33 41 44 39 46 38 39 39 31 32 42 43 34 33 33 37 33 33 38 45 33 44 45 37 31 38 32 42 37 38 35 00 00 ) IL_0000: newobj instance void [mscorlib]System.NotSupportedException::.ctor() IL_0005: throw } .property instance int32 Item( int16& p ) { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 37 33 41 44 39 46 38 39 39 31 32 42 43 34 33 33 37 33 33 38 45 33 44 45 37 31 38 32 42 37 38 35 00 00 ) .get instance int32 E/'<G>$9794DAFCCB9E752B29BFD6350ADA77F2'::get_Item(int16&) } } .method public hidebysig static int32 get_Item ( class C c, int16& p ) cil managed { ldc.i4.0 ret } } """ + ExtensionMarkerAttributeIL; string source = """ short x = 123; C c = new C(); _ = c[ref x]; _ = c[x]; """; var comp = CreateCompilationWithIL(source, ilSrc); comp.VerifyEmitDiagnostics( // (3,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[ref x]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[ref x]").WithArguments("C").WithLocation(3, 5), // (4,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = c[x]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[x]").WithArguments("C").WithLocation(4, 5)); } [Fact] public void RefOmittedComCall_02() { string source = @" using System; using System.Runtime.InteropServices; C c = default; c.M(); c.M2(); [ComImport, Guid(""1234C65D-1234-447A-B786-64682CBEF136"")] class C { } static class E { extension(ref C c) { public void M() { } } public static void M2(this ref C c) { } } "; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,3): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) // c.M(); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("C", "M").WithLocation(6, 3), // (7,3): error CS1061: 'C' does not contain a definition for 'M2' and no accessible extension method 'M2' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) // c.M2(); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M2").WithArguments("C", "M2").WithLocation(7, 3), // (14,19): error CS9300: The 'ref' receiver parameter of an extension block must be a value type or a generic type constrained to struct. // extension(ref C c) Diagnostic(ErrorCode.ERR_RefExtensionParameterMustBeValueTypeOrConstrainedToOne, "C").WithLocation(14, 19), // (18,24): error CS8337: The first parameter of a 'ref' extension method 'M2' must be a value type or a generic type constrained to struct. // public static void M2(this ref C c) { } Diagnostic(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, "M2").WithArguments("M2").WithLocation(18, 24)); } [Fact] public void Dynamic_01() { var src = """ dynamic d = new object(); _ = new object()[d]; _ = new object().M(d); static class E { extension(object o) { public int this[object o2] { get => 0; } public void M(object o2) { } } } """; // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, consider a dedicated error when extension indexer is found given dynamic argument var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = new object()[d]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "new object()[d]").WithArguments("object").WithLocation(2, 5), // (3,5): error CS1973: 'object' has no applicable method named 'M' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax. // _ = new object().M(d); Diagnostic(ErrorCode.ERR_BadArgTypeDynamicExtension, "new object().M(d)").WithArguments("object", "M").WithLocation(3, 5)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var indexerAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "new object()[d]"); Assert.Null(model.GetSymbolInfo(indexerAccess).Symbol); Assert.Equal([], model.GetSymbolInfo(indexerAccess).CandidateSymbols.ToDisplayStrings()); var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "new object().M(d)"); Assert.Null(model.GetSymbolInfo(invocation).Symbol); Assert.Equal([], model.GetSymbolInfo(invocation).CandidateSymbols.ToDisplayStrings()); } [Fact] public void Dynamic_02() { var src = """ dynamic d = new object(); _ = new C()[d]; _ = new C().M(d); static class E { extension(C c) { public int this[object o2] { get => 0; } public int this[int i] { get => 0; } public void M(object o2) { } public void M(int i) { } } } class C { public int this[object o1, object o2] { get => 0; } public int M(object o1, object o2) => 0; } """; // https://github.com/dotnet/roslyn/issues/78829 diagnostic quality, consider a dedicated error when extension indexer is found given dynamic argument var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (2,5): error CS7036: There is no argument given that corresponds to the required parameter 'o2' of 'C.this[object, object]' // _ = new C()[d]; Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "new C()[d]").WithArguments("o2", "C.this[object, object]").WithLocation(2, 5), // (3,5): error CS1973: 'C' has no applicable method named 'M' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax. // _ = new C().M(d); Diagnostic(ErrorCode.ERR_BadArgTypeDynamicExtension, "new C().M(d)").WithArguments("C", "M").WithLocation(3, 5)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var indexerAccess = GetSyntax<ElementAccessExpressionSyntax>(tree, "new C()[d]"); Assert.Null(model.GetSymbolInfo(indexerAccess).Symbol); Assert.Equal(["C.this[object, object]"], model.GetSymbolInfo(indexerAccess).CandidateSymbols.ToDisplayStrings()); var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "new C().M(d)"); Assert.Null(model.GetSymbolInfo(invocation).Symbol); Assert.Equal(["C.M(object, object)"], model.GetSymbolInfo(invocation).CandidateSymbols.ToDisplayStrings()); } [Fact] public void Dynamic_04() { var src = """ int i = 42; _ = i[i]; static class E { extension(object o) { public int this[dynamic d] { get { System.Console.Write(d); return 0; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void Dynamic_05() { var src = """ try { dynamic d = 42; _ = d[0]; } catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e) { System.Console.Write(e.Message); } static class E { extension(object o) { public int this[int i] => 0; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Cannot apply indexing with [] to an expression of type 'int'"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Fact] public void CheckAndCoerceArguments_01() { // Irregular/legacy behavior for indexer arguments (we skip the safety check for usage of pointer types in indexer arguments) var libSrc = """ public static unsafe class E { extension(int i) { public int this[int* j] { get => throw null; set => throw null; } } } """; var libComp = CreateCompilation(libSrc, options: TestOptions.UnsafeDebugDll); var src = """ _ = 42[null]; E.get_Item(42, null); """; var comp = CreateCompilation(src, references: [libComp.EmitToImageReference()]); comp.VerifyDiagnostics( // (1,5): error CS9363: 'E.extension(int).this[int*].get' must be used in an unsafe context because it has pointers in its signature // _ = 42[null]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "42[null]").WithArguments("E.extension(int).this[int*].get").WithLocation(1, 5), // (2,1): error CS9363: 'E.get_Item(int, int*)' must be used in an unsafe context because it has pointers in its signature // E.get_Item(42, null); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "E.get_Item(42, null)").WithArguments("E.get_Item(int, int*)").WithLocation(2, 1)); src = """ unsafe { _ = 42[null]; E.get_Item(42, null); } """; comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], options: TestOptions.UnsafeDebugExe); comp.VerifyEmitDiagnostics(); // Compared with non-extension indexers libSrc = """ public unsafe class C { public int this[int* j] { get => throw null; set => throw null; } } """; libComp = CreateCompilation(libSrc, options: TestOptions.UnsafeDebugDll); src = """ _ = new C()[null]; """; comp = CreateCompilation(src, references: [libComp.EmitToImageReference()]); comp.VerifyDiagnostics( // (1,5): error CS9363: 'C.this[int*].get' must be used in an unsafe context because it has pointers in its signature // _ = new C()[null]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "new C()[null]").WithArguments("C.this[int*].get").WithLocation(1, 5)); src = """ unsafe { _ = new C()[null]; } """; comp = CreateCompilation(src, references: [libComp.EmitToImageReference()], options: TestOptions.UnsafeDebugExe); comp.VerifyEmitDiagnostics(); } [Fact] public void UpdatedMemorySafetyRules_01() { // unsafe indexer var libSrc = """ public static class E { extension(int x) { unsafe public int this[int i] { get => x + i; set { } } } } """; var libComp = CreateCompilation(libSrc, options: TestOptions.UnsafeReleaseDll.WithUpdatedMemorySafetyRules()); var libRef = libComp.EmitToImageReference(); var src = """ var x = 111; x[0] = x[1] + 222; E.get_Item(x, 0); E.set_Item(x, 0, 0); unsafe { x[0] = x[1] + 333; } unsafe { E.get_Item(x, 0); E.set_Item(x, 0, 0); } """; CreateCompilation(src, references: [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics( // (2,1): error CS9362: 'E.extension(int).this[int].set' must be used in an unsafe context because it is marked as 'unsafe' // x[0] = x[1] + 222; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "x[0]").WithArguments("E.extension(int).this[int].set").WithLocation(2, 1), // (2,8): error CS9362: 'E.extension(int).this[int].get' must be used in an unsafe context because it is marked as 'unsafe' // x[0] = x[1] + 222; Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "x[1]").WithArguments("E.extension(int).this[int].get").WithLocation(2, 8), // (3,1): error CS9362: 'E.get_Item(int, int)' must be used in an unsafe context because it is marked as 'unsafe' // E.get_Item(x, 0); E.set_Item(x, 0, 0); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E.get_Item(x, 0)").WithArguments("E.get_Item(int, int)").WithLocation(3, 1), // (3,19): error CS9362: 'E.set_Item(int, int, int)' must be used in an unsafe context because it is marked as 'unsafe' // E.get_Item(x, 0); E.set_Item(x, 0, 0); Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "E.set_Item(x, 0, 0)").WithArguments("E.set_Item(int, int, int)").WithLocation(3, 19)); CompileAndVerify(src, references: [libRef], options: TestOptions.UnsafeReleaseExe) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void UpdatedMemorySafetyRules_02(bool useCompilationReference) { // unsafe receiver type var libSrc = """ public class C<T> { } public unsafe static class E { extension(C<int*[]> c) { public int this[int i] { get => 0; set { } } } } """; var libComp = CreateCompilation(libSrc, options: TestOptions.UnsafeReleaseDll, assemblyName: "lib"); var libRef = AsReference(libComp, useCompilationReference); var src = """ var c = new C<int*[]>(); c[0] = c[0]; E.get_Item(c, 0); E.set_Item(c, 0, 0); unsafe { c[0] = c[0]; } unsafe { E.get_Item(c, 0); E.set_Item(c, 0, 0); } """; var expectedDiagnostics = new[] { // (2,1): error CS9363: 'E.extension(C<int*[]>).this[int].set' must be used in an unsafe context because it has pointers in its signature // c[0] = c[0]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c[0]").WithArguments("E.extension(C<int*[]>).this[int].set").WithLocation(2, 1), // (2,8): error CS9363: 'E.extension(C<int*[]>).this[int].get' must be used in an unsafe context because it has pointers in its signature // c[0] = c[0]; Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "c[0]").WithArguments("E.extension(C<int*[]>).this[int].get").WithLocation(2, 8), // (3,1): error CS9363: 'E.get_Item(C<int*[]>, int)' must be used in an unsafe context because it has pointers in its signature // E.get_Item(c, 0); E.set_Item(c, 0, 0); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "E.get_Item(c, 0)").WithArguments("E.get_Item(C<int*[]>, int)").WithLocation(3, 1), // (3,19): error CS9363: 'E.set_Item(C<int*[]>, int, int)' must be used in an unsafe context because it has pointers in its signature // E.get_Item(c, 0); E.set_Item(c, 0, 0); Diagnostic(ErrorCode.ERR_UnsafeMemberOperationCompat, "E.set_Item(c, 0, 0)").WithArguments("E.set_Item(C<int*[]>, int, int)").WithLocation(3, 19), }; CreateCompilation(src, references: [libRef], options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()).VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(src, references: [libRef], options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(expectedDiagnostics); CreateCompilation(src, references: [libRef], parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] public void ConditionalAttribute_01() { var src = """ static class E { extension(int i) { [System.Diagnostics.Conditional("DEBUG")] public int this[int j] => 0; } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (5,10): error CS0592: Attribute 'System.Diagnostics.Conditional' is not valid on this declaration type. It is only valid on 'class, method' declarations. // [System.Diagnostics.Conditional("DEBUG")] Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "System.Diagnostics.Conditional").WithArguments("System.Diagnostics.Conditional", "class, method").WithLocation(5, 10)); } [Fact] public void ConditionalAttribute_02() { var src = """ static class E { extension(int i) { public int this[int j] { [System.Diagnostics.Conditional("DEBUG")] get => 0; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (7,14): error CS1667: Attribute 'System.Diagnostics.Conditional' is not valid on property or event accessors. It is only valid on 'class, method' declarations. // [System.Diagnostics.Conditional("DEBUG")] Diagnostic(ErrorCode.ERR_AttributeNotOnAccessor, "System.Diagnostics.Conditional").WithArguments("System.Diagnostics.Conditional", "class, method").WithLocation(7, 14)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_Indexing_01() { string source = """ class C { ref int M2() { int i = 0; return ref i[1]; } ref int M3() { int i = 0; return ref E.get_Item(ref i, 1); } } static class E { extension(ref int i) { public ref int this[int j] => ref i; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,20): error CS8168: Cannot return local 'i' by reference because it is not a ref local // return ref i[1]; Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(6, 20), // (6,20): error CS8347: Cannot use a result of 'E.extension(ref int).this[int]' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return ref i[1]; Diagnostic(ErrorCode.ERR_EscapeCall, "i[1]").WithArguments("E.extension(ref int).this[int]", "i").WithLocation(6, 20), // (12,20): error CS8347: Cannot use a result of 'E.get_Item(ref int, int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return ref E.get_Item(ref i, 1); Diagnostic(ErrorCode.ERR_EscapeCall, "E.get_Item(ref i, 1)").WithArguments("E.get_Item(ref int, int)", "i").WithLocation(12, 20), // (12,35): error CS8168: Cannot return local 'i' by reference because it is not a ref local // return ref E.get_Item(ref i, 1); Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(12, 35)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_Indexing_02() { string source = """ class C { ref int M2() { int i = 0; ref int ri = ref i; return ref ri[0]; } ref int M3() { int i = 0; ref int ri = ref i; return ref E.get_Item(ref ri, 0); } } static class E { extension(ref int i) { public ref int this[int j] => ref i; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (7,20): error CS8157: Cannot return 'ri' by reference because it was initialized to a value that cannot be returned by reference // return ref ri[0]; Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "ri").WithArguments("ri").WithLocation(7, 20), // (7,20): error CS8347: Cannot use a result of 'E.extension(ref int).this[int]' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return ref ri[0]; Diagnostic(ErrorCode.ERR_EscapeCall, "ri[0]").WithArguments("E.extension(ref int).this[int]", "i").WithLocation(7, 20), // (14,20): error CS8347: Cannot use a result of 'E.get_Item(ref int, int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return ref E.get_Item(ref ri, 0); Diagnostic(ErrorCode.ERR_EscapeCall, "E.get_Item(ref ri, 0)").WithArguments("E.get_Item(ref int, int)", "i").WithLocation(14, 20), // (14,35): error CS8157: Cannot return 'ri' by reference because it was initialized to a value that cannot be returned by reference // return ref E.get_Item(ref ri, 0); Diagnostic(ErrorCode.ERR_RefReturnNonreturnableLocal, "ri").WithArguments("ri").WithLocation(14, 35)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_Indexing_03() { string source = """ class C { RS MA(RS rs) => rs[0]; RS MB(RS rs) => E.get_Item(rs, 0); ref RS MC(ref RS rs) => ref rs[""]; ref RS MD(ref RS rs) => ref E.get_Item(ref rs, ""); } static class E { extension(RS rs) { public RS this[int i] => rs; } extension(ref RS rs) { public ref RS this[string s] => ref rs; } } ref struct RS { } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_Indexing_07() { string source = """ class C { void MA(ref readonly int j) { int i = 0; j = ref i[1]; } } static class E { extension(ref readonly int i) { public ref readonly int this[int j] => ref i; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,9): error CS8374: Cannot ref-assign 'i[1]' to 'j' because 'i[1]' has a narrower escape scope than 'j'. // j = ref i[1]; Diagnostic(ErrorCode.ERR_RefAssignNarrower, "j = ref i[1]").WithArguments("j", "i[1]").WithLocation(6, 9)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_Indexing_08() { // Based on RefLikeEscapeMixingIndexer3 var src = """ using System; public static class E { extension(S s) { public int this[Span<byte> span] { readonly get => 0; set {} } } } public ref struct S { static void M(S s) { Span<byte> span = stackalloc byte[10]; // `span` can't escape into `s` here because the get is readonly _ = s[span]; s[span] = 42; // 1 s[span]++; // 2 } } """; var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugDll, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (7,53): error CS0106: The modifier 'readonly' is not valid for this item // public int this[Span<byte> span] { readonly get => 0; set {} } Diagnostic(ErrorCode.ERR_BadMemberFlag, "get").WithArguments("readonly").WithLocation(7, 53)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_Indexing_09() { var src = """ public static class E { extension(object o) { public int this[int i] { get => 0; set {} } } } public ref struct S { object GetReceiver(System.Span<byte> span) => throw null; int GetIndex(System.Span<byte> span) => throw null; void M() { System.Span<byte> span = stackalloc byte[10]; System.Span<byte> span2 = stackalloc byte[10]; this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 1 _ = this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 2 this.GetReceiver(span)[this.GetIndex(span2)] += 42; // 3 this.GetReceiver(span)[this.GetIndex(span2)]++; // 4 } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (19,9): error CS8350: This combination of arguments to 'S.GetReceiver(Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 1 Diagnostic(ErrorCode.ERR_CallArgMixing, "this.GetReceiver(span)").WithArguments("S.GetReceiver(System.Span<byte>)", "span").WithLocation(19, 9), // (19,26): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(19, 26), // (19,32): error CS8350: This combination of arguments to 'S.GetIndex(Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 1 Diagnostic(ErrorCode.ERR_CallArgMixing, "this.GetIndex(span2)").WithArguments("S.GetIndex(System.Span<byte>)", "span").WithLocation(19, 32), // (19,46): error CS8352: Cannot use variable 'span2' in this context because it may expose referenced variables outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "span2").WithArguments("span2").WithLocation(19, 46), // (20,13): error CS8350: This combination of arguments to 'S.GetReceiver(Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // _ = this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 2 Diagnostic(ErrorCode.ERR_CallArgMixing, "this.GetReceiver(span)").WithArguments("S.GetReceiver(System.Span<byte>)", "span").WithLocation(20, 13), // (20,30): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // _ = this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 2 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(20, 30), // (20,36): error CS8350: This combination of arguments to 'S.GetIndex(Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // _ = this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 2 Diagnostic(ErrorCode.ERR_CallArgMixing, "this.GetIndex(span2)").WithArguments("S.GetIndex(System.Span<byte>)", "span").WithLocation(20, 36), // (20,50): error CS8352: Cannot use variable 'span2' in this context because it may expose referenced variables outside of their declaration scope // _ = this.GetReceiver(span)[this.GetIndex(span2)] = 42; // 2 Diagnostic(ErrorCode.ERR_EscapeVariable, "span2").WithArguments("span2").WithLocation(20, 50), // (21,9): error CS8350: This combination of arguments to 'S.GetReceiver(Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)] += 42; // 3 Diagnostic(ErrorCode.ERR_CallArgMixing, "this.GetReceiver(span)").WithArguments("S.GetReceiver(System.Span<byte>)", "span").WithLocation(21, 9), // (21,26): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)] += 42; // 3 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(21, 26), // (21,32): error CS8350: This combination of arguments to 'S.GetIndex(Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)] += 42; // 3 Diagnostic(ErrorCode.ERR_CallArgMixing, "this.GetIndex(span2)").WithArguments("S.GetIndex(System.Span<byte>)", "span").WithLocation(21, 32), // (21,46): error CS8352: Cannot use variable 'span2' in this context because it may expose referenced variables outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)] += 42; // 3 Diagnostic(ErrorCode.ERR_EscapeVariable, "span2").WithArguments("span2").WithLocation(21, 46), // (22,9): error CS8350: This combination of arguments to 'S.GetReceiver(Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)]++; // 4 Diagnostic(ErrorCode.ERR_CallArgMixing, "this.GetReceiver(span)").WithArguments("S.GetReceiver(System.Span<byte>)", "span").WithLocation(22, 9), // (22,26): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)]++; // 4 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(22, 26), // (22,32): error CS8350: This combination of arguments to 'S.GetIndex(Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)]++; // 4 Diagnostic(ErrorCode.ERR_CallArgMixing, "this.GetIndex(span2)").WithArguments("S.GetIndex(System.Span<byte>)", "span").WithLocation(22, 32), // (22,46): error CS8352: Cannot use variable 'span2' in this context because it may expose referenced variables outside of their declaration scope // this.GetReceiver(span)[this.GetIndex(span2)]++; // 4 Diagnostic(ErrorCode.ERR_EscapeVariable, "span2").WithArguments("span2").WithLocation(22, 46)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_Indexing_10() { var src = """ public static class E { extension(S s) { public int this[System.Span<byte> span] { get => throw null; set { } } } } public ref struct S { void M() { System.Span<byte> span = stackalloc byte[10]; this[span] = 42; E.set_Item(this, span, 42); _ = this[span]; E.get_Item(this, span); this[span] += 42; this[span]++; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_Indexing_11() { var src = """ public static class E { extension(ref S s) { public int this[System.Span<byte> span] { get => throw null; set { } } } } public ref struct S { void M() { System.Span<byte> span = stackalloc byte[10]; this[span] = 42; // 1 E.set_Item(ref this, span, 42); // 2 _ = this[span]; // 3 E.get_Item(ref this, span); // 4 this[span] += 42; // 5 this[span]++; // 6 } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (15,9): error CS8350: This combination of arguments to 'E.extension(ref S).this[Span<byte>]' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this[span] = 42; // 1 Diagnostic(ErrorCode.ERR_CallArgMixing, "this[span]").WithArguments("E.extension(ref S).this[System.Span<byte>]", "span").WithLocation(15, 9), // (15,14): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // this[span] = 42; // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(15, 14), // (16,9): error CS8350: This combination of arguments to 'E.set_Item(ref S, Span<byte>, int)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // E.set_Item(ref this, span, 42); // 2 Diagnostic(ErrorCode.ERR_CallArgMixing, "E.set_Item(ref this, span, 42)").WithArguments("E.set_Item(ref S, System.Span<byte>, int)", "span").WithLocation(16, 9), // (16,30): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // E.set_Item(ref this, span, 42); // 2 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(16, 30), // (18,13): error CS8350: This combination of arguments to 'E.extension(ref S).this[Span<byte>]' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // _ = this[span]; // 3 Diagnostic(ErrorCode.ERR_CallArgMixing, "this[span]").WithArguments("E.extension(ref S).this[System.Span<byte>]", "span").WithLocation(18, 13), // (18,18): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // _ = this[span]; // 3 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(18, 18), // (19,9): error CS8350: This combination of arguments to 'E.get_Item(ref S, Span<byte>)' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // E.get_Item(ref this, span); // 4 Diagnostic(ErrorCode.ERR_CallArgMixing, "E.get_Item(ref this, span)").WithArguments("E.get_Item(ref S, System.Span<byte>)", "span").WithLocation(19, 9), // (19,30): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // E.get_Item(ref this, span); // 4 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(19, 30), // (21,9): error CS8350: This combination of arguments to 'E.extension(ref S).this[Span<byte>]' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this[span] += 42; // 5 Diagnostic(ErrorCode.ERR_CallArgMixing, "this[span]").WithArguments("E.extension(ref S).this[System.Span<byte>]", "span").WithLocation(21, 9), // (21,9): error CS8350: This combination of arguments to 'E.extension(ref S).this[Span<byte>]' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this[span] += 42; // 5 Diagnostic(ErrorCode.ERR_CallArgMixing, "this[span] += 42").WithArguments("E.extension(ref S).this[System.Span<byte>]", "span").WithLocation(21, 9), // (21,14): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // this[span] += 42; // 5 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(21, 14), // (21,14): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // this[span] += 42; // 5 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(21, 14), // (22,9): error CS8350: This combination of arguments to 'E.extension(ref S).this[Span<byte>]' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this[span]++; // 6 Diagnostic(ErrorCode.ERR_CallArgMixing, "this[span]").WithArguments("E.extension(ref S).this[System.Span<byte>]", "span").WithLocation(22, 9), // (22,9): error CS8350: This combination of arguments to 'E.extension(ref S).this[Span<byte>]' is disallowed because it may expose variables referenced by parameter 'span' outside of their declaration scope // this[span]++; // 6 Diagnostic(ErrorCode.ERR_CallArgMixing, "this[span]").WithArguments("E.extension(ref S).this[System.Span<byte>]", "span").WithLocation(22, 9), // (22,14): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // this[span]++; // 6 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(22, 14), // (22,14): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // this[span]++; // 6 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(22, 14)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_ObjectCreation_01() { string source = """ class C { ref int M2() { int i = 0; return ref i[1]; } ref int M3() { int i = 0; return ref E.get_Item(ref i, 1); } } static class E { extension(ref int i) { public ref int this[int j] => ref i; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,20): error CS8168: Cannot return local 'i' by reference because it is not a ref local // return ref i[1]; Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(6, 20), // (6,20): error CS8347: Cannot use a result of 'E.extension(ref int).this[int]' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return ref i[1]; Diagnostic(ErrorCode.ERR_EscapeCall, "i[1]").WithArguments("E.extension(ref int).this[int]", "i").WithLocation(6, 20), // (12,20): error CS8347: Cannot use a result of 'E.get_Item(ref int, int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope // return ref E.get_Item(ref i, 1); Diagnostic(ErrorCode.ERR_EscapeCall, "E.get_Item(ref i, 1)").WithArguments("E.get_Item(ref int, int)", "i").WithLocation(12, 20), // (12,35): error CS8168: Cannot return local 'i' by reference because it is not a ref local // return ref E.get_Item(ref i, 1); Diagnostic(ErrorCode.ERR_RefReturnLocal, "i").WithArguments("i").WithLocation(12, 35)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_ObjectCreation_02() { string source = """ System.Span<byte> span = stackalloc byte[10]; _ = new S() { [span] = 42 }; E.set_Item(new S(), span, 42); ref struct S { } static class E { extension(S s) { public int this[System.Span<byte> s2] { get => throw null; set { } } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_ObjectCreation_03() { string source = """ System.Span<byte> span = stackalloc byte[10]; _ = new S() { [span] = 42 }; S s = new S(); E.set_Item(ref s, span, 42); // 1 ref struct S { } static class E { extension(ref S s) { public int this[System.Span<byte> s2] { get => throw null; set { } } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (5,1): error CS8350: This combination of arguments to 'E.set_Item(ref S, Span<byte>, int)' is disallowed because it may expose variables referenced by parameter 's2' outside of their declaration scope // E.set_Item(ref s, span, 42); // 1 Diagnostic(ErrorCode.ERR_CallArgMixing, "E.set_Item(ref s, span, 42)").WithArguments("E.set_Item(ref S, System.Span<byte>, int)", "s2").WithLocation(5, 1), // (5,19): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // E.set_Item(ref s, span, 42); // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(5, 19)); } [Fact] public void RefAnalysis_ObjectCreation_04() { string source = """ System.Span<int> span = stackalloc int[10]; _ = new S() { [span] = 42 }; System.Console.Write(span[0]); System.Span<int> span2 = stackalloc int[10]; S s = new S(); E.get_Item(s, span2) = 43; System.Console.Write(span2[0]); ref struct S { } static class E { extension(S s) { public ref int this[System.Span<int> s2] { get => ref s2[0]; } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("4243"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void RefAnalysis_ObjectCreation_05() { string source = """ _ = new S() { [GetIndex()] = GetValue() }; int GetIndex() { System.Console.Write("GetIndex "); return 0; } int GetValue() { System.Console.Write("GetValue"); return 0; } ref struct S { } static class E { public static int Field; extension(S s) { public ref int this[int i] { get { System.Console.Write("get "); return ref Field; } } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex get GetValue"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_ObjectCreation_06() { string source = """ System.Span<byte> span = stackalloc byte[10]; S s1 = new S() { [span] = 42 }; S s2 = new S(); E.get_Item(ref s2, span) = 42; // 1 ref struct S { } static class E { extension(ref S s) { public ref int this[System.Span<byte> s2] { get => throw null; } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (5,1): error CS8350: This combination of arguments to 'E.get_Item(ref S, Span<byte>)' is disallowed because it may expose variables referenced by parameter 's2' outside of their declaration scope // E.get_Item(ref s2, span) = 42; // 1 Diagnostic(ErrorCode.ERR_CallArgMixing, "E.get_Item(ref s2, span)").WithArguments("E.get_Item(ref S, System.Span<byte>)", "s2").WithLocation(5, 1), // (5,20): error CS8352: Cannot use variable 'span' in this context because it may expose referenced variables outside of their declaration scope // E.get_Item(ref s2, span) = 42; // 1 Diagnostic(ErrorCode.ERR_EscapeVariable, "span").WithArguments("span").WithLocation(5, 20)); } [Fact, CompilerTrait(CompilerFeature.RefLifetime)] public void RefAnalysis_ObjectCreation_07() { string source = """ System.Span<byte> span = stackalloc byte[10]; _ = new C() { [span] = 42 }; System.Console.Write(E.Field); C c = new C(); E.get_Item(c, span) = 43; System.Console.Write(E.Field); class C { } static class E { public static int Field; extension(C c) { public ref int this[System.Span<byte> s2] { get => ref Field; } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("4243"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void RefAnalysis_ObjectCreation_08() { string source = """ _ = new S() { [GetIndex()] = 1 }; int GetIndex() { System.Console.Write("GetIndex "); return 0; } struct S { } static class E { public static int Field; extension(S s) { public ref int this[int i] { get { System.Console.Write("get"); return ref Field; } } } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex get"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void SynthesizedAttributeOnParameters_In_01() { var src = """ class C { void M(in int i) { _ = i[0]; _ = E.get_Item(i, 0); _ = i[""]; _ = E.get_Item(ref i, ""); } } """; var libSrc = """ public static class E { extension(in int i) { public int this[int j] => 0; } extension(ref int i) { public int this[string s] => 0; } } """; DiagnosticDescription[] expected = [ // (8,13): error CS8329: Cannot use variable 'i' as a ref or out value because it is a readonly variable // _ = i[""]; Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "i").WithArguments("variable", "i").WithLocation(8, 13), // (9,28): error CS8329: Cannot use variable 'i' as a ref or out value because it is a readonly variable // _ = E.get_Item(ref i, ""); Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "i").WithArguments("variable", "i").WithLocation(9, 28) ]; var comp = CreateCompilation([src, libSrc]); comp.VerifyEmitDiagnostics(expected); var libComp = CreateCompilation(libSrc); CompileAndVerify(libComp, symbolValidator: validate); var comp2 = CreateCompilation(src, references: [libComp.EmitToImageReference()]); comp2.VerifyEmitDiagnostics(expected); static void validate(ModuleSymbol m) { var module = (PEModuleSymbol)m; var getters = m.GlobalNamespace.GetTypeMember("E").GetMembers("get_Item").OfType<MethodSymbol>().ToArray(); AssertEx.Equal("E.get_Item(in int, int)", getters[0].ToDisplayString()); var parameterSymbol = (PEParameterSymbol)getters[0].Parameters[0]; Assert.True(module.Module.HasIsReadOnlyAttribute(parameterSymbol.Handle)); AssertEx.Equal("E.get_Item(ref int, string)", getters[1].ToDisplayString()); parameterSymbol = (PEParameterSymbol)getters[1].Parameters[0]; Assert.False(module.Module.HasIsReadOnlyAttribute(parameterSymbol.Handle)); } } [Fact] public void IOperation_01() { // indexing var src = """ C c = new C(); /*<bind>*/ c[0] = 1; /*</bind>*/ public static class E { extension(C c) { public int this[int i] { get => 0; set { } } } } public class C { } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); string expectedOperationTree = """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'c[0] = 1') Left: IPropertyReferenceOperation: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i] { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'c[0]') Instance Receiver: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') """; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_02() { // list pattern, extension Index indexer var src = """ C c = new C(); /*<bind>*/ _ = c is [42]; /*</bind>*/ public static class E { extension(C c) { public int this[System.Index i] => 42; } } public class C { public int Length => 1; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); string expectedOperationTree = """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: '_ = c is [42]') Left: IDiscardOperation (Symbol: System.Boolean _) (OperationKind.Discard, Type: System.Boolean) (Syntax: '_') Right: IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'c is [42]') Value: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') Pattern: IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[42]') (InputType: C, NarrowedType: C, DeclaredSymbol: null, LengthSymbol: System.Int32 C.Length { get; }, IndexerSymbol: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Index i] { get; }) Patterns (1): IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '42') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') """; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_03() { // list pattern, extension Length, instance this[Index] var src = """ C c = new C(); /*<bind>*/ _ = c is [42]; /*</bind>*/ public static class E { extension(C c) { public int Length => 1; } } public class C { public int this[System.Index i] => 42; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); string expectedOperationTree = """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: '_ = c is [42]') Left: IDiscardOperation (Symbol: System.Boolean _) (OperationKind.Discard, Type: System.Boolean) (Syntax: '_') Right: IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'c is [42]') Value: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') Pattern: IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[42]') (InputType: C, NarrowedType: C, DeclaredSymbol: null, LengthSymbol: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.Length { get; }, IndexerSymbol: System.Int32 C.this[System.Index i] { get; }) Patterns (1): IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '42') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') """; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_04() { // slice pattern, extension Range indexer var src = """ C c = new C(); /*<bind>*/ _ = c is [_, .. 42]; /*</bind>*/ public static class E { extension(C c) { public int this[System.Range r] => 42; } } public class C { public int this[System.Index i] => 42; public int Length => 1; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); string expectedOperationTree = """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: '_ = c is [_, .. 42]') Left: IDiscardOperation (Symbol: System.Boolean _) (OperationKind.Discard, Type: System.Boolean) (Syntax: '_') Right: IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'c is [_, .. 42]') Value: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') Pattern: IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[_, .. 42]') (InputType: C, NarrowedType: C, DeclaredSymbol: null, LengthSymbol: System.Int32 C.Length { get; }, IndexerSymbol: System.Int32 C.this[System.Index i] { get; }) Patterns (2): IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. 42') (InputType: C, NarrowedType: C, SliceSymbol: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Range r] { get; } Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '42') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') """; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_05() { // object initializer var src = """ C c; /*<bind>*/ c = new C { [0] = 1 }; /*</bind>*/ public static class E { extension(C c) { public int this[int i] { set { } } } } public class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); string expectedOperationTree = """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C) (Syntax: 'c = new C { [0] = 1 }') Left: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C { [0] = 1 }') Arguments(0) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C) (Syntax: '{ [0] = 1 }') Initializers(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: '[0] = 1') Left: IPropertyReferenceOperation: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i] { set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: '[0]') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: '[0]') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') """; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_06() { // null-conditional indexing var src = """ C c = new C(); /*<bind>*/ c?[0] = 1; /*</bind>*/ public static class E { extension(C c) { public int this[int i] { set { } } } } public class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); string expectedOperationTree = """ IConditionalAccessOperation (OperationKind.ConditionalAccess, Type: System.Int32?) (Syntax: 'c?[0] = 1') Operation: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') WhenNotNull: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: '[0] = 1') Left: IPropertyReferenceOperation: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i] { set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: '[0]') Instance Receiver: IConditionalAccessInstanceOperation (OperationKind.ConditionalAccessInstance, Type: C, IsImplicit) (Syntax: 'c') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') """; VerifyOperationTreeAndDiagnosticsForTest<ConditionalAccessExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_07() { // null-conditional access var src = """ C c = new C(); /*<bind>*/ _ = c?[0] ?? -1; /*</bind>*/ public static class E { extension(C c) { public int this[int i] { get => 0; } } } public class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); string expectedOperationTree = """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: '_ = c?[0] ?? -1') Left: IDiscardOperation (Symbol: System.Int32 _) (OperationKind.Discard, Type: System.Int32) (Syntax: '_') Right: ICoalesceOperation (OperationKind.Coalesce, Type: System.Int32) (Syntax: 'c?[0] ?? -1') Expression: IConditionalAccessOperation (OperationKind.ConditionalAccess, Type: System.Int32?) (Syntax: 'c?[0]') Operation: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') WhenNotNull: IPropertyReferenceOperation: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i] { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: '[0]') Instance Receiver: IConditionalAccessInstanceOperation (OperationKind.ConditionalAccessInstance, Type: C, IsImplicit) (Syntax: 'c') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '0') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) ValueConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) WhenNull: IUnaryOperation (UnaryOperatorKind.Minus) (OperationKind.Unary, Type: System.Int32, Constant: -1) (Syntax: '-1') Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') """; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_08() { var src = """ C c = new C(); /*<bind>*/ c[42] += 100; /*</bind>*/ public static class E { extension(C c) { public ref D this[int i] { get => throw null; } } extension(D d) { public void operator +=(int i) => throw null; } } public class C { } public class D { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics(); string expectedOperationTree = """ ICompoundAssignmentOperation (BinaryOperatorKind.Add) (OperatorMethod: void E.<G>$AFA52AAB7DF1E8FB2EB2AED850FD4E4B.op_AdditionAssignment(System.Int32 i)) (OperationKind.CompoundAssignment, Type: System.Void) (Syntax: 'c[42] += 100') InConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Left: IPropertyReferenceOperation: ref D E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i] { get; } (OperationKind.PropertyReference, Type: D) (Syntax: 'c[42]') Instance Receiver: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '42') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 100) (Syntax: '100') """; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_09() { // implicit indexer with extension Length paired with instance this[int] var src = """ var c = new C(); /*<bind>*/ c[^2] = 10; /*</bind>*/ static class E { extension(C c) { public int Length => 3; } } class C { public int this[int i] { get { System.Console.Write($"instance.get({i}) "); return 42; } set { System.Console.Write($"instance.set({i}, {value}) "); } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("instance.set(1, 10) "), verify: Verification.FailsPEVerify).VerifyDiagnostics(); string expectedOperationTree = """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'c[^2] = 10') Left: IImplicitIndexerReferenceOperation (OperationKind.ImplicitIndexerReference, Type: System.Int32) (Syntax: 'c[^2]') Instance: ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') Argument: IUnaryOperation (UnaryOperatorKind.Hat) (OperationKind.Unary, Type: System.Index) (Syntax: '^2') Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') LengthSymbol: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.Length { get; } IndexerSymbol: System.Int32 C.this[System.Int32 i] { get; set; } Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') """; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, [], targetFramework: TargetFramework.Net100); } [Fact] public void IOperation_10() { // implicit indexer with extension Length but no this[int] var src = """ var c = new C(); /*<bind>*/ c[^2] = 10; /*</bind>*/ static class E { extension(C c) { public int Length => 3; } } class C { } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); string expectedOperationTree = """ ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: 'c[^2] = 10') Left: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'c[^2]') Children(2): IUnaryOperation (UnaryOperatorKind.Hat) (OperationKind.Unary, Type: System.Index, IsInvalid) (Syntax: '^2') Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2') ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C, IsInvalid) (Syntax: 'c') Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') """; DiagnosticDescription[] expected = [ // (3,1): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // c[^2] = 10; Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[^2]").WithArguments("C").WithLocation(3, 1) ]; VerifyOperationTreeAndDiagnosticsForTest<AssignmentExpressionSyntax>(src, expectedOperationTree, expected, targetFramework: TargetFramework.Net100); } [Fact] public void MissingMembers_01() { // missing DefaultMemberAttribute var src = """ public static class E { extension(object o) { public int this[int i] { get => 0; set { } } } } """; var comp = CreateCompilation(src); comp.MakeMemberMissing(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor); comp.VerifyEmitDiagnostics( // (5,20): error CS0656: Missing compiler required member 'System.Reflection.DefaultMemberAttribute..ctor' // public int this[int i] Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "this").WithArguments("System.Reflection.DefaultMemberAttribute", ".ctor").WithLocation(5, 20)); } [Fact] public void Nullability_ReceiverConversion_01() { var src = """ #nullable enable (object, object)? o = (new object(), new object()); _ = o[0]; o[0] = 42; static class E { extension((object?, object?)? o) { public int this[int i] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,5): warning CS8620: Argument of type '(object, object)?' cannot be used for parameter 'o' of type '(object?, object?)?' in 'E.extension((object?, object?)?)' due to differences in the nullability of reference types. // _ = o[0]; Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "o").WithArguments("(object, object)?", "(object?, object?)?", "o", "E.extension((object?, object?)?)").WithLocation(4, 5), // (5,1): warning CS8620: Argument of type '(object, object)?' cannot be used for parameter 'o' of type '(object?, object?)?' in 'E.extension((object?, object?)?)' due to differences in the nullability of reference types. // o[0] = 42; Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "o").WithArguments("(object, object)?", "(object?, object?)?", "o", "E.extension((object?, object?)?)").WithLocation(5, 1)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_02() { // initializer var src = """ #nullable enable _ = new System.Nullable<System.ValueTuple<object, object>>() { [0] = null }; public static class E { extension((object?, object?)? t) { public string? this[int i] { set { } } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,64): warning CS8620: Argument of type '(object, object)?' cannot be used for parameter 't' of type '(object?, object?)?' in 'E.extension((object?, object?)?)' due to differences in the nullability of reference types. // _ = new System.Nullable<System.ValueTuple<object, object>>() { [0] = null }; Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "[0]").WithArguments("(object, object)?", "(object?, object?)?", "t", "E.extension((object?, object?)?)").WithLocation(3, 64)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_03() { // initializer, target-typed var src = """ #nullable enable System.Nullable<System.ValueTuple<object, object>> t = new() { [0] = null }; public static class E { extension((object?, object?)? t) { public string? this[int i] { set { } } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,64): error CS0021: Cannot apply indexing with [] to an expression of type '(object, object)' // System.Nullable<System.ValueTuple<object, object>> t = new() { [0] = null }; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[0]").WithArguments("(object, object)").WithLocation(3, 64)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_04() { // initializer, target-typed var src = """ #nullable enable System.Nullable<System.ValueTuple<object, object>> t = new() { [0] = null }; public static class E { extension((object?, object?) t) { public string? this[int i] { set { } } } } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_05() { // nested initializer var src = """ #nullable enable _ = new System.Nullable<System.ValueTuple<object, object>>() { [0] = { F = "" } }; public static class E { extension((object?, object?)? t) { public C this[int i] { get => new C(); } } } public class C { public string? F; } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (3,64): warning CS8620: Argument of type '(object, object)?' cannot be used for parameter 't' of type '(object?, object?)?' in 'E.extension((object?, object?)?)' due to differences in the nullability of reference types. // _ = new System.Nullable<System.ValueTuple<object, object>>() { [0] = { F = "" } }; Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "[0]").WithArguments("(object, object)?", "(object?, object?)?", "t", "E.extension((object?, object?)?)").WithLocation(3, 64)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_06() { // list pattern var src = """ #nullable enable (object, object)? o = (new object(), new object()); _ = o is [var x]; static class E { extension((object?, object?)? o) { public int this[System.Index i] { get => throw null!; } } } namespace System { public struct ValueTuple<T1, T2> { public ValueTuple(T1 t1, T2 t2) { } public int Length => throw null!; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (4,10): error CS0021: Cannot apply indexing with [] to an expression of type '(object, object)' // _ = o is [var x]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var x]").WithArguments("(object, object)").WithLocation(4, 10)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_07() { // list pattern var src = """ #nullable enable (object, object)? o = (new object(), new object()); if (o is [var x]) { System.Console.Write(x); } static class E { extension((object?, object?) o) { public int this[System.Index i] { get { System.Console.Write($"{i} "); return 42; } } } extension((object, object) o) { public int Length { get { System.Console.Write("Length "); return 1; } } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Length 0 42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_08() { // 'with' expression var src = """ #nullable enable (object, object)? o = (new object(), new object()); _ = o with { [0] = 42 }; static class E { extension((object?, object?)? o) { public int this[int i] { set { } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,14): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // _ = o with { [0] = 42 }; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[0]").WithLocation(4, 14), // (4,14): error CS0747: Invalid initializer member declarator // _ = o with { [0] = 42 }; Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, "[0] = 42").WithLocation(4, 14)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_09() { // increment operator var src = """ #nullable enable (object, object)? o = (new object(), new object()); o++; static class E { extension((object?, object?)?) { public static (object?, object?)? operator++((object?, object?)? t) => t; } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,1): warning CS8619: Nullability of reference types in value of type '(object?, object?)?' doesn't match target type '(object, object)?'. // o++; Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "o++").WithArguments("(object?, object?)?", "(object, object)?").WithLocation(4, 1)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81851")] public void Nullability_ReceiverConversion_10() { // compound assignment var src = """ #nullable enable (object, object)? o = (new object(), new object()); o.P++; static class E { extension((object?, object?)? o) { public int P { get => throw null!; set { } } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,1): warning CS8620: Argument of type '(object, object)?' cannot be used for parameter 'o' of type '(object?, object?)?' in 'E.extension((object?, object?)?)' due to differences in the nullability of reference types. // o.P++; Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "o").WithArguments("(object, object)?", "(object?, object?)?", "o", "E.extension((object?, object?)?)").WithLocation(4, 1)); } [Fact] public void Nullability_Setter_01() { // generic extension parameter, property write access, notnull constraint var src = """ #nullable enable object? oNull = null; oNull[0] = null; // 1 object? oNull2 = null; oNull2[0] = new object(); // 2 object? oNotNull = new object(); oNotNull[0] = null; // 3 E.set_Item(oNotNull, 0, null); // 4 oNotNull[0] = new object(); oNotNull?[0] = null; // 5 static class E { extension<T>(T t) where T : notnull { public T this[int i] { set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,1): warning CS8714: The type 'object?' cannot be used as type parameter 'T' in the generic type or method 'E.extension<T>(T)'. Nullability of type argument 'object?' doesn't match 'notnull' constraint. // oNull[0] = null; // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "oNull[0]").WithArguments("E.extension<T>(T)", "T", "object?").WithLocation(4, 1), // (7,1): warning CS8714: The type 'object?' cannot be used as type parameter 'T' in the generic type or method 'E.extension<T>(T)'. Nullability of type argument 'object?' doesn't match 'notnull' constraint. // oNull2[0] = new object(); // 2 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "oNull2[0]").WithArguments("E.extension<T>(T)", "T", "object?").WithLocation(7, 1), // (10,15): warning CS8625: Cannot convert null literal to non-nullable reference type. // oNotNull[0] = null; // 3 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 15), // (11,1): warning CS8714: The type 'object?' cannot be used as type parameter 'T' in the generic type or method 'E.set_Item<T>(T, int, T)'. Nullability of type argument 'object?' doesn't match 'notnull' constraint. // E.set_Item(oNotNull, 0, null); // 4 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "E.set_Item").WithArguments("E.set_Item<T>(T, int, T)", "T", "object?").WithLocation(11, 1), // (15,16): warning CS8625: Cannot convert null literal to non-nullable reference type. // oNotNull?[0] = null; // 5 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(15, 16)); } [Fact] public void Nullability_Setter_02() { // generic extension parameter, property write access var src = """ #nullable enable object? oNull = null; oNull[0] = null; object? oNull2 = null; oNull2[0] = new object(); object? oNotNull = new object(); oNotNull[0] = null; // 1 E.set_Item(oNotNull, 0, null); oNotNull[0] = new object(); oNotNull?[0] = null; // 2 static class E { extension<T>(T t) { public T this[int i] { set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (10,15): warning CS8625: Cannot convert null literal to non-nullable reference type. // oNotNull[0] = null; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 15), // (15,16): warning CS8625: Cannot convert null literal to non-nullable reference type. // oNotNull?[0] = null; // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(15, 16)); } [Fact] public void Nullability_Setter_03() { // property write access, warning in receiver var src = """ #nullable enable object? oNull = null; M(oNull)[0] = null; // 1, 2 object? oNull2 = null; M(oNull2)[0] = new object(); // 3 object M(object o) => throw null!; static class E { extension<T>(T t) { public T this[int i] { set => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (4,3): warning CS8604: Possible null reference argument for parameter 'o' in 'object M(object o)'. // M(oNull)[0] = null; // 1, 2 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "oNull").WithArguments("o", "object M(object o)").WithLocation(4, 3), // (4,15): warning CS8625: Cannot convert null literal to non-nullable reference type. // M(oNull)[0] = null; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 15), // (7,3): warning CS8604: Possible null reference argument for parameter 'o' in 'object M(object o)'. // M(oNull2)[0] = new object(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "oNull2").WithArguments("o", "object M(object o)").WithLocation(7, 3)); } [Fact] public void Nullability_Setter_04() { // generic extension parameter, check result of assignment var src = """ #nullable enable object? oNull = null; (oNull[0] = null).ToString(); // 1, 2 object? oNull2 = null; (oNull2[0] = new object()).ToString(); object? oNotNull = new object(); (oNotNull[0] = null).ToString(); // 2 (oNotNull[0] = new object()).ToString(); static class E { extension<T>(T t) { public T this[int i] { set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,2): warning CS8602: Dereference of a possibly null reference. // (oNull[0] = null).ToString(); // 1, 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNull[0] = null").WithLocation(4, 2), // (10,2): warning CS8602: Dereference of a possibly null reference. // (oNotNull[0] = null).ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNotNull[0] = null").WithLocation(10, 2), // (10,16): warning CS8625: Cannot convert null literal to non-nullable reference type. // (oNotNull[0] = null).ToString(); // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 16)); } [Fact] public void Nullability_Setter_05() { // variance in receiver var src = """ #nullable enable I<object?> iNull = null!; iNull[0] = 42; _ = iNull[0]; I<object> iNotNull = null!; iNotNull[0] = 42; _ = iNotNull[0]; static class E { extension(I<object?> t) { public int this[int i] { get => throw null!; set => throw null!; } } } interface I<out T> { } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics(); } [Fact] public void Nullability_Setter_06() { // variance in receiver var src = """ #nullable enable I<object?> iNull = null!; iNull[0] = 42; _ = iNull[0]; I<object> iNotNull = null!; iNotNull[0] = 42; _ = iNotNull[0]; static class E { extension(I<object> t) { public int this[int i] { get => throw null!; set => throw null!; } } } interface I<out T> { } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (4,1): warning CS8620: Argument of type 'I<object?>' cannot be used for parameter 't' of type 'I<object>' in 'E.extension(I<object>)' due to differences in the nullability of reference types. // iNull[0] = 42; Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "iNull").WithArguments("I<object?>", "I<object>", "t", "E.extension(I<object>)").WithLocation(4, 1), // (5,5): warning CS8620: Argument of type 'I<object?>' cannot be used for parameter 't' of type 'I<object>' in 'E.extension(I<object>)' due to differences in the nullability of reference types. // _ = iNull[0]; Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "iNull").WithArguments("I<object?>", "I<object>", "t", "E.extension(I<object>)").WithLocation(5, 5)); } [Fact] public void Nullability_Setter_08() { // ref-returning property var src = """ #nullable enable object? oNull3 = null; object? oNull = null; oNull[0] = oNull3; object? oNull2 = null; object? oNotNull = new object(); oNull2[0] = oNotNull; oNotNull[0] = oNull3; // 1 oNotNull[0] = oNotNull; static class E { extension<T>(T t) { public ref T this[int i] { get => throw null!; } } } """; var comp = CreateCompilation(src); comp.VerifyEmitDiagnostics( // (11,15): warning CS8601: Possible null reference assignment. // oNotNull[0] = oNull3; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNull3").WithLocation(11, 15)); } [Fact] public void Nullability_Setter_09() { // indexer's state var src = """ #nullable enable object o = new object(); o[0] = null; o[0].ToString(); // 1 o[0] = new object(); o[0].ToString(); // 2 static class E { extension(object o) { public object? this[int i] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,1): warning CS8602: Dereference of a possibly null reference. // o[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o[0]").WithLocation(5, 1), // (8,1): warning CS8602: Dereference of a possibly null reference. // o[0].ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o[0]").WithLocation(8, 1)); } [Fact] public void Nullability_Setter_10() { // assignment resulting in maybe-null converted value var src = """ #nullable enable (new object()[0] = new D()).ToString(); // 1 static class E { extension(object o) { public C? this[int i] { get => throw null!; set => throw null!; } } } class D { public static implicit operator C?(D d) => throw null!; } class C { } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,2): warning CS8602: Dereference of a possibly null reference. // (new object()[0] = new D()).ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "new object()[0] = new D()").WithLocation(3, 2)); } [Fact] public void Nullability_Setter_11() { // assignment resulting in not-null converted value var src = """ #nullable enable D? dNull = null; (new object()[0] = dNull).ToString(); static class E { extension(object o) { public C? this[int i] { get => throw null!; set => throw null!; } } } class D { public static implicit operator C(D? d) => throw null!; } class C { } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Setter_13() { // compound assignment var src = """ #nullable enable object? oNull = null; oNull[0] += (object?)null; object? oNull2 = null; oNull2[0] += new object(); object? oNotNull = new object(); oNotNull[0] += (object?)null; // 1 oNotNull[0] += new object(); static class E { extension<T>(T t) { public T this[int i] { get => throw null!; set => throw null!; } public static T operator +(T t1, T t2) => throw null!; } } """; var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (10,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += (object?)null; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += (object?)null").WithLocation(10, 1)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var elementAccess = GetSyntaxes<ElementAccessExpressionSyntax>(tree, "oNull[0]").First(); AssertEx.Equal("System.Object? E.extension<System.Object?>(System.Object?).this[System.Int32 i] { get; set; }", model.GetSymbolInfo(elementAccess).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_Setter_14() { // compound assignment var src = """ #nullable enable object? oNull = null; oNull[0] += (object?)null; // 1 object? oNull2 = null; oNull2[0] += new object(); // 2 object? oNotNull = new object(); oNotNull[0] += (object?)null; // 3 oNotNull[0] += new object(); static class E { extension<T>(T t) { [property: System.Diagnostics.CodeAnalysis.DisallowNull] public T this[int i] { get => throw null!; set => throw null!; } public static T operator +(T t1, T t2) => throw null!; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (4,1): warning CS8601: Possible null reference assignment. // oNull[0] += (object?)null; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNull[0] += (object?)null").WithLocation(4, 1), // (7,1): warning CS8601: Possible null reference assignment. // oNull2[0] += new object(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNull2[0] += new object()").WithLocation(7, 1), // (10,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += (object?)null; // 3 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += (object?)null").WithLocation(10, 1)); } [Fact] public void Nullability_Setter_15() { // + returns nullable reference type var src = """ #nullable enable C? oNull = null; oNull[0] += null; C? oNull2 = null; oNull2[0] += new C(); C? oNotNull = new C(); oNotNull[0] += null; // 1 var x = E.get_Item(oNotNull, 0); x += null; E.set_Item(oNotNull, 0, x); oNotNull[0] += new C(); // 2 static class E { extension<T>(T t) { public T this[int i] { get => throw null!; set => throw null!; } } } class C { public static C? operator +(C? c1, C? c2) => throw null!; } """; var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (10,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += null; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += null").WithLocation(10, 1), // (16,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += new C(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += new C()").WithLocation(16, 1)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var elementAccess = GetSyntaxes<ElementAccessExpressionSyntax>(tree, "oNull[0]").First(); AssertEx.Equal("C? E.extension<C?>(C?).this[System.Int32 i] { get; set; }", model.GetSymbolInfo(elementAccess).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void Nullability_Setter_16() { // + returns non-nullable reference type var src = """ #nullable enable C? oNull = null; oNull[0] += null; C? oNull2 = null; oNull2[0] += new C(); C? oNotNull = new C(); oNotNull[0] += null; oNotNull[0] += new C(); static class E { extension<T>(T t) { public T this[int i] { get => throw null!; set => throw null!; } } } class C { public static C operator +(C? c1, C? c2) => throw null!; } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Setter_17() { // compound assignment, + returns nullable reference type, ref-returning property var src = """ #nullable enable C? oNull = null; oNull[0] += null; C? oNull2 = null; oNull2[0] += new C(); C? oNotNull = new C(); oNotNull[0] += null; oNotNull[0] += new C(); static class E { extension<T>(T t) { public ref T this[int i] { get => throw null!; } } } class C { public static C? operator +(C? c1, C? c2) => throw null!; } """; CreateCompilation(src).VerifyEmitDiagnostics( // (10,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += null; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += null").WithLocation(10, 1), // (11,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += new C(); Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += new C()").WithLocation(11, 1)); } [Fact] public void Nullability_Setter_18() { // compound assignment, + returns non-nullable reference type, ref-returning property var src = """ #nullable enable C? oNull = null; oNull[0] += null; C? oNull2 = null; oNull2[0] += new C(); C? oNotNull = new C(); oNotNull[0] += null; oNotNull[0] += new C(); static class E { extension<T>(T t) { public ref T this[int i] { get => throw null!; } } } class C { public static C operator +(C? c1, C? c2) => throw null!; } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Setter_19() { // compound assignment, + returns nullable reference type, check result value var src = """ #nullable enable C? oNull = null; (oNull[0] += null).ToString(); // 1 C? oNull2 = null; (oNull2[0] += new C()).ToString(); // 2 C? oNotNull = new C(); (oNotNull[0] += null).ToString(); // 3, 4 (oNotNull[0] += new C()).ToString(); // 5, 6 static class E { extension<T>(T t) { public T this[int i] { get => throw null!; set => throw null!; } } } class C { public static C? operator +(C? c1, C? c2) => throw null!; } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,2): warning CS8602: Dereference of a possibly null reference. // (oNull[0] += null).ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNull[0] += null").WithLocation(4, 2), // (7,2): warning CS8602: Dereference of a possibly null reference. // (oNull2[0] += new C()).ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNull2[0] += new C()").WithLocation(7, 2), // (10,2): warning CS8601: Possible null reference assignment. // (oNotNull[0] += null).ToString(); // 3, 4 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += null").WithLocation(10, 2), // (10,2): warning CS8602: Dereference of a possibly null reference. // (oNotNull[0] += null).ToString(); // 3, 4 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNotNull[0] += null").WithLocation(10, 2), // (12,2): warning CS8601: Possible null reference assignment. // (oNotNull[0] += new C()).ToString(); // 5, 6 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += new C()").WithLocation(12, 2), // (12,2): warning CS8602: Dereference of a possibly null reference. // (oNotNull[0] += new C()).ToString(); // 5, 6 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "oNotNull[0] += new C()").WithLocation(12, 2)); } [Fact] public void Nullability_Setter_20() { // compound assignment, + returns non-nullable reference type, check result value var src = """ #nullable enable C? oNull = null; (oNull[0] += null).ToString(); C? oNull2 = null; (oNull2[0] += new C()).ToString(); C? oNotNull = new C(); (oNotNull[0] += null).ToString(); (oNotNull[0] += new C()).ToString(); static class E { extension<T>(T t) { public T this[int i] { get => throw null!; set => throw null!; } } } class C { public static C operator +(C? c1, C? c2) => throw null!; } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Setter_21() { // compound assignment, warning in receiver var src = """ #nullable enable M(null)[0] += 0; object M(object o) => throw null!; static class E { extension(object o) { public int this[int i] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,3): warning CS8625: Cannot convert null literal to non-nullable reference type. // M(null)[0] += 0; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 3)); } [Fact] public void Nullability_Setter_22() { // compound assignment, warning in RHS var src = """ #nullable enable new object()[0] += M(null); int M(object o) => throw null!; static class E { extension(object o) { public int this[int i] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,22): warning CS8625: Cannot convert null literal to non-nullable reference type. // new object()[0] += M(null); Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 22)); } [Fact] public void Nullability_Setter_23() { // compound assignment, warning in indexer argument var src = """ #nullable enable new object()[M(null)] += 0; int M(object o) => throw null!; static class E { extension(object o) { public int this[int i] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,16): warning CS8625: Cannot convert null literal to non-nullable reference type. // new object()[M(null)] += 0; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 16)); } [Fact] public void Nullability_Setter_24() { // compound assignment resulting in maybe-null converted value var src = """ #nullable enable (new object()[0] += new D()).ToString(); // 1 static class E { extension(object o) { public C? this[int i] { get => throw null!; set => throw null!; } } } class D { } class C { public static C? operator+(C? c1, D d2) => throw null!; } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,2): warning CS8602: Dereference of a possibly null reference. // (new object()[0] += new D()).ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "new object()[0] += new D()").WithLocation(3, 2)); } [Fact] public void Nullability_Setter_25() { // compound assignment resulting in not-null converted value var src = """ #nullable enable (new object()[0] += new D()).ToString(); static class E { extension(object o) { public C? this[int i] { get => throw null!; set => throw null!; } } } class D { } class C { public static C operator+(C? c1, D d2) => throw null!; } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Setter_26() { // compound assignment, + returns nullable reference type, ref-returning property, with suppression var src = """ #nullable enable C? oNotNull = new C(); oNotNull[0] += null!; oNotNull[0] += new C()!; static class E { extension<T>(T t) { public ref T this[int i] { get => throw null!; } } } class C { public static C? operator +(C? c1, C? c2) => throw null!; } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += null!; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += null!").WithLocation(4, 1), // (5,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += new C()!; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += new C()!").WithLocation(5, 1)); src = """ #nullable enable C? oNotNull = new C(); oNotNull[0] += null!; oNotNull[0] += new C()!; class C { public static C? operator +(C? c1, C? c2) => throw null!; public ref C this[int i] { get => throw null!; } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += null!; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += null!").WithLocation(4, 1), // (5,1): warning CS8601: Possible null reference assignment. // oNotNull[0] += new C()!; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "oNotNull[0] += new C()!").WithLocation(5, 1)); } [Fact] public void Nullability_Params_01() { // in indexing, null disallowed var src = """ #nullable enable var o = new object(); _ = o[42, null, new object(), null]; // 1, 2 o[42, null, new object(), null] = 0; // 3, 4 o?[42, null, new object(), null] = 0; // 5, 6 public static class E { extension(object o) { public int this[int a, params object[] b] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,11): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = o[42, null, new object(), null]; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 11), // (4,31): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = o[42, null, new object(), null]; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 31), // (6,7): warning CS8625: Cannot convert null literal to non-nullable reference type. // o[42, null, new object(), null] = 0; // 3, 4 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(6, 7), // (6,27): warning CS8625: Cannot convert null literal to non-nullable reference type. // o[42, null, new object(), null] = 0; // 3, 4 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(6, 27), // (8,8): warning CS8625: Cannot convert null literal to non-nullable reference type. // o?[42, null, new object(), null] = 0; // 5, 6 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 8), // (8,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // o?[42, null, new object(), null] = 0; // 5, 6 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 28)); } [Fact] public void Nullability_Params_02() { // in indexing, null allowed var src = """ #nullable enable var o = new object(); _ = o[42, null, new object(), null]; o[42, null, new object(), null] = 0; o?[42, null, new object(), null] = 0; public static class E { extension(object o) { public int this[int a, params object?[] b] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Params_03() { // in indexing, generic var src = """ #nullable enable var o = new object(); _ = o[42, null, new object(), null]; o[42, null, new object(), null] = 0; o?[42, null, new object(), null] = 0; public static class E { extension<T>(object o) { public int this[int a, params T[] b] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Params_04() { // in indexing, params generic var src = """ #nullable enable var o = new object(); o[42, new object()] = null; // 1 o[42, new object()].ToString(); public static class E { extension<T>(object o) { public T this[int a, params T[] b] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (4,23): warning CS8625: Cannot convert null literal to non-nullable reference type. // o[42, new object()] = null; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 23)); } [Fact] public void Nullability_Params_05() { // in indexing, params and return generic var src = """ #nullable enable var o = new object(); o[42, new object(), null] = null; o[42, new object(), null].ToString(); // 1 public static class E { extension<T>(object o) { public T this[int a, params T[] b] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,1): warning CS8602: Dereference of a possibly null reference. // o[42, new object(), null].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o[42, new object(), null]").WithLocation(5, 1)); } [Fact] public void Nullability_Params_06() { // in object initializer, null disallowed var src = """ #nullable enable _ = new object() { [42, null, new object(), null] = 0 }; // 1, 2 public static class E { extension(object o) { public int this[int a, params object[] b] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,25): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new object() { [42, null, new object(), null] = 0 }; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 25), // (3,45): warning CS8625: Cannot convert null literal to non-nullable reference type. // _ = new object() { [42, null, new object(), null] = 0 }; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 45)); } [Fact] public void Nullability_Params_07() { // in object initializer, null allowed var src = """ #nullable enable _ = new object() { [42, null, new object(), null] = 0 }; public static class E { extension(object o) { public int this[int a, params object?[] b] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Params_08() { // in compound assignment, null disallowed var src = """ #nullable enable new object()[0, null, new object(), null] += 0; // 1, 2 static class E { extension(object o1) { public int this[int i, params object[] o2] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,17): warning CS8625: Cannot convert null literal to non-nullable reference type. // new object()[0, null, new object(), null] += 0; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 17), // (3,37): warning CS8625: Cannot convert null literal to non-nullable reference type. // new object()[0, null, new object(), null] += 0; // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 37)); } [Fact] public void Nullability_Params_09() { // in compound assignment, null allowed var src = """ #nullable enable new object()[0, null, new object(), null] += 0; static class E { extension(object o1) { public int this[int i, params object?[] o2] { get => throw null!; set => throw null!; } } } """; CreateCompilation(src).VerifyEmitDiagnostics(); } [Fact] public void Nullability_Params_10() { // disambiguation invocation var src = """ #nullable enable var o = new object(); E.get_Item(o, 42, null, new object(), null); // 1, 2 E.set_Item(o, 42, null, new object(), null, 0); // 3 public static class E { extension(object o) { public int this[int a, params object[] b] { get => throw null!; set => throw null!; } } } """; var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (4,19): warning CS8625: Cannot convert null literal to non-nullable reference type. // E.get_Item(o, 42, null, new object(), null); // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 19), // (4,39): warning CS8625: Cannot convert null literal to non-nullable reference type. // E.get_Item(o, 42, null, new object(), null); // 1, 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 39), // (6,3): error CS1501: No overload for method 'set_Item' takes 6 arguments // E.set_Item(o, 42, null, new object(), null, 0); // 3 Diagnostic(ErrorCode.ERR_BadArgCount, "set_Item").WithArguments("set_Item", "6").WithLocation(6, 3)); var bParameter = comp.GlobalNamespace.GetTypeMember("E").GetMethod("set_Item").Parameters[2]; Assert.Equal("b", bParameter.Name); Assert.True(bParameter.IsParams); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81953")] public void DynamicIndexerAccess_11() { var src = """ #nullable enable class C { public dynamic D = null!; static void M() { var c = new C { D = { [0] = M3(null), [M2(null)] = 0 } }; } static int M2(object o) => 0; static int M3(object o) => 0; } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (14,21): warning CS8625: Cannot convert null literal to non-nullable reference type. // [M2(null)] = 0 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(14, 21)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81953")] public void DynamicIndexerAccess_12() { var src = """ #nullable enable class C { public dynamic D = null!; static void M() { C c = new() { D = { [0] = M3(null), [M2(null)] = 0 } }; } static int M2(object o) => 0; static int M3(object o) => 0; } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (14,21): warning CS8625: Cannot convert null literal to non-nullable reference type. // [M2(null)] = 0 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(14, 21)); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_WithOtherParameters(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, C s1, string s2) { System.Console.Write(s1.value); System.Console.Write(s2); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(C s1) { public int this[string s2, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s1", "s2")] InterpolationHandler h] { get => 0; } } } public class C(string s) { public string value = s; } """; var exeSource = """ _ = new C("1")["2", $""]; _ = E.get_Item(new C("3"), "4", $""); """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "1234" : null; CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) .VerifyDiagnostics(); var comp = (CSharpCompilation)verifier.Compilation; var tree = comp.SyntaxTrees[0]; var compRoot = tree.GetCompilationUnitRoot(); var model = comp.GetSemanticModel(tree); var opRoot = model.GetOperation(compRoot); VerifyOperationTree(comp, opRoot, expectedOperationTree: """ IMethodBodyOperation (OperationKind.MethodBody, Type: null) (Syntax: '_ = new C(" ... "4", $"");') BlockBody: IBlockOperation (2 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: '_ = new C(" ... "4", $"");') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = new C(" ... ["2", $""];') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: '_ = new C("1")["2", $""]') Left: IDiscardOperation (Symbol: System.Int32 _) (OperationKind.Discard, Type: System.Int32) (Syntax: '_') Right: IPropertyReferenceOperation: System.Int32 E.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.String s2, InterpolationHandler h] { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'new C("1")["2", $""]') Instance Receiver: IObjectCreationOperation (Constructor: C..ctor(System.String s)) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C("1")') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s) (OperationKind.Argument, Type: null) (Syntax: '"1"') ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "1") (Syntax: '"1"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s2) (OperationKind.Argument, Type: null) (Syntax: '"2"') ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "2") (Syntax: '"2"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: h) (OperationKind.Argument, Type: null) (Syntax: '$""') IInterpolatedStringHandlerCreationOperation (HandlerAppendCallsReturnBool: False, HandlerCreationHasSuccessParameter: False) (OperationKind.InterpolatedStringHandlerCreation, Type: InterpolationHandler, IsImplicit) (Syntax: '$""') Creation: IObjectCreationOperation (Constructor: InterpolationHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount, C s1, System.String s2)) (OperationKind.ObjectCreation, Type: InterpolationHandler, IsImplicit) (Syntax: '$""') Arguments(4): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: literalLength) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$""') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$""') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: formattedCount) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$""') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$""') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s1) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'new C("1")') IInterpolatedStringHandlerArgumentPlaceholderOperation (CallsiteReceiver) (OperationKind.InterpolatedStringHandlerArgumentPlaceholder, Type: null, IsImplicit) (Syntax: 'new C("1")') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s2) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '"2"') IInterpolatedStringHandlerArgumentPlaceholderOperation (ArgumentIndex: 0) (OperationKind.InterpolatedStringHandlerArgumentPlaceholder, Type: null, IsImplicit) (Syntax: '"2"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null Content: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: "") (Syntax: '$""') Parts(0) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = E.get_I ... "4", $"");') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: '_ = E.get_I ... , "4", $"")') Left: IDiscardOperation (Symbol: System.Int32 _) (OperationKind.Discard, Type: System.Int32) (Syntax: '_') Right: IInvocationOperation (System.Int32 E.get_Item(C s1, System.String s2, InterpolationHandler h)) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'E.get_Item( ... , "4", $"")') Instance Receiver: null Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s1) (OperationKind.Argument, Type: null) (Syntax: 'new C("3")') IObjectCreationOperation (Constructor: C..ctor(System.String s)) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C("3")') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s) (OperationKind.Argument, Type: null) (Syntax: '"3"') ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "3") (Syntax: '"3"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s2) (OperationKind.Argument, Type: null) (Syntax: '"4"') ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "4") (Syntax: '"4"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: h) (OperationKind.Argument, Type: null) (Syntax: '$""') IInterpolatedStringHandlerCreationOperation (HandlerAppendCallsReturnBool: False, HandlerCreationHasSuccessParameter: False) (OperationKind.InterpolatedStringHandlerCreation, Type: InterpolationHandler, IsImplicit) (Syntax: '$""') Creation: IObjectCreationOperation (Constructor: InterpolationHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount, C s1, System.String s2)) (OperationKind.ObjectCreation, Type: InterpolationHandler, IsImplicit) (Syntax: '$""') Arguments(4): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: literalLength) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$""') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$""') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: formattedCount) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$""') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$""') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s1) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'new C("3")') IInterpolatedStringHandlerArgumentPlaceholderOperation (ArgumentIndex: 0) (OperationKind.InterpolatedStringHandlerArgumentPlaceholder, Type: null, IsImplicit) (Syntax: 'new C("3")') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: s2) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '"4"') IInterpolatedStringHandlerArgumentPlaceholderOperation (ArgumentIndex: 1) (OperationKind.InterpolatedStringHandlerArgumentPlaceholder, Type: null, IsImplicit) (Syntax: '"4"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null Content: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: "") (Syntax: '$""') Parts(0) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) ExpressionBody: null """); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_WithConversion(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, object o) { System.Console.Write(o); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(object o) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("o")] InterpolationHandler h] { get => 0; } } } public class C(string s) { public string value = s; public override string ToString() => value; } """; // The receiver is a C, so there's a BoundConversion to object as the receiver of the extension indexer var exeSource = """ _ = new C("1")[$""]; _ = E.get_Item(new C("2"), $""); """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "12" : null; var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 53 (0x35) .maxstack 4 .locals init (object V_0) IL_0000: ldstr "1" IL_0005: newobj "C..ctor(string)" IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: newobj "InterpolationHandler..ctor(int, int, object)" IL_0014: call "int E.get_Item(object, InterpolationHandler)" IL_0019: pop IL_001a: ldstr "2" IL_001f: newobj "C..ctor(string)" IL_0024: stloc.0 IL_0025: ldloc.0 IL_0026: ldc.i4.0 IL_0027: ldc.i4.0 IL_0028: ldloc.0 IL_0029: newobj "InterpolationHandler..ctor(int, int, object)" IL_002e: call "int E.get_Item(object, InterpolationHandler)" IL_0033: pop IL_0034: ret } """); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_WithConversion_ExtensionParameterNarrowerThanConstructor(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, object o) { System.Console.Write(o); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(C o) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("o")] InterpolationHandler h] { get => 0; } } } public class C(string s) { public string value = s; public override string ToString() => value; } """; var exeSource = """ _ = new C("1")[$""]; _ = E.get_Item(new ("2"), $""); """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "12" : null; CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_WithConversion_ExtensionParameterWiderThanConstructor(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, C s) { System.Console.WriteLine(s); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(object o) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("o")] InterpolationHandler h] { get => 0; } } } public class C(string s) { public string value = s; public override string ToString() => value; } """; var exeSource = """ _ = new C("1")[$""]; _ = E.get_Item(new C("2"), $""); """; var expectedDiagnostics = new[] { // (1,5): error CS1503: Argument 3: cannot convert from 'object' to 'C' // _ = new C("1")[$""]; Diagnostic(ErrorCode.ERR_BadArgType, @"new C(""1"")").WithArguments("3", "object", "C").WithLocation(1, 5), // (2,16): error CS1503: Argument 3: cannot convert from 'object' to 'C' // _ = E.get_Item(new C("2"), $""); Diagnostic(ErrorCode.ERR_BadArgType, @"new C(""2"")").WithArguments("3", "object", "C").WithLocation(2, 16) }; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostics); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CreateCompilation(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByRef(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, ref int i) { System.Console.Write(i); i = 2; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(ref int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h] { get { System.Console.Write(i); i = 3; return 0; } } } } """; var exeSource = """ class Program { static void Main() { int i = 1; Test1(ref i); System.Console.Write(i); i = 4; Test2(ref i); System.Console.Write(i); } static void Test1(ref int i) { _ = i[$""]; } static void Test2(ref int i) { E.get_Item(ref i, $""); } } """; var expectedOutput = "123423"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 18 (0x12) .maxstack 4 .locals init (int& V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: newobj "InterpolationHandler..ctor(int, int, ref int)" IL_000b: call "int E.get_Item(ref int, InterpolationHandler)" IL_0010: pop IL_0011: ret } """); verifier.VerifyIL("Program.Test2", """ { // Code size 18 (0x12) .maxstack 4 .locals init (int& V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: newobj "InterpolationHandler..ctor(int, int, ref int)" IL_000b: call "int E.get_Item(ref int, InterpolationHandler)" IL_0010: pop IL_0011: ret } """); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], expectedOutput: expectedOutput) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByRef_WithConstantReceiver(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, ref int i) { System.Console.Write(i); System.Runtime.CompilerServices.Unsafe.AsRef(in i)++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(ref int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h] { get { System.Console.Write(i); return 0; } } } } """; var exeSource = """ _ = 1[$""]; E.get_Item(ref 3, $""); """; var expectedDiagnostic = new DiagnosticDescription[] { // (1,5): error CS1510: A ref or out value must be an assignable variable // _ = 1[$""]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "1").WithLocation(1, 5), // (2,16): error CS1510: A ref or out value must be an assignable variable // E.get_Item(ref 3, $""); Diagnostic(ErrorCode.ERR_RefLvalueExpected, "3").WithLocation(2, 16) }; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostic); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CreateCompilation(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostic); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_Generic_ByRef(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler<TR> { public InterpolationHandler(int literalLength, int formattedCount, ref TR i) { System.Console.Write(i); i = (TR)(object)2; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension<T>(ref T i) where T : struct { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler<T> h] { get { System.Console.Write(i); i = (T)(object)3; return 0; } } } } """; var exeSource = """ class Program { static void Main() { int i = 1; Test1(ref i); System.Console.Write(i); i = 4; Test2(ref i); System.Console.Write(i); } static void Test1<T>(ref T i) where T : struct { _ = i[$""]; } static void Test2<T>(ref T i) where T : struct { E.get_Item(ref i, $""); } } """; var expectedOutput = "123423"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>", """ { // Code size 18 (0x12) .maxstack 4 .locals init (T& V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: newobj "InterpolationHandler<T>..ctor(int, int, ref T)" IL_000b: call "int E.get_Item<T>(ref T, InterpolationHandler<T>)" IL_0010: pop IL_0011: ret } """); verifier.VerifyIL("Program.Test2<T>", """ { // Code size 18 (0x12) .maxstack 4 .locals init (T& V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.0 IL_0004: ldc.i4.0 IL_0005: ldloc.0 IL_0006: newobj "InterpolationHandler<T>..ctor(int, int, ref T)" IL_000b: call "int E.get_Item<T>(ref T, InterpolationHandler<T>)" IL_0010: pop IL_0011: ret } """); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], expectedOutput: expectedOutput) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByIn_WithConstantReceiver(bool useMetadataRef, [CombinatorialValues("ref readonly", "in")] string refkind) { var src = $$$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, {{{refkind}}} int i) { System.Console.Write(i); System.Runtime.CompilerServices.Unsafe.AsRef(in i)++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension({{{refkind}}} int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h] { get { System.Console.Write(i); return 0; } } } } """; var exeSource = """ #pragma warning disable CS9193 // Argument 0 should be a variable because it is passed to a 'ref readonly' parameter class Program { static void Main() { Test1(); Test2(); } static void Test1() { _ = 1[$""]; } static void Test2() { E.get_Item(3, $""); } } """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "1234" : null; var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$$""" { // Code size 20 (0x14) .maxstack 4 .locals init (int V_0) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.0 IL_0005: ldc.i4.0 IL_0006: ldloca.s V_0 IL_0008: newobj "InterpolationHandler..ctor(int, int, {{{refkind}}} int)" IL_000d: call "int E.get_Item({{{refkind}}} int, InterpolationHandler)" IL_0012: pop IL_0013: ret } """); verifier.VerifyIL("Program.Test2", $$$""" { // Code size 20 (0x14) .maxstack 4 .locals init (int V_0) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.0 IL_0005: ldc.i4.0 IL_0006: ldloca.s V_0 IL_0008: newobj "InterpolationHandler..ctor(int, int, {{{refkind}}} int)" IL_000d: call "int E.get_Item({{{refkind}}} int, InterpolationHandler)" IL_0012: pop IL_0013: ret } """); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByIn_WithLocalReceiver(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, in int i) { System.Console.Write(i); System.Runtime.CompilerServices.Unsafe.AsRef(in i)++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(in int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h] { get { System.Console.Write(i); System.Runtime.CompilerServices.Unsafe.AsRef(in i)++; return 0; } } } } """; var exeSource = """ int i = 1; _ = i[$""]; System.Console.Write(i); E.get_Item(i, $""); System.Console.Write(i); """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "123345" : null; var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 51 (0x33) .maxstack 4 .locals init (int V_0, //i int& V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: stloc.1 IL_0005: ldloc.1 IL_0006: ldc.i4.0 IL_0007: ldc.i4.0 IL_0008: ldloc.1 IL_0009: newobj "InterpolationHandler..ctor(int, int, in int)" IL_000e: call "int E.get_Item(in int, InterpolationHandler)" IL_0013: pop IL_0014: ldloc.0 IL_0015: call "void System.Console.Write(int)" IL_001a: ldloca.s V_0 IL_001c: stloc.1 IL_001d: ldloc.1 IL_001e: ldc.i4.0 IL_001f: ldc.i4.0 IL_0020: ldloc.1 IL_0021: newobj "InterpolationHandler..ctor(int, int, in int)" IL_0026: call "int E.get_Item(in int, InterpolationHandler)" IL_002b: pop IL_002c: ldloc.0 IL_002d: call "void System.Console.Write(int)" IL_0032: ret } """); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByRefMismatch_01(bool useMetadataRef, [CombinatorialValues("ref readonly", "in", "")] string refkind) { var src = $$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, ref int i) { System.Console.Write(i); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension({{refkind}} int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h] { get => 0; } } } """; var exeSource = $$""" int i = 1; _ = i[$""]; E.get_Item({{(refkind == "" ? "" : "in ")}}i, $""); """; var expectedDiagnostic = new[] { // (2,5): error CS1620: Argument 3 must be passed with the 'ref' keyword // _ = i[$""]; Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "ref").WithLocation(2, 5), // (3,12): error CS1620: Argument 3 must be passed with the 'ref' keyword // E.get_Item(i, $""); Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "ref") }; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostic); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CreateCompilation(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostic); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByRefMismatch_02(bool useMetadataRef, [CombinatorialValues("ref readonly", "in")] string refkind) { var src = $$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, {{refkind}} int i) { System.Console.Write(i); System.Runtime.CompilerServices.Unsafe.AsRef(in i)++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(ref int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h] { get { System.Console.Write(i); i++; return 0; } } } } """; var exeSource = """ int i = 1; _ = i[$""]; System.Console.Write(i); E.get_Item(ref i, $""); System.Console.Write(i); """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "123345" : null; CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify).VerifyDiagnostics(); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByRefMismatch_03(bool useMetadataRef) { var src = $$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, int i) { System.Console.WriteLine(i); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(ref int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h] { get => 0; } } } """; var exeSource = """ int i = 1; _ = i[$""]; E.get_Item(ref i, $""); """; var expectedDiagnostics = new[] { // (2,5): error CS1615: Argument 3 may not be passed with the 'ref' keyword // _ = i[$""]; Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "ref").WithLocation(2, 5), // (3,16): error CS1615: Argument 3 may not be passed with the 'ref' keyword // E.get_Item(ref i, $""); Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "ref").WithLocation(3, 16) }; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostics); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CreateCompilation(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void InterpolationHandler_StructReceiverParameter_ByValue(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, MyStruct s) { System.Console.Write(s.i); s.i++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public struct MyStruct { public int i; } public static class E { extension(MyStruct s) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h] { get { System.Console.Write(s.i); s.i++; return 0; } } } } """; var exeSource = """ _ = new MyStruct()[$""]; E.get_Item(new MyStruct(), $""); """; var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "0000" : null; var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 47 (0x2f) .maxstack 4 .locals init (MyStruct V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "MyStruct" IL_0008: ldloc.0 IL_0009: ldc.i4.0 IL_000a: ldc.i4.0 IL_000b: ldloc.0 IL_000c: newobj "InterpolationHandler..ctor(int, int, MyStruct)" IL_0011: call "int E.get_Item(MyStruct, InterpolationHandler)" IL_0016: pop IL_0017: ldloca.s V_0 IL_0019: initobj "MyStruct" IL_001f: ldloc.0 IL_0020: ldc.i4.0 IL_0021: ldc.i4.0 IL_0022: ldloc.0 IL_0023: newobj "InterpolationHandler..ctor(int, int, MyStruct)" IL_0028: call "int E.get_Item(MyStruct, InterpolationHandler)" IL_002d: pop IL_002e: ret } """); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100, expectedOutput: expectedOutput, verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_StructReceiverParameter_ByValueThroughField(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, MyStruct s) { System.Console.Write(s.i); E.field.i++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public struct MyStruct { public int i; } public static class E { extension(MyStruct s) { public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h] { get { System.Console.Write(s.i); E.field.i++; return 0; } } } public static MyStruct field; } """; var exeSource = """ _ = E.field[Increment(), $""]; E.get_Item(E.field, Increment(), $""); int Increment() => E.field.i++; """; // See GetExtensionBlockMemberReceiverCaptureRefKind var expectedOutput = "1233"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 67 (0x43) .maxstack 5 .locals init (MyStruct& V_0, int V_1, InterpolationHandler V_2, MyStruct V_3) IL_0000: ldsflda "MyStruct E.field" IL_0005: stloc.0 IL_0006: call "int Program.<<Main>$>g__Increment|0_0()" IL_000b: stloc.1 IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: ldobj "MyStruct" IL_0014: newobj "InterpolationHandler..ctor(int, int, MyStruct)" IL_0019: stloc.2 IL_001a: ldloc.0 IL_001b: ldobj "MyStruct" IL_0020: ldloc.1 IL_0021: ldloc.2 IL_0022: call "int E.get_Item(MyStruct, int, InterpolationHandler)" IL_0027: pop IL_0028: ldsfld "MyStruct E.field" IL_002d: stloc.3 IL_002e: ldloc.3 IL_002f: call "int Program.<<Main>$>g__Increment|0_0()" IL_0034: ldc.i4.0 IL_0035: ldc.i4.0 IL_0036: ldloc.3 IL_0037: newobj "InterpolationHandler..ctor(int, int, MyStruct)" IL_003c: call "int E.get_Item(MyStruct, int, InterpolationHandler)" IL_0041: pop IL_0042: ret } """); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], expectedOutput: expectedOutput) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_StructReceiverParameter_Generic_ByValueThroughField(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler<TR> { public InterpolationHandler(int literalLength, int formattedCount, TR s) { System.Console.Write(((MyStruct)(object)s).i); E<MyStruct>.field.i++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public struct MyStruct { public int i; } public static class E { extension<T>(T s) { public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler<T> h] { get { System.Console.Write(((MyStruct)(object)s).i); E<MyStruct>.field.i++; return 0; } } } } public static class E<T> { public static T field; } """; var exeSource = """ class Program { static void Main() { Test1<MyStruct>(); Test2<MyStruct>(); Test3<MyStruct>(); Test4<MyStruct>(); } static void Test1<T>() { _ = E<T>.field[Increment(), $""]; } static void Test2<T>() { E.get_Item(E<T>.field, Increment(), $""); } static void Test3<T>() where T : struct { _ = E<T>.field[Increment(), $""]; } static void Test4<T>() where T : struct { E.get_Item(E<T>.field, Increment(), $""); } static int Increment() => E<MyStruct>.field.i++; } """; // See GetExtensionBlockMemberReceiverCaptureRefKind var expectedOutput = "12337899"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>", """ { // Code size 73 (0x49) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, InterpolationHandler<T> V_4, T V_5) IL_0000: ldsflda "T E<T>.field" IL_0005: stloc.2 IL_0006: ldloca.s V_5 IL_0008: initobj "T" IL_000e: ldloc.s V_5 IL_0010: box "T" IL_0015: brtrue.s IL_0022 IL_0017: ldloc.2 IL_0018: ldobj "T" IL_001d: stloc.1 IL_001e: ldloca.s V_1 IL_0020: br.s IL_0023 IL_0022: ldloc.2 IL_0023: stloc.0 IL_0024: call "int Program.Increment()" IL_0029: stloc.3 IL_002a: ldc.i4.0 IL_002b: ldc.i4.0 IL_002c: ldloc.0 IL_002d: ldobj "T" IL_0032: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0037: stloc.s V_4 IL_0039: ldloc.0 IL_003a: ldobj "T" IL_003f: ldloc.3 IL_0040: ldloc.s V_4 IL_0042: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0047: pop IL_0048: ret } """); var expectedIL = """ { // Code size 27 (0x1b) .maxstack 5 .locals init (T V_0) IL_0000: ldsfld "T E<T>.field" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call "int Program.Increment()" IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0014: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0019: pop IL_001a: ret } """; verifier.VerifyIL("Program.Test2<T>", expectedIL); verifier.VerifyIL("Program.Test3<T>", """ { // Code size 41 (0x29) .maxstack 3 .locals init (T& V_0, int V_1, InterpolationHandler<T> V_2) IL_0000: ldsflda "T E<T>.field" IL_0005: stloc.0 IL_0006: call "int Program.Increment()" IL_000b: stloc.1 IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: ldobj "T" IL_0014: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0019: stloc.2 IL_001a: ldloc.0 IL_001b: ldobj "T" IL_0020: ldloc.1 IL_0021: ldloc.2 IL_0022: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0027: pop IL_0028: ret } """); verifier.VerifyIL("Program.Test4<T>", expectedIL); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], expectedOutput: expectedOutput) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_StructReceiverParameter_Generic_ByValueThroughField_CompoundAssignment(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler<TR> { public InterpolationHandler(int literalLength, int formattedCount, TR s) { System.Console.Write(((MyStruct)(object)s).i); E<MyStruct>.field.i++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public struct MyStruct { public int i; } public static class E { extension<T>(T s) { public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler<T> h] { get { System.Console.Write(((MyStruct)(object)s).i); E<MyStruct>.field.i++; return 0; } set { System.Console.Write(((MyStruct)(object)s).i); System.Console.Write(" "); E<MyStruct>.field.i++; } } } } public static class E<T> { public static T field; } """; var exeSource = """ class Program { static void Main() { Test1<MyStruct>(); Test3<MyStruct>(); } static void Test1<T>() { E<T>.field[Increment(), $""] += 0; } static void Test3<T>() where T : struct { E<T>.field[Increment(), $""] += 0; } static int Increment() => E<MyStruct>.field.i++; } """; var expectedOutput = "123 567"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], expectedOutput: expectedOutput) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_StructReceiverParameter_GenericStruct_ByValueThroughField(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler<TR> { public InterpolationHandler(int literalLength, int formattedCount, TR s) { System.Console.Write(((MyStruct)(object)s).i); E<MyStruct>.field.i++; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public struct MyStruct { public int i; } public static class E { extension<T>(T s) where T : struct { public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler<T> h] { get { System.Console.Write(((MyStruct)(object)s).i); E<MyStruct>.field.i++; return 0; } } } } public static class E<T> { public static T field; } """; var exeSource = """ class Program { static void Main() { Test3<MyStruct>(); Test4<MyStruct>(); } static void Test3<T>() where T : struct { _ = E<T>.field[Increment(), $""]; } static void Test4<T>() where T : struct { E.get_Item(E<T>.field, Increment(), $""); } static int Increment() => E<MyStruct>.field.i++; } """; var expectedOutput = "1233"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test3<T>", """ { // Code size 41 (0x29) .maxstack 3 .locals init (T& V_0, int V_1, InterpolationHandler<T> V_2) IL_0000: ldsflda "T E<T>.field" IL_0005: stloc.0 IL_0006: call "int Program.Increment()" IL_000b: stloc.1 IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: ldobj "T" IL_0014: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0019: stloc.2 IL_001a: ldloc.0 IL_001b: ldobj "T" IL_0020: ldloc.1 IL_0021: ldloc.2 IL_0022: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0027: pop IL_0028: ret } """); verifier.VerifyIL("Program.Test4<T>", """ { // Code size 27 (0x1b) .maxstack 5 .locals init (T V_0) IL_0000: ldsfld "T E<T>.field" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call "int Program.Increment()" IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0014: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0019: pop IL_001a: ret } """); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], expectedOutput: expectedOutput) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ClassReceiverParameter_GenericClass_ByValueThroughField(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler<TR> { public InterpolationHandler(int literalLength, int formattedCount, TR s) { System.Console.Write(((MyClass)(object)s).i); E<MyClass>.field = new MyClass() { i = E<MyClass>.field.i + 1 }; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public class MyClass { public int i; } public static class E { extension<T>(T s) { public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler<T> h] { get { System.Console.Write(((MyClass)(object)s).i); E<MyClass>.field = new MyClass() { i = E<MyClass>.field.i + 1 }; return 0; } } } } public static class E<T> { public static T field; } """; var exeSource = """ class Program { static void Main() { E<MyClass>.field = new MyClass(); Test1<MyClass>(); Test2<MyClass>(); Test3<MyClass>(); Test4<MyClass>(); } static void Test1<T>() { _ = E<T>.field[Increment(), $""]; } static void Test2<T>() { E.get_Item(E<T>.field, Increment(), $""); } static void Test3<T>() where T : class { _ = E<T>.field[Increment(), $""]; } static void Test4<T>() where T : class { E.get_Item(E<T>.field, Increment(), $""); } static int Increment() => (E<MyClass>.field = new MyClass() { i = E<MyClass>.field.i + 1 }).i; } """; var expectedOutput = "00336699"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>", """ { // Code size 73 (0x49) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, InterpolationHandler<T> V_4, T V_5) IL_0000: ldsflda "T E<T>.field" IL_0005: stloc.2 IL_0006: ldloca.s V_5 IL_0008: initobj "T" IL_000e: ldloc.s V_5 IL_0010: box "T" IL_0015: brtrue.s IL_0022 IL_0017: ldloc.2 IL_0018: ldobj "T" IL_001d: stloc.1 IL_001e: ldloca.s V_1 IL_0020: br.s IL_0023 IL_0022: ldloc.2 IL_0023: stloc.0 IL_0024: call "int Program.Increment()" IL_0029: stloc.3 IL_002a: ldc.i4.0 IL_002b: ldc.i4.0 IL_002c: ldloc.0 IL_002d: ldobj "T" IL_0032: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0037: stloc.s V_4 IL_0039: ldloc.0 IL_003a: ldobj "T" IL_003f: ldloc.3 IL_0040: ldloc.s V_4 IL_0042: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0047: pop IL_0048: ret } """); verifier.VerifyIL("Program.Test2<T>", """ { // Code size 27 (0x1b) .maxstack 5 .locals init (T V_0) IL_0000: ldsfld "T E<T>.field" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call "int Program.Increment()" IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0014: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0019: pop IL_001a: ret } """); verifier.VerifyIL("Program.Test3<T>", """ { // Code size 27 (0x1b) .maxstack 5 .locals init (T V_0) IL_0000: ldsfld "T E<T>.field" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call "int Program.Increment()" IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0014: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0019: pop IL_001a: ret } """); verifier.VerifyIL("Program.Test4<T>", """ { // Code size 27 (0x1b) .maxstack 5 .locals init (T V_0) IL_0000: ldsfld "T E<T>.field" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call "int Program.Increment()" IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0014: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0019: pop IL_001a: ret } """); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], expectedOutput: expectedOutput) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ClassReceiverParameter_Generic_ByValueThroughField(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler<TR> { public InterpolationHandler(int literalLength, int formattedCount, TR s) { System.Console.Write(((MyClass)(object)s).i); E<MyClass>.field = new MyClass() { i = E<MyClass>.field.i + 1 }; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public class MyClass { public int i; } public static class E { extension<T>(T s) where T : class { public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler<T> h] { get { System.Console.Write(((MyClass)(object)s).i); E<MyClass>.field = new MyClass() { i = E<MyClass>.field.i + 1 }; return 0; } } } } public static class E<T> { public static T field; } """; var exeSource = """ class Program { static void Main() { E<MyClass>.field = new MyClass(); Test3<MyClass>(); Test4<MyClass>(); } static void Test3<T>() where T : class { _ = E<T>.field[Increment(), $""]; } static void Test4<T>() where T : class { E.get_Item(E<T>.field, Increment(), $""); } static int Increment() => (E<MyClass>.field = new MyClass() { i = E<MyClass>.field.i + 1 }).i; } """; var expectedOutput = "0033"; var verifier = CompileAndVerify([exeSource, src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], expectedOutput: expectedOutput) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test3<T>", """ { // Code size 27 (0x1b) .maxstack 5 .locals init (T V_0) IL_0000: ldsfld "T E<T>.field" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call "int Program.Increment()" IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0014: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0019: pop IL_001a: ret } """); verifier.VerifyIL("Program.Test4<T>", """ { // Code size 27 (0x1b) .maxstack 5 .locals init (T V_0) IL_0000: ldsfld "T E<T>.field" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: call "int Program.Increment()" IL_000c: ldc.i4.0 IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: newobj "InterpolationHandler<T>..ctor(int, int, T)" IL_0014: call "int E.get_Item<T>(T, int, InterpolationHandler<T>)" IL_0019: pop IL_001a: ret } """); var comp1 = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute]); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], expectedOutput: expectedOutput) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_RefStructReceiverParameter_EscapeScopes_01(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public ref struct InterpolationHandler { #pragma warning disable CS0169 // The field 'InterpolationHandler.i' is never used private ref int i; #pragma warning restore CS0169 // The field 'InterpolationHandler.i' is never used public InterpolationHandler(int literalLength, int formattedCount, scoped MyStruct s) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public ref struct MyStruct { public ref int i; } public static class E { extension(MyStruct s) { public MyStruct this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s")] InterpolationHandler h] { get { return new(); } } } } """; var exeSource = """ #pragma warning disable CS8321 // The local function 'localFunc' is declared but never used MyStruct localFunc() #pragma warning restore CS8321 // The local function 'localFunc' is declared but never used { return new MyStruct()[$""]; } """; var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, verify: Verification.Fails).VerifyDiagnostics(); verifier.VerifyIL("Program.<<Main>$>g__localFunc|0_0()", """ { // Code size 23 (0x17) .maxstack 4 .locals init (MyStruct V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "MyStruct" IL_0008: ldloc.0 IL_0009: ldc.i4.0 IL_000a: ldc.i4.0 IL_000b: ldloc.0 IL_000c: newobj "InterpolationHandler..ctor(int, int, scoped MyStruct)" IL_0011: call "MyStruct E.get_Item(MyStruct, InterpolationHandler)" IL_0016: ret } """); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100, verify: Verification.Fails) .VerifyDiagnostics(); } [Fact] public void InterpolationHandler_RefStructReceiverParameter_EscapeScopes_02() { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public ref struct InterpolationHandler { public ref int i; public InterpolationHandler(int literalLength, int formattedCount, MyStruct s2) { i = ref s2.i; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public ref struct MyStruct { public ref int i; } public static class E { extension(MyStruct s1) { public MyStruct this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s1")] InterpolationHandler h] => new() { i = ref h.i }; } } """; var exeSource = """ #pragma warning disable CS8321 // The local function 'localFunc' is declared but never used MyStruct localFunc() #pragma warning restore CS8321 // The local function 'localFunc' is declared but never used { int i = 0; return new MyStruct() { i = ref i }[$""]; } """; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (6,27): error CS8352: Cannot use variable 'i = ref i' in this context because it may expose referenced variables outside of their declaration scope // return new MyStruct() { i = ref i }[$""]; Diagnostic(ErrorCode.ERR_EscapeVariable, "{ i = ref i }").WithArguments("i = ref i").WithLocation(6, 27), // (6,12): error CS8347: Cannot use a result of 'E.extension(MyStruct).this[InterpolationHandler]' in this context because it may expose variables referenced by parameter 's1' outside of their declaration scope // return new MyStruct() { i = ref i }[$""]; Diagnostic(ErrorCode.ERR_EscapeCall, @"new MyStruct() { i = ref i }[$""""]").WithArguments("E.extension(MyStruct).this[InterpolationHandler]", "s1").WithLocation(6, 12) ); } [Fact] public void InterpolationHandler_RefStructReceiverParameter_EscapeScopes_05() { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public ref struct InterpolationHandler { public ref int i; public InterpolationHandler(int literalLength, int formattedCount, MyStruct s2) { i = ref s2.i; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public ref struct MyStruct { public ref int i; } public static class E { extension(scoped MyStruct s1) { public MyStruct this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s1")] InterpolationHandler h] => new() { i = ref h.i }; } } """; var exeSource = """ #pragma warning disable CS8321 // The local function 'localFunc' is declared but never used MyStruct localFunc() #pragma warning restore CS8321 // The local function 'localFunc' is declared but never used { int i = 0; return new MyStruct() { i = ref i }[$""]; } """; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (6,12): error CS8352: Cannot use variable 'new MyStruct() { i = ref i }' in this context because it may expose referenced variables outside of their declaration scope // return new MyStruct() { i = ref i }[$""]; Diagnostic(ErrorCode.ERR_EscapeVariable, "new MyStruct() { i = ref i }").WithArguments("new MyStruct() { i = ref i }").WithLocation(6, 12), // (6,12): error CS8347: Cannot use a result of 'E.extension(scoped MyStruct).this[InterpolationHandler]' in this context because it may expose variables referenced by parameter 'h' outside of their declaration scope // return new MyStruct() { i = ref i }[$""]; Diagnostic(ErrorCode.ERR_EscapeCall, @"new MyStruct() { i = ref i }[$""""]").WithArguments("E.extension(scoped MyStruct).this[InterpolationHandler]", "h").WithLocation(6, 12), // (6,41): error CS8347: Cannot use a result of 'InterpolationHandler.InterpolationHandler(int, int, MyStruct)' in this context because it may expose variables referenced by parameter 's2' outside of their declaration scope // return new MyStruct() { i = ref i }[$""]; Diagnostic(ErrorCode.ERR_EscapeCall, @"$""""").WithArguments("InterpolationHandler.InterpolationHandler(int, int, MyStruct)", "s2").WithLocation(6, 41) ); } [Fact] public void InterpolationHandler_RefStructReceiverParameter_EscapeScopes_06() { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public ref struct InterpolationHandler { public ref int i; public InterpolationHandler(int literalLength, int formattedCount, MyStruct s2) { i = ref s2.i; } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public ref struct MyStruct { public ref int i; } public static class E { extension(scoped MyStruct s1) { public MyStruct this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s1")] scoped InterpolationHandler h] => new() { i = ref h.i }; } } """; var exeSource = """ #pragma warning disable CS8321 // The local function 'localFunc' is declared but never used MyStruct localFunc() #pragma warning restore CS8321 // The local function 'localFunc' is declared but never used { int i = 0; return new MyStruct() { i = ref i }[$""]; } """; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (24,22): error CS8352: Cannot use variable 'i = ref h.i' in this context because it may expose referenced variables outside of their declaration scope // => new() { i = ref h.i }; Diagnostic(ErrorCode.ERR_EscapeVariable, "{ i = ref h.i }").WithArguments("i = ref h.i").WithLocation(24, 22) ); } [Theory, CombinatorialData] public void InterpolationHandler_RefStructReceiverParameter_EscapeScopes_07(bool useMetadataRef) { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public ref struct InterpolationHandler { #pragma warning disable CS0169 // The field 'InterpolationHandler.i' is never used private ref int i; #pragma warning restore CS0169 // The field 'InterpolationHandler.i' is never used public InterpolationHandler(int literalLength, int formattedCount, scoped MyStruct s2) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public ref struct MyStruct { public ref int i; } public static class E { extension(scoped MyStruct s1) { public MyStruct this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s1")] scoped InterpolationHandler h] => new(); } } """; var exeSource = """ #pragma warning disable CS8321 // The local function 'localFunc' is declared but never used MyStruct localFunc() #pragma warning restore CS8321 // The local function 'localFunc' is declared but never used { int i = 0; return new MyStruct() { i = ref i }[$""]; } """; var verifier = CompileAndVerify([exeSource, src], targetFramework: TargetFramework.Net100, verify: Verification.Fails).VerifyDiagnostics(); verifier.VerifyIL("Program.<<Main>$>g__localFunc|0_0()", """ { // Code size 36 (0x24) .maxstack 4 .locals init (int V_0, //i MyStruct V_1, MyStruct V_2) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloca.s V_2 IL_0004: initobj "MyStruct" IL_000a: ldloca.s V_2 IL_000c: ldloca.s V_0 IL_000e: stfld "ref int MyStruct.i" IL_0013: ldloc.2 IL_0014: stloc.1 IL_0015: ldloc.1 IL_0016: ldc.i4.0 IL_0017: ldc.i4.0 IL_0018: ldloc.1 IL_0019: newobj "InterpolationHandler..ctor(int, int, scoped MyStruct)" IL_001e: call "MyStruct E.get_Item(scoped MyStruct, scoped InterpolationHandler)" IL_0023: ret } """); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100, verify: Verification.Fails) .VerifyDiagnostics(); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_NullableMismatch_01(bool useMetadataRef, bool useOut) { string outParam = useOut ? ", out bool valid" : ""; var src = $$""" #nullable enable [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{{outParam}}) { System.Console.Write(s1); System.Console.Write(s2); {{(useOut ? "valid = true;" : "")}} } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string? format = null) => throw null!; } public static class E { extension(C? s1) { public int this[string? s2, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s1", "s2")] InterpolationHandler h] { get => 0; } } } public class C { } """; var exeSource = """ #nullable enable _ = ((C?)null)[null, $""]; // 1, 2 _ = ((C?)null)["", $""]; // 3 _ = new C()[null, $""]; // 4 _ = new C()["", $""]; _ = E.get_Item(null, null, $""); // 5, 6 _ = E.get_Item(new C(), null, $""); // 7 _ = E.get_Item(null, "", $""); // 8 _ = E.get_Item(new C(), "", $""); """; var expectedDiagnostics = new[] { // (2,6): warning CS8604: Possible null reference argument for parameter 's1' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2, out bool valid)'. // _ = ((C?)null)[null, $""]; // 1, 2 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "(C?)null").WithArguments("s1", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{outParam})").WithLocation(2, 6), // (2,16): warning CS8604: Possible null reference argument for parameter 's2' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2, out bool valid)'. // _ = ((C?)null)[null, $""]; // 1, 2 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s2", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{outParam})").WithLocation(2, 16), // (3,6): warning CS8604: Possible null reference argument for parameter 's1' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2, out bool valid)'. // _ = ((C?)null)["", $""]; // 3 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "(C?)null").WithArguments("s1", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{outParam})").WithLocation(3, 6), // (4,13): warning CS8604: Possible null reference argument for parameter 's2' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2, out bool valid)'. // _ = new C()[null, $""]; // 4 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s2", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{outParam})").WithLocation(4, 13), // (7,16): warning CS8604: Possible null reference argument for parameter 's1' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2, out bool valid)'. // _ = E.get_Item(null, null, $""); // 5, 6 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s1", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{outParam})").WithLocation(7, 16), // (7,22): warning CS8604: Possible null reference argument for parameter 's2' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2, out bool valid)'. // _ = E.get_Item(null, null, $""); // 5, 6 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s2", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{outParam})").WithLocation(7, 22), // (8,25): warning CS8604: Possible null reference argument for parameter 's2' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2, out bool valid)'. // _ = E.get_Item(new C(), null, $""); // 7 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s2", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{outParam})").WithLocation(8, 25), // (9,16): warning CS8604: Possible null reference argument for parameter 's1' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2, out bool valid)'. // _ = E.get_Item(null, "", $""); // 8 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s1", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, C s1, string s2{outParam})").WithLocation(9, 16) }; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostics); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CreateCompilation(exeSource, references: [AsReference(comp1, useMetadataRef)], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostics); } [Theory, CombinatorialData] public void InterpolationHandler_ReceiverParameter_NullableMismatch_02(bool useMetadataRef, bool useOut) { string outParam = useOut ? ", out bool valid" : ""; var src = $$""" #nullable enable [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{{outParam}}) { System.Console.Write(s1); System.Console.Write(s2); {{(useOut ? "valid = true;" : "")}} } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string? format = null) => throw null!; } public static class E { extension(C? s1) { public int this[string? s2, string? s3, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("s2", "s3")] InterpolationHandler h] { get => 0; } } } public class C { } """; var exeSource = """ #nullable enable _ = new C()[null, null, $""]; _ = new C()[null, "", $""]; _ = new C()["", null, $""]; _ = new C()["", "", $""]; E.get_Item(new C(), null, null, $""); E.get_Item(new C(), "", null, $""); E.get_Item(new C(), null, "", $""); E.get_Item(new C(), "", "", $""); """; var expectedDiagnostics = new[] { // (2,13): warning CS8604: Possible null reference argument for parameter 's1' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2)'. // _ = new C()[null, null, $""]; Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s1", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{outParam})").WithLocation(2, 13), // (2,19): warning CS8604: Possible null reference argument for parameter 's2' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2)'. // _ = new C()[null, null, $""]; Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s2", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{outParam})").WithLocation(2, 19), // (3,13): warning CS8604: Possible null reference argument for parameter 's1' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2)'. // _ = new C()[null, "", $""]; Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s1", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{outParam})").WithLocation(3, 13), // (4,17): warning CS8604: Possible null reference argument for parameter 's2' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2)'. // _ = new C()["", null, $""]; Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s2", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{outParam})").WithLocation(4, 17), // (7,21): warning CS8604: Possible null reference argument for parameter 's1' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2)'. // E.get_Item(new C(), null, null, $""); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s1", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{outParam})").WithLocation(7, 21), // (7,27): warning CS8604: Possible null reference argument for parameter 's2' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2)'. // E.get_Item(new C(), null, null, $""); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s2", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{outParam})").WithLocation(7, 27), // (8,25): warning CS8604: Possible null reference argument for parameter 's2' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2)'. // E.get_Item(new C(), "", null, $""); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s2", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{outParam})").WithLocation(8, 25), // (9,21): warning CS8604: Possible null reference argument for parameter 's1' in 'InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2)'. // E.get_Item(new C(), null, "", $""); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "null").WithArguments("s1", $"InterpolationHandler.InterpolationHandler(int literalLength, int formattedCount, string s1, string s2{outParam})").WithLocation(9, 21) }; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostics); var comp1 = CreateCompilation(src, targetFramework: TargetFramework.Net100); CreateCompilation(exeSource, references: [useMetadataRef ? comp1.ToMetadataReference() : comp1.EmitToImageReference()], targetFramework: TargetFramework.Net100).VerifyDiagnostics(expectedDiagnostics); } [Fact] public void InterpolationHandler_ParameterErrors_MappedCorrectly_01() { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, int i) { System.Console.WriteLine(i); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("nonexistent")] InterpolationHandler h] { get => 0; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyDiagnostics( // (16,26): error CS8945: 'nonexistent' is not a valid parameter name from 'E.extension(int).this[InterpolationHandler]'. // public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("nonexistent")] InterpolationHandler h] { get => 0; } Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"System.Runtime.CompilerServices.InterpolatedStringHandlerArgument(""nonexistent"")").WithArguments("nonexistent", "E.extension(int).this[InterpolationHandler]").WithLocation(16, 26) ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var extension = tree.GetRoot().DescendantNodes().OfType<ExtensionBlockDeclarationSyntax>().Single(); var symbol = model.GetDeclaredSymbol(extension); Assert.True(symbol.IsExtension); var underlying = symbol.GetSymbol<NamedTypeSymbol>(); var indexer = underlying.GetMember<PropertySymbol>("this[]"); Assert.False(underlying.ExtensionParameter.HasInterpolatedStringHandlerArgumentError); Assert.True(underlying.ExtensionParameter.InterpolatedStringHandlerArgumentIndexes.IsEmpty); Assert.True(indexer.Parameters[0].HasInterpolatedStringHandlerArgumentError); Assert.True(indexer.Parameters[0].InterpolatedStringHandlerArgumentIndexes.IsEmpty); var implGetter = underlying.ContainingType.GetMember<MethodSymbol>("get_Item"); Assert.False(implGetter.Parameters[0].HasInterpolatedStringHandlerArgumentError); Assert.True(implGetter.Parameters[0].InterpolatedStringHandlerArgumentIndexes.IsEmpty); Assert.True(implGetter.Parameters[1].HasInterpolatedStringHandlerArgumentError); Assert.True(implGetter.Parameters[1].InterpolatedStringHandlerArgumentIndexes.IsEmpty); } [Theory] [InlineData("i")] [InlineData("")] [InlineData("nonexistent")] public void InterpolationHandler_ParameterErrors_MappedCorrectly_02(string attributeValue) { var src = $$""" [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, InterpolationHandler i) { System.Console.WriteLine(i); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("{{attributeValue}}")] InterpolationHandler i) { public int this[int j] { get => 0; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyDiagnostics( // (14,16): error CS9325: Interpolated string handler arguments are not allowed in this context. // extension([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("")] InterpolationHandler i) Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentDisallowed, $@"System.Runtime.CompilerServices.InterpolatedStringHandlerArgument(""{attributeValue}"")").WithLocation(14, 16) ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var extension = tree.GetRoot().DescendantNodes().OfType<ExtensionBlockDeclarationSyntax>().Single(); var symbol = model.GetDeclaredSymbol(extension); Assert.True(symbol.IsExtension); var underlying = symbol.GetSymbol<NamedTypeSymbol>(); Assert.True(underlying.ExtensionParameter.HasInterpolatedStringHandlerArgumentError); Assert.True(underlying.ExtensionParameter.InterpolatedStringHandlerArgumentIndexes.IsEmpty); var implGetter = underlying.ContainingType.GetMember<MethodSymbol>("get_Item"); Assert.True(implGetter.Parameters[0].HasInterpolatedStringHandlerArgumentError); Assert.True(implGetter.Parameters[0].InterpolatedStringHandlerArgumentIndexes.IsEmpty); } [Fact] public void InterpolationHandler_ParameterErrors_MappedCorrectly_03() { var src = """ [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount, int i) { System.Console.WriteLine(i); } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(int i) { public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("")] InterpolationHandler h] { get => 0; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyDiagnostics( // (16,26): error CS8944: 'E.extension(int).this[InterpolationHandler]' is not an instance method, the receiver or extension receiver parameter cannot be an interpolated string handler argument. // public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("")] InterpolationHandler h] { get => 0; } Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("""")").WithArguments("E.extension(int).this[InterpolationHandler]").WithLocation(16, 26) ); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var extension = tree.GetRoot().DescendantNodes().OfType<ExtensionBlockDeclarationSyntax>().Single(); var symbol = model.GetDeclaredSymbol(extension); Assert.True(symbol.IsExtension); var underlying = symbol.GetSymbol<NamedTypeSymbol>(); var indexer = underlying.GetMember<PropertySymbol>("this[]"); Assert.False(underlying.ExtensionParameter.HasInterpolatedStringHandlerArgumentError); Assert.True(underlying.ExtensionParameter.InterpolatedStringHandlerArgumentIndexes.IsEmpty); Assert.True(indexer.Parameters[0].HasInterpolatedStringHandlerArgumentError); Assert.True(indexer.Parameters[0].InterpolatedStringHandlerArgumentIndexes.IsEmpty); var implGetter = underlying.ContainingType.GetMember<MethodSymbol>("get_Item"); Assert.False(implGetter.Parameters[0].HasInterpolatedStringHandlerArgumentError); Assert.True(implGetter.Parameters[0].InterpolatedStringHandlerArgumentIndexes.IsEmpty); Assert.True(implGetter.Parameters[1].HasInterpolatedStringHandlerArgumentError); Assert.True(implGetter.Parameters[1].InterpolatedStringHandlerArgumentIndexes.IsEmpty); } [Fact] public void InterpolationHandler_ReferencesInstanceParameter_FromMetadata() { // Equivalent to: // [System.Runtime.CompilerServices.InterpolatedStringHandler] // public struct InterpolationHandler // { // public InterpolationHandler(int literalLength, int formattedCount, string param) // { // } // public void AppendLiteral(string value) { } // public void AppendFormatted<T>(T hole, int alignment = 0, string? format = null) { } // } //public static class E //{ // extension(int i) // { // public int this[[System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] InterpolationHandler h] // { // } // } //} var il = """ .class public sequential ansi sealed beforefieldinit InterpolationHandler extends [mscorlib]System.ValueType { .custom instance void [mscorlib]System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute::.ctor() = (01 00 00 00) .pack 0 .size 1 // Methods .method public hidebysig specialname rtspecialname instance void .ctor (int32 literalLength, int32 formattedCount, int32 param) cil managed { nop ret } .method public hidebysig instance void AppendLiteral (string 'value') cil managed { nop ret } .method public hidebysig instance void AppendFormatted<T> (!!T hole, [opt] int32 'alignment', [opt] string format) cil managed { .param [2] = int32(0) .param [3] = nullref nop ret } } .class public auto ansi abstract sealed beforefieldinit E extends System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .class nested public auto ansi sealed specialname '<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69' extends System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6d 00 00 ) .class nested public auto ansi abstract sealed specialname '<M>$F4B4FFE41AB49E80A4ECF390CF6EB372' extends [mscorlib]System.Object { .method public hidebysig specialname static void '<Extension>$' ( int32 i ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) ret } } .method public hidebysig specialname instance int32 get_Item ( valuetype InterpolationHandler h ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) .param [1] .custom instance void [mscorlib]System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute::.ctor(string) = (01 00 00 00 00) newobj instance void [mscorlib]System.NotSupportedException::.ctor() throw } .property instance int32 Item( valuetype InterpolationHandler h ) { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 46 34 42 34 46 46 45 34 31 41 42 34 39 45 38 30 41 34 45 43 46 33 39 30 43 46 36 45 42 33 37 32 00 00 ) .get instance int32 E/'<G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69'::get_Item(valuetype InterpolationHandler) } } .method public hidebysig static int32 get_Item ( int32 i, valuetype InterpolationHandler h ) cil managed { .param [2] .custom instance void [mscorlib]System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute::.ctor(string) = (01 00 00 00 00) ldc.i4.0 ret } } """ + ExtensionMarkerAttributeIL; var src = """ _ = 1[$""]; E.get_Item(1, $""); """; CreateCompilationWithIL(src, ilSource: il, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( // (1,7): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'InterpolationHandler h' is malformed and cannot be interpreted. Construct an instance of 'InterpolationHandler' manually. // _ = 1[$""]; Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("InterpolationHandler h", "InterpolationHandler").WithLocation(1, 7), // (1,7): error CS7036: There is no argument given that corresponds to the required parameter 'param' of 'InterpolationHandler.InterpolationHandler(int, int, int)' // _ = 1[$""]; Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""""").WithArguments("param", "InterpolationHandler.InterpolationHandler(int, int, int)").WithLocation(1, 7), // (1,7): error CS1615: Argument 3 may not be passed with the 'out' keyword // _ = 1[$""]; Diagnostic(ErrorCode.ERR_BadArgExtraRef, @"$""""").WithArguments("3", "out").WithLocation(1, 7), // (2,15): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'InterpolationHandler h' is malformed and cannot be interpreted. Construct an instance of 'InterpolationHandler' manually. // E.get_Item(1, $""); Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("InterpolationHandler h", "InterpolationHandler").WithLocation(2, 15), // (2,15): error CS7036: There is no argument given that corresponds to the required parameter 'param' of 'InterpolationHandler.InterpolationHandler(int, int, int)' // E.get_Item(1, $""); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""""").WithArguments("param", "InterpolationHandler.InterpolationHandler(int, int, int)").WithLocation(2, 15), // (2,15): error CS1615: Argument 3 may not be passed with the 'out' keyword // E.get_Item(1, $""); Diagnostic(ErrorCode.ERR_BadArgExtraRef, @"$""""").WithArguments("3", "out").WithLocation(2, 15) ); } [Fact] public void InterpolationHandler_AsExtensionParameter() { var src = """ _ = $"{42}"[""]; [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct InterpolationHandler { public InterpolationHandler(int literalLength, int formattedCount) { } public void AppendLiteral(string value) { } public void AppendFormatted<T>(T hole, int alignment = 0, string format = null) => throw null; } public static class E { extension(InterpolationHandler h) { public int this[string s] { get => 0; } } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyDiagnostics( // (1,13): error CS1503: Argument 1: cannot convert from 'string' to 'int' // _ = $"{42}"[""]; Diagnostic(ErrorCode.ERR_BadArgType, @"""""").WithArguments("1", "string", "int").WithLocation(1, 13)); } [Fact] public void InterpolationHandler_ObjectInitializer_01() { var code = """ public class Program { public static void Main() { /*<bind>*/ _ = new C() { [42, $"{43}"] = 1 }; /*</bind>*/ } } public class C { } [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i) { System.Console.Write($"{i} "); } public void AppendFormatted(int i) { System.Console.Write($"{i} "); } } public static class CExt { extension(C c) { public int this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] CustomHandler h] { set { } } } } """; var comp = CreateCompilation(code, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("42 43"), verify: Verification.Skipped).VerifyDiagnostics(); string expectedOperationTree = """ IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = new C() ... 3}"] = 1 };') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C) (Syntax: '_ = new C() ... 43}"] = 1 }') Left: IDiscardOperation (Symbol: C _) (OperationKind.Discard, Type: C) (Syntax: '_') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C() { [ ... 43}"] = 1 }') Arguments(0) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C) (Syntax: '{ [42, $"{43}"] = 1 }') Initializers(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: '[42, $"{43}"] = 1') Left: IPropertyReferenceOperation: System.Int32 CExt.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i, CustomHandler h] { set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: '[42, $"{43}"]') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: '[42, $"{43}"]') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '42') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: h) (OperationKind.Argument, Type: null) (Syntax: '$"{43}"') IInterpolatedStringHandlerCreationOperation (HandlerAppendCallsReturnBool: False, HandlerCreationHasSuccessParameter: False) (OperationKind.InterpolatedStringHandlerCreation, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Creation: IObjectCreationOperation (Constructor: CustomHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount, System.Int32 i)) (OperationKind.ObjectCreation, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: literalLength) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$"{43}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: formattedCount) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$"{43}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '42') IInterpolatedStringHandlerArgumentPlaceholderOperation (ArgumentIndex: 0) (OperationKind.InterpolatedStringHandlerArgumentPlaceholder, Type: null, IsImplicit) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null Content: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$"{43}"') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{43}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Int32 i)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{43}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '43') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 43) (Syntax: '43') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') """; VerifyOperationTreeAndDiagnosticsForTest<ExpressionStatementSyntax>(comp, expectedOperationTree, expectedDiagnostics: []); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var mainDeclaration = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().First(); var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(mainDeclaration.Body, model); ControlFlowGraphVerifier.VerifyGraph(comp, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] Block[B1] - Block Predecessors: [B0] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new C() { [ ... 43}"] = 1 }') Value: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C() { [ ... 43}"] = 1 }') Arguments(0) Initializer: null Next (Regular) Block[B2] Entering: {R2} .locals {R2} { CaptureIds: [1] [2] Block[B2] - Block Predecessors: [B1] Statements (4) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '42') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '$"{43}"') Value: IObjectCreationOperation (Constructor: CustomHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount, System.Int32 i)) (OperationKind.ObjectCreation, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: literalLength) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$"{43}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: formattedCount) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$"{43}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '42') IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, Constant: 42, IsImplicit) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null IInvocationOperation ( void CustomHandler.AppendFormatted(System.Int32 i)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{43}') Instance Receiver: IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '43') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 43) (Syntax: '43') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: '[42, $"{43}"] = 1') Left: IPropertyReferenceOperation: System.Int32 CExt.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i, CustomHandler h] { set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: '[42, $"{43}"]') Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'new C() { [ ... 43}"] = 1 }') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '42') IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, Constant: 42, IsImplicit) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: h) (OperationKind.Argument, Type: null) (Syntax: '$"{43}"') IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Next (Regular) Block[B3] Leaving: {R2} } Block[B3] - Block Predecessors: [B2] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = new C() ... 3}"] = 1 };') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C) (Syntax: '_ = new C() ... 43}"] = 1 }') Left: IDiscardOperation (Symbol: C _) (OperationKind.Discard, Type: C) (Syntax: '_') Right: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'new C() { [ ... 43}"] = 1 }') Next (Regular) Block[B4] Leaving: {R1} } Block[B4] - Exit Predecessors: [B3] Statements (0) """, graph, symbol); } [Fact] public void InterpolationHandler_ObjectInitializer_02() { var code = """ public class Program { public static void Main() { /*<bind>*/ _ = new C() { [42, $"{43}"] = { Field = 0 } }; /*</bind>*/ } } public class C { } public class D { public int Field; } [System.Runtime.CompilerServices.InterpolatedStringHandler] public struct CustomHandler { public CustomHandler(int literalLength, int formattedCount, int i) { System.Console.Write($"{i} "); } public void AppendFormatted(int i) { System.Console.Write($"{i} "); } } public static class CExt { extension(C c) { public D this[int i, [System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("i")] CustomHandler h] { get => new D(); } } } """; var comp = CreateCompilation(code, targetFramework: TargetFramework.Net100, options: TestOptions.DebugExe); CompileAndVerify(comp, expectedOutput: ExpectedOutput("42 43"), verify: Verification.Skipped).VerifyDiagnostics(); string expectedOperationTree = """ IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = new C() ... ld = 0 } };') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C) (Syntax: '_ = new C() ... eld = 0 } }') Left: IDiscardOperation (Symbol: C _) (OperationKind.Discard, Type: C) (Syntax: '_') Right: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C() { [ ... eld = 0 } }') Arguments(0) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C) (Syntax: '{ [42, $"{4 ... eld = 0 } }') Initializers(1): IMemberInitializerOperation (OperationKind.MemberInitializer, Type: D) (Syntax: '[42, $"{43} ... Field = 0 }') InitializedMember: IPropertyReferenceOperation: D CExt.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i, CustomHandler h] { get; } (OperationKind.PropertyReference, Type: D) (Syntax: '[42, $"{43}"]') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: '[42, $"{43}"]') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '42') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: h) (OperationKind.Argument, Type: null) (Syntax: '$"{43}"') IInterpolatedStringHandlerCreationOperation (HandlerAppendCallsReturnBool: False, HandlerCreationHasSuccessParameter: False) (OperationKind.InterpolatedStringHandlerCreation, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Creation: IObjectCreationOperation (Constructor: CustomHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount, System.Int32 i)) (OperationKind.ObjectCreation, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: literalLength) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$"{43}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: formattedCount) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$"{43}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '42') IInterpolatedStringHandlerArgumentPlaceholderOperation (ArgumentIndex: 0) (OperationKind.InterpolatedStringHandlerArgumentPlaceholder, Type: null, IsImplicit) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null Content: IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$"{43}"') Parts(1): IInterpolatedStringAppendOperation (OperationKind.InterpolatedStringAppendFormatted, Type: null, IsImplicit) (Syntax: '{43}') AppendCall: IInvocationOperation ( void CustomHandler.AppendFormatted(System.Int32 i)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{43}') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: InterpolatedStringHandler) (OperationKind.InstanceReference, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '43') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 43) (Syntax: '43') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: D) (Syntax: '{ Field = 0 }') Initializers(1): ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'Field = 0') Left: IFieldReferenceOperation: System.Int32 D.Field (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'Field') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: D, IsImplicit) (Syntax: 'Field') Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') """; VerifyOperationTreeAndDiagnosticsForTest<ExpressionStatementSyntax>(comp, expectedOperationTree, expectedDiagnostics: []); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var mainDeclaration = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().First(); var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(mainDeclaration.Body, model); ControlFlowGraphVerifier.VerifyGraph(comp, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] Block[B1] - Block Predecessors: [B0] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new C() { [ ... eld = 0 } }') Value: IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C() { [ ... eld = 0 } }') Arguments(0) Initializer: null Next (Regular) Block[B2] Entering: {R2} .locals {R2} { CaptureIds: [1] [2] Block[B2] - Block Predecessors: [B1] Statements (4) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '42') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '$"{43}"') Value: IObjectCreationOperation (Constructor: CustomHandler..ctor(System.Int32 literalLength, System.Int32 formattedCount, System.Int32 i)) (OperationKind.ObjectCreation, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: literalLength) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$"{43}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: formattedCount) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '$"{43}"') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '42') IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, Constant: 42, IsImplicit) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Initializer: null IInvocationOperation ( void CustomHandler.AppendFormatted(System.Int32 i)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '{43}') Instance Receiver: IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '43') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 43) (Syntax: '43') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'Field = 0') Left: IFieldReferenceOperation: System.Int32 D.Field (OperationKind.FieldReference, Type: System.Int32) (Syntax: 'Field') Instance Receiver: IPropertyReferenceOperation: D CExt.<G>$9794DAFCCB9E752B29BFD6350ADA77F2.this[System.Int32 i, CustomHandler h] { get; } (OperationKind.PropertyReference, Type: D) (Syntax: '[42, $"{43}"]') Instance Receiver: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'new C() { [ ... eld = 0 } }') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null) (Syntax: '42') IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32, Constant: 42, IsImplicit) (Syntax: '42') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: h) (OperationKind.Argument, Type: null) (Syntax: '$"{43}"') IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: CustomHandler, IsImplicit) (Syntax: '$"{43}"') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') Next (Regular) Block[B3] Leaving: {R2} } Block[B3] - Block Predecessors: [B2] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = new C() ... ld = 0 } };') Expression: ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C) (Syntax: '_ = new C() ... eld = 0 } }') Left: IDiscardOperation (Symbol: C _) (OperationKind.Discard, Type: C) (Syntax: '_') Right: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'new C() { [ ... eld = 0 } }') Next (Regular) Block[B4] Leaving: {R1} } Block[B4] - Exit Predecessors: [B3] Statements (0) """, graph, symbol); } [Fact] public void UseSite_01() { // list-pattern with extension(Missing).Length var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing", targetFramework: TargetFramework.Net100) .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(Missing m) { public int Length => 1; } extension(object o) { public int this[System.Index i] => throw null; public int this[System.Range r] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o is [var x, .. var y]; namespace Outer { public static class E2 { extension(object o) { public int Length => 1; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,10): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x, .. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 10)); } [Fact] public void UseSite_02() { // list-pattern with extension(Missing).this[Index] var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing", targetFramework: TargetFramework.Net100) .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(Missing m) { public int this[System.Index i] => throw null; } extension(object o) { public int Length => 1; public int this[System.Range r] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o is [var x, .. var y]; namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,10): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x, .. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 10), // (4,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 18)); } [Fact] public void UseSite_03() { // list-pattern with extension(Missing).this[Range] var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing", targetFramework: TargetFramework.Net100) .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(Missing m) { public int this[System.Range r] => throw null; } extension(object o) { public int Length => 1; public int this[System.Index i] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o is [var x, .. var y]; namespace Outer { public static class E2 { extension(object o) { public int this[System.Range r] => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,10): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x, .. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 10), // (4,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 18)); } [Fact] public void UseSite_04() { // list-pattern with extension(Missing).this[int] var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing", targetFramework: TargetFramework.Net100) .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(Missing m) { public int this[int i] => throw null; } extension(object o) { public int Length => 1; public int this[System.Range r] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o is [var x, .. var y]; namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,10): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x, .. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 10), // (4,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 18)); } [Fact] public void UseSite_05() { // list-pattern with extension(Missing).Slice(int, int) var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing", targetFramework: TargetFramework.Net100) .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(Missing m) { public object Slice(int i, int j) => throw null; } extension(object o) { public int Length => 1; public int this[System.Index i] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o is [var x, .. var y]; namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,18): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_BadIndexLHS, ".. var y").WithArguments("object").WithLocation(4, 18), // (4,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 18)); } [Fact] public void UseSite_06() { // list-pattern with extension(object).this[Index] returning Missing var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing", targetFramework: TargetFramework.Net100) .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(object o) { public int Length => 1; public Missing this[System.Index i] => throw null; public int this[System.Range r] => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o is [var x, .. var y]; namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,10): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[var x, .. var y]").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 10), // (4,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 18)); } [Fact] public void UseSite_07() { // list-pattern with extension(object).Slice(int, int) returning Missing var missing_cs = """ public class Missing { } """; var missingRef = CreateCompilation(missing_cs, assemblyName: "missing", targetFramework: TargetFramework.Net100) .EmitToImageReference(); var lib2_cs = """ public static class E1 { extension(object o) { public int Length => 1; public int this[System.Index i] => throw null; public Missing Slice(int i, int j) => throw null; } } """; var lib2Ref = CreateCompilation(lib2_cs, references: [missingRef], targetFramework: TargetFramework.Net100) .EmitToImageReference(); var source = """ using Outer; var o = new object(); _ = o is [var x, .. var y]; namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] => throw null; } } } """; var compilation = CreateCompilation(source, references: [lib2Ref], targetFramework: TargetFramework.Net100); compilation.VerifyEmitDiagnostics( // (4,18): error CS0021: Cannot apply indexing with [] to an expression of type 'object' // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_BadIndexLHS, ".. var y").WithArguments("object").WithLocation(4, 18), // (4,18): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // _ = o is [var x, .. var y]; Diagnostic(ErrorCode.ERR_NoTypeDef, ".. var y").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(4, 18)); } [Fact] public void LengthPattern_NegativeLengthTest() { var src = @" object o = new object(); _ = o is { Length: -1 }; // 1 _ = o is { Length: -1 or 1 }; // 2 _ = o is { Length: -1 } or { Length: 1 }; // 3 _ = o switch // 4 { { Length: -1 } => 0, // 5 }; _ = o switch // 6 { { Length: -1 or 1 } => 0, }; _ = o switch // 7 { { Length: -1 } or { Length: 1 } => 0, }; _ = o switch // 8 { { Length: -1 } => 0, // 9 { Length: 1 } => 0, }; public static class E { extension(object o) { public int Length => 0; public int this[System.Index i] => 0; } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyDiagnostics( // 0.cs(3,5): error CS8518: An expression of type 'object' can never match the provided pattern. // _ = o is { Length: -1 }; // 1 Diagnostic(ErrorCode.ERR_IsPatternImpossible, "o is { Length: -1 }").WithArguments("object").WithLocation(3, 5), // 0.cs(7,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. // _ = o switch // 4 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("_").WithLocation(7, 7), // 0.cs(9,5): error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match. // { Length: -1 } => 0, // 5 Diagnostic(ErrorCode.ERR_SwitchArmSubsumed, "{ Length: -1 }").WithLocation(9, 5), // 0.cs(12,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 0 }' is not covered. // _ = o switch // 6 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 0 }").WithLocation(12, 7), // 0.cs(17,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 0 }' is not covered. // _ = o switch // 7 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 0 }").WithLocation(17, 7), // 0.cs(22,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 0 }' is not covered. // _ = o switch // 8 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 0 }").WithLocation(22, 7), // 0.cs(24,5): error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match. // { Length: -1 } => 0, // 9 Diagnostic(ErrorCode.ERR_SwitchArmSubsumed, "{ Length: -1 }").WithLocation(24, 5) ); } [Fact] public void LengthPattern_NegativeLengthTest_02() { var src = @" int? i = 0; _ = i is { Length: -1 }; // 1 _ = i is { Length: -1 or 1 }; // 2 _ = i is { Length: -1 } or { Length: 1 }; // 3 _ = i switch // 4 { { Length: -1 } => 0, // 5 }; _ = i switch // 6 { { Length: -1 or 1 } => 0, }; _ = i switch // 7 { { Length: -1 } or { Length: 1 } => 0, }; _ = i switch // 8 { { Length: -1 } => 0, // 9 { Length: 1 } => 0, }; public static class E { extension(object o) { public int Length => 0; public int this[System.Index i] => 0; } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyDiagnostics( // 0.cs(3,5): error CS8518: An expression of type 'int?' can never match the provided pattern. // _ = i is { Length: -1 }; // 1 Diagnostic(ErrorCode.ERR_IsPatternImpossible, "i is { Length: -1 }").WithArguments("int?").WithLocation(3, 5), // 0.cs(7,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. // _ = i switch // 4 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("_").WithLocation(7, 7), // 0.cs(9,5): error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match. // { Length: -1 } => 0, // 5 Diagnostic(ErrorCode.ERR_SwitchArmSubsumed, "{ Length: -1 }").WithLocation(9, 5), // 0.cs(12,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 0 }' is not covered. // _ = i switch // 6 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 0 }").WithLocation(12, 7), // 0.cs(17,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 0 }' is not covered. // _ = i switch // 7 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 0 }").WithLocation(17, 7), // 0.cs(22,7): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 0 }' is not covered. // _ = i switch // 8 Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 0 }").WithLocation(22, 7), // 0.cs(24,5): error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match. // { Length: -1 } => 0, // 9 Diagnostic(ErrorCode.ERR_SwitchArmSubsumed, "{ Length: -1 }").WithLocation(24, 5) ); } [Fact] public void LengthPattern_NegativeLengthTest_03() { var src = @" using Outer; object o = new object(); _ = o is { Length: -1 }; // 1 public static class E { extension(object o) { public int Length => 0; } } namespace Outer { public static class E2 { extension(object o) { public int this[System.Index i] => 0; } } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyDiagnostics( // (5,5): error CS8518: An expression of type 'object' can never match the provided pattern. // _ = o is { Length: -1 }; // 1 Diagnostic(ErrorCode.ERR_IsPatternImpossible, "o is { Length: -1 }").WithArguments("object").WithLocation(5, 5)); } [Fact] public void CollectionSpread_01() { var src = """ C c = new C(); int[] i = [0, .. c]; foreach (var x in i) System.Console.Write(x); public class C : System.Collections.Generic.IEnumerable<int> { public System.Collections.Generic.IEnumerator<int> GetEnumerator() { yield return 3; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); } public static class E { extension(object o) { public int Length => 1; } } """; // Note: the Length extension is not used to calculate the size of the collection var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("03"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 54 (0x36) .maxstack 3 .locals init (C V_0, //c int[] V_1, int V_2) IL_0000: newobj "C..ctor()" IL_0005: stloc.0 IL_0006: newobj "System.Collections.Generic.List<int>..ctor()" IL_000b: dup IL_000c: ldc.i4.0 IL_000d: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_0012: dup IL_0013: ldloc.0 IL_0014: callvirt "void System.Collections.Generic.List<int>.AddRange(System.Collections.Generic.IEnumerable<int>)" IL_0019: callvirt "int[] System.Collections.Generic.List<int>.ToArray()" IL_001e: stloc.1 IL_001f: ldc.i4.0 IL_0020: stloc.2 IL_0021: br.s IL_002f IL_0023: ldloc.1 IL_0024: ldloc.2 IL_0025: ldelem.i4 IL_0026: call "void System.Console.Write(int)" IL_002b: ldloc.2 IL_002c: ldc.i4.1 IL_002d: add IL_002e: stloc.2 IL_002f: ldloc.2 IL_0030: ldloc.1 IL_0031: ldlen IL_0032: conv.i4 IL_0033: blt.s IL_0023 IL_0035: ret } """); } [Fact, CompilerTrait(CompilerFeature.NullableReferenceTypes)] public void LoopWithPatternDeclaration_ListPattern() { var source = """ #nullable enable object? o = new object(); for (var x = 0; x < 10; x++) { var a = Infer(o); if (a is [var z]) { z.ToString(); } o = null; } C<T> Infer<T>(T t) => throw null!; public class C<T> { } public static class E { extension<T>(C<T> t) { public int Length => 1; public T this[System.Index i] => throw null!; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics( // (10,9): warning CS8602: Dereference of a possibly null reference. // z.ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "z").WithLocation(10, 9)); } [Fact, CompilerTrait(CompilerFeature.NullableReferenceTypes)] [WorkItem("https://github.com/dotnet/roslyn/issues/82802")] public void LoopWithPatternDeclaration_SlicePattern() { var source = """ #nullable enable object? o = new object(); for (var x = 0; x < 10; x++) { var a = Infer(o); if (a is [.. var z]) { z.ToString(); } o = null; } C<T> Infer<T>(T t) => throw null!; public class C<T> { } public static class E { extension<T>(C<T> t) { public int Length => 1; public T this[System.Index i] => throw null!; public T this[System.Range i] => throw null!; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); // Note: slice patterns currently assume that returned slices are not-null comp.VerifyEmitDiagnostics(); } [Fact, CompilerTrait(CompilerFeature.NullableReferenceTypes)] [WorkItem("https://github.com/dotnet/roslyn/issues/82802")] public void LoopWithPatternDeclaration_SlicePattern_SliceMethod() { var source = """ #nullable enable object? o = new object(); for (var x = 0; x < 10; x++) { var a = Infer(o); if (a is [.. var z]) { z.ToString(); } o = null; } C<T> Infer<T>(T t) => throw null!; public class C<T> { } public static class E { extension<T>(C<T> t) { public int Length => 1; public T this[System.Index i] => throw null!; public T Slice(int i, int j) => throw null!; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); // Note: slice patterns currently assume that returned slices are not-null comp.VerifyEmitDiagnostics(); } [Fact] public void ExtensionSlice_01() { // slice on Span with missing Slice, classic extension Slice method var src = """ #pragma warning disable CS0436 // The type 'Span<T>' in '' conflicts with the imported type 'Span<T>' System.Span<int> s = default; _ = s[1..^1]; static class E { public static System.Span<int> Slice(this System.Span<int> s, int i, int j) { System.Console.Write("ran"); return s; } } namespace System { public readonly ref struct Span<T> { public Span() { } public int Length => 3; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void ExtensionSlice_02() { // slice on Span with missing Slice, extension Slice var src = """ #pragma warning disable CS0436 // The type 'Span<T>' in '' conflicts with the imported type 'Span<T>' System.Span<int> s = default; _ = s[1..^1]; static class E { extension(System.Span<int> s) { public System.Span<int> Slice(int i, int j) { System.Console.Write("ran"); return s; } } } namespace System { public readonly ref struct Span<T> { public Span() { } public int Length => 3; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void ExtensionSlice_03() { // slice with open-ended range on Span with missing Slice(int, int), classic extension Slice method var src = """ #pragma warning disable CS0436 // The type 'Span<T>' in '' conflicts with the imported type 'Span<T>' System.Span<int> s = default; _ = s[1..]; static class E { public static System.Span<int> Slice(this System.Span<int> s, int i, int j) { System.Console.Write("ran"); return s; } } namespace System { public readonly ref struct Span<T> { public Span() { } public int Length => 3; public System.Span<int> Slice(int start) => throw null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void ExtensionSlice_04() { // slice with open-ended range on Span with missing Slice(int, int), extension Slice var src = """ #pragma warning disable CS0436 // The type 'Span<T>' in '' conflicts with the imported type 'Span<T>' System.Span<int> s = default; _ = s[1..]; static class E { extension(System.Span<int> s) { public System.Span<int> Slice(int i, int j) { System.Console.Write("ran"); return s; } } } namespace System { public readonly ref struct Span<T> { public Span() { } public int Length => 3; public System.Span<int> Slice(int start) => throw null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void Array_01() { var src = """ public class C { public static void M(int[] i, C c) { _ = i[c]; } } public static class E { extension(int[] i) { public int this[C c] => throw null; } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (5,15): error CS0029: Cannot implicitly convert type 'C' to 'int' // _ = i[c]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "c").WithArguments("C", "int").WithLocation(5, 15)); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_01() { // sibling of IndexerAccess_Get_LValueReceiver_01 // struct lvalue receiver passed by value, extension implicit this[Index] var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } public struct S1 { public int F1; public void Test2() { _ = this[Program.GetIndex()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 46 (0x2e) .maxstack 2 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: ldobj "S1" IL_0015: call "int E.get_Length(S1)" IL_001a: call "int System.Index.GetOffset(int)" IL_001f: stloc.1 IL_0020: ldloc.0 IL_0021: ldobj "S1" IL_0026: ldloc.1 IL_0027: call "int E.get_Item(S1, int)" IL_002c: pop IL_002d: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 42 (0x2a) .maxstack 2 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: ldobj "S1" IL_0011: call "int E.get_Length(S1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: stloc.1 IL_001c: ldloc.0 IL_001d: ldobj "S1" IL_0022: ldloc.1 IL_0023: call "int E.get_Item(S1, int)" IL_0028: pop IL_0029: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_01_02() { // sibling of IndexerAccess_Get_LValueReceiver_01 // struct lvalue receiver passed by value, extension this[int] + instance Length var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } } } public struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 3; } } public void Test2() { _ = this[Program.GetIndex()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 41 (0x29) .maxstack 2 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int S1.Length.get" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldobj "S1" IL_0021: ldloc.1 IL_0022: call "int E.get_Item(S1, int)" IL_0027: pop IL_0028: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 37 (0x25) .maxstack 2 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: call "int S1.Length.get" IL_0011: call "int System.Index.GetOffset(int)" IL_0016: stloc.1 IL_0017: ldloc.0 IL_0018: ldobj "S1" IL_001d: ldloc.1 IL_001e: call "int E.get_Item(S1, int)" IL_0023: pop IL_0024: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_01_03() { // sibling of IndexerAccess_Get_LValueReceiver_01 // struct lvalue receiver passed by value, instance this[int] + extension Length var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } public struct S1 { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); return 0; } } public void Test2() { _ = this[Program.GetIndex()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 39 (0x27) .maxstack 3 .locals init (S1& V_0, System.Index V_1) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: ldobj "S1" IL_0016: call "int E.get_Length(S1)" IL_001b: call "int System.Index.GetOffset(int)" IL_0020: call "int S1.this[int].get" IL_0025: pop IL_0026: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 35 (0x23) .maxstack 3 .locals init (S1& V_0, System.Index V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call "System.Index Program.GetIndex()" IL_0009: stloc.1 IL_000a: ldloca.s V_1 IL_000c: ldloc.0 IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: call "int System.Index.GetOffset(int)" IL_001c: call "int S1.this[int].get" IL_0021: pop IL_0022: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_02(string refKind) { // sibling of IndexerAccess_Get_LValueReceiver_02 // struct lvalue receiver passed by ref, extension implicit this[Index] var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { _ = this[Program.GetIndex()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[Program.GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 34 (0x22) .maxstack 3 .locals init (S1& V_0, System.Index V_1) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: call "int E.get_Length({{refKind}} S1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: call "int E.get_Item({{refKind}} S1, int)" IL_0020: pop IL_0021: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 30 (0x1e) .maxstack 3 .locals init (S1& V_0, System.Index V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call "System.Index Program.GetIndex()" IL_0009: stloc.1 IL_000a: ldloca.s V_1 IL_000c: ldloc.0 IL_000d: call "int E.get_Length({{refKind}} S1)" IL_0012: call "int System.Index.GetOffset(int)" IL_0017: call "int E.get_Item({{refKind}} S1, int)" IL_001c: pop IL_001d: ret } """); var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get => 0; set {} } public int Length => 3; } } struct S1; class Program { static void Test() { _ = default(S1)[^1]; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); if (refKind == "ref") { comp2.VerifyDiagnostics( // (16,13): error CS1510: A ref or out value must be an assignable variable // _ = default(S1)[^1]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 13), // (16,13): error CS1510: A ref or out value must be an assignable variable // _ = default(S1)[^1]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 13)); } else if (refKind == "ref readonly") { comp2.VerifyDiagnostics( // (16,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = default(S1)[^1]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 13), // (16,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = default(S1)[^1]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 13)); } else { comp2.VerifyDiagnostics(); } if (refKind != "ref") { verifier = CompileAndVerify(comp2, verify: Verification.Skipped); verifier.VerifyIL("Program.Test", $$""" { // Code size 24 (0x18) .maxstack 3 .locals init (S1 V_0) IL_0000: ldloca.s V_0 IL_0002: dup IL_0003: initobj "S1" IL_0009: dup IL_000a: call "int E.get_Length({{refKind}} S1)" IL_000f: ldc.i4.1 IL_0010: sub IL_0011: call "int E.get_Item({{refKind}} S1, int)" IL_0016: pop IL_0017: ret } """); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_02_02(string refKind) { // struct lvalue receiver passed by ref, instance Length + extension this[int] var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } } } struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 3; } } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[Program.GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_02_03(string refKind) { // struct lvalue receiver passed by ref, extension Length + instance this[int] var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); return 0; } } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[Program.GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_03() { // sibling of IndexerAccess_Get_LValueReceiver_03 // class lvalue receiver passed by value, extension implicit this[Index] var src = """ static class E { extension(C1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 3; } } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 34 (0x22) .maxstack 3 .locals init (C1 V_0, System.Index V_1) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: call "int E.get_Length(C1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: call "int E.get_Item(C1, int)" IL_0020: pop IL_0021: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_03_02() { // class lvalue receiver passed by value, instance Length + extension this[int] var src = """ static class E { extension(C1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } } } class C1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 3; } } } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_03_03() { // class lvalue receiver passed by value, extension Length + instance this[int] var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 3; } } } } class C1 { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); return 0; } } } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_04() { // sibling of IndexerAccess_Get_LValueReceiver_04 // generic struct lvalue receiver passed by value, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //System.Console.Write(", "); //Program<S1>.F = new S1 { F1 = 3 }; //await Test3<S1>(); //System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) { _ = f[GetIndex()]; } static void Test2<T>(ref T f) where T : struct { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //static async Task Test3<T>() //{ // _ = Program<T>.F[await GetIndexAsync()]; //} //static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 74 (0x4a) .maxstack 2 .locals init (T& V_0, T V_1, T& V_2, T V_3, int V_4, System.Index V_5) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 IL_0003: ldloca.s V_3 IL_0005: initobj "T" IL_000b: ldloc.3 IL_000c: box "T" IL_0011: brtrue.s IL_001e IL_0013: ldloc.2 IL_0014: ldobj "T" IL_0019: stloc.1 IL_001a: ldloca.s V_1 IL_001c: br.s IL_001f IL_001e: ldloc.2 IL_001f: stloc.0 IL_0020: call "System.Index Program.GetIndex()" IL_0025: stloc.s V_5 IL_0027: ldloca.s V_5 IL_0029: ldloc.0 IL_002a: ldobj "T" IL_002f: call "int E.get_Length<T>(T)" IL_0034: call "int System.Index.GetOffset(int)" IL_0039: stloc.s V_4 IL_003b: ldloc.0 IL_003c: ldobj "T" IL_0041: ldloc.s V_4 IL_0043: call "int E.get_Item<T>(T, int)" IL_0048: pop IL_0049: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 42 (0x2a) .maxstack 2 .locals init (T& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: ldobj "T" IL_0011: call "int E.get_Length<T>(T)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: stloc.1 IL_001c: ldloc.0 IL_001d: ldobj "T" IL_0022: ldloc.1 IL_0023: call "int E.get_Item<T>(T, int)" IL_0028: pop IL_0029: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_04_02() { // generic struct lvalue receiver passed by value, instance Length (via interface) + extension this[int] var src = """ interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : IHasLength { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); return 0; } } } } struct S1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program<S1>.F.F1++; return 3; } } } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) where T : IHasLength { _ = f[GetIndex()]; } static void Test2<T>(ref T f) where T : struct, IHasLength { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_04_02_02() { // generic struct lvalue receiver passed by value, instance Length (via interface) + extension this[int], struct constraint var src = """ interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : struct, IHasLength { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); return 0; } } } } struct S1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program<S1>.F.F1++; return 3; } } } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) where T : struct, IHasLength { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_04_02_03() { // generic struct lvalue receiver passed by value, instance Length (via interface) + extension this[int], class constraint var src = """ interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : class, IHasLength { public int this[int i] { get { System.Console.Write($"get:{((C1)(object)x).F1} "); return 0; } } } } class C1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program<C1>.F.F1++; return 3; } } } class Program<T> { public static T F; } class Program { static void Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) where T : class, IHasLength { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<C1>.F.F1} "); Program<C1>.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_04_03() { // generic struct lvalue receiver passed by value, extension Length + instance this[int] (via interface) var src = """ interface IHasIndexer { int this[int i] { get; } } static class E { extension<T>(T x) where T : IHasIndexer { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } } } struct S1 : IHasIndexer { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); return 0; } } } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) where T : IHasIndexer { _ = f[GetIndex()]; } static void Test2<T>(ref T f) where T : struct, IHasIndexer { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndex:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_05() { // sibling of IndexerAccess_Get_LValueReceiver_05 // struct lvalue receiver passed by ref, constrained to struct, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(ref T x) where T : struct { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } static async Task Test3<T>() where T : struct { _ = Program<T>.F[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndexAsync:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 30 (0x1e) .maxstack 3 .locals init (T& V_0, System.Index V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call "System.Index Program.GetIndex()" IL_0009: stloc.1 IL_000a: ldloca.s V_1 IL_000c: ldloc.0 IL_000d: call "int E.get_Length<T>(ref T)" IL_0012: call "int System.Index.GetOffset(int)" IL_0017: call "int E.get_Item<T>(ref T, int)" IL_001c: pop IL_001d: ret } """); var src2 = """ static class E { extension<T>(ref T x) where T : struct { public int this[int i] { get => 0; set {} } public int Length => 3; } } class Program { static void Test<T>() where T : struct { _ = default(T)[^1]; } } namespace NS1 { static class E { extension<T>(in T x) where T : struct { } } } namespace NS2 { static class E { extension<T>(ref readonly T x) where T : struct { } } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); comp2.VerifyDiagnostics( // (14,13): error CS1510: A ref or out value must be an assignable variable // _ = default(T)[^1]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 13), // (14,13): error CS1510: A ref or out value must be an assignable variable // _ = default(T)[^1]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 13), // (22,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(22, 25), // (32,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(ref readonly T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(32, 35) ); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_05_02() { // struct lvalue receiver passed by ref, constrained to struct, instance Length (via interface) + extension this[int] var src = """ using System.Threading.Tasks; interface IHasLength { int Length { get; } } static class E { extension<T>(ref T x) where T : struct, IHasLength { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); return 0; } } } } struct S1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program<S1>.F.F1++; return 3; } } } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct, IHasLength { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } static async Task Test3<T>() where T : struct, IHasLength { _ = Program<T>.F[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndexAsync:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_05_03() { // struct lvalue receiver passed by ref, constrained to struct, extension Length + instance this[int] (via interface) var src = """ using System.Threading.Tasks; interface IHasIndexer { int this[int i] { get; } } static class E { extension<T>(ref T x) where T : struct, IHasIndexer { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } } } struct S1 : IHasIndexer { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); return 0; } } } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct, IHasIndexer { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } static async Task Test3<T>() where T : struct, IHasIndexer { _ = Program<T>.F[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 final:5, GetIndexAsync:3 length:4 get:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_06() { // sibling of IndexerAccess_Get_LValueReceiver_06 // generic class lvalue receiver passed by value, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { get { System.Console.Write($"get:{((C1)(object)x).F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 3; } } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { static async Task Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(", "); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //System.Console.Write(", "); //Program<C1>.F = new C1 { F1 = 3 }; //await Test3<C1>(); //System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) { _ = f[GetIndex()]; } static void Test2<T>(ref T f) where T : class { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return ^1; } // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //static async Task Test3<T>() //{ // _ = Program<T>.F[await GetIndexAsync()]; //} //static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 74 (0x4a) .maxstack 2 .locals init (T& V_0, T V_1, T& V_2, T V_3, int V_4, System.Index V_5) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 IL_0003: ldloca.s V_3 IL_0005: initobj "T" IL_000b: ldloc.3 IL_000c: box "T" IL_0011: brtrue.s IL_001e IL_0013: ldloc.2 IL_0014: ldobj "T" IL_0019: stloc.1 IL_001a: ldloca.s V_1 IL_001c: br.s IL_001f IL_001e: ldloc.2 IL_001f: stloc.0 IL_0020: call "System.Index Program.GetIndex()" IL_0025: stloc.s V_5 IL_0027: ldloca.s V_5 IL_0029: ldloc.0 IL_002a: ldobj "T" IL_002f: call "int E.get_Length<T>(T)" IL_0034: call "int System.Index.GetOffset(int)" IL_0039: stloc.s V_4 IL_003b: ldloc.0 IL_003c: ldobj "T" IL_0041: ldloc.s V_4 IL_0043: call "int E.get_Item<T>(T, int)" IL_0048: pop IL_0049: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 35 (0x23) .maxstack 3 .locals init (T V_0, System.Index V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call "System.Index Program.GetIndex()" IL_000e: stloc.1 IL_000f: ldloca.s V_1 IL_0011: ldloc.0 IL_0012: call "int E.get_Length<T>(T)" IL_0017: call "int System.Index.GetOffset(int)" IL_001c: call "int E.get_Item<T>(T, int)" IL_0021: pop IL_0022: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_06_02() { // generic class lvalue receiver passed by value, instance Length (via interface) + extension implicit this[Index] var src = """ interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : IHasLength { public int this[int i] { get { System.Console.Write($"get:{((C1)(object)x).F1} "); return 0; } } } } class C1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 3; } } } class Program<T> { public static T F; } class Program { static void Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(", "); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) where T : IHasLength { _ = f[GetIndex()]; } static void Test2<T>(ref T f) where T : class, IHasLength { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_06_03() { // generic class lvalue receiver passed by value, extension Length + instance this[Index] (via interface) var src = """ interface IHasIndexer { int this[int i] { get; } } static class E { extension<T>(T x) where T : IHasIndexer { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 3; } } } } class C1 : IHasIndexer { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); return 0; } } } class Program<T> { public static T F; } class Program { static void Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(", "); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) where T : IHasIndexer { _ = f[GetIndex()]; } static void Test2<T>(ref T f) where T : class, IHasIndexer { _ = f[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_01() { // sibling of IndexerAccess_Get_RValueReceiver_01 // struct rvalue receiver passed by value, extension implicit this[Index] var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 3; } } } } public struct S1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetIndex()]; } static S1 GetS1() => new S1 { F1 = State }; public static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 49 (0x31) .maxstack 2 .locals init (S1& V_0, S1 V_1, int V_2, System.Index V_3) IL_0000: nop IL_0001: call "S1 Program.GetS1()" IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: stloc.0 IL_000a: call "System.Index Program.GetIndex()" IL_000f: stloc.3 IL_0010: ldloca.s V_3 IL_0012: ldloc.0 IL_0013: ldobj "S1" IL_0018: call "int E.get_Length(S1)" IL_001d: call "int System.Index.GetOffset(int)" IL_0022: stloc.2 IL_0023: ldloc.0 IL_0024: ldobj "S1" IL_0029: ldloc.2 IL_002a: call "int E.get_Item(S1, int)" IL_002f: pop IL_0030: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_02(string refKind) { // sibling of IndexerAccess_Get_RValueReceiver_02 // struct rvalue receiver passed by ref, extension implicit this[Index] var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 3; } } } } struct S1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetIndex()]; } static S1 GetS1() => new S1 { F1 = State }; public static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithAllowUnsafe(true), targetFramework: TargetFramework.Net100); if (refKind == "ref") { comp.VerifyEmitDiagnostics( // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetS1()[GetIndex()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetS1()").WithLocation(28, 13), // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetS1()[GetIndex()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetS1()").WithLocation(28, 13)); return; } else if (refKind == "ref readonly") { comp.VerifyEmitDiagnostics( // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetIndex()]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13), // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetIndex()]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13)); } else { comp.VerifyEmitDiagnostics(); } var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped); verifier.VerifyIL("Program.Test", $$""" { // Code size 37 (0x25) .maxstack 3 .locals init (S1& V_0, S1 V_1, System.Index V_2) IL_0000: nop IL_0001: call "S1 Program.GetS1()" IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: call "System.Index Program.GetIndex()" IL_0010: stloc.2 IL_0011: ldloca.s V_2 IL_0013: ldloc.0 IL_0014: call "int E.get_Length({{refKind}} S1)" IL_0019: call "int System.Index.GetOffset(int)" IL_001e: call "int E.get_Item({{refKind}} S1, int)" IL_0023: pop IL_0024: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_03() { // sibling of IndexerAccess_Get_RValueReceiver_03 // class rvalue receiver passed by value, extension implicit this[Index] var src = """ static class E { extension(C1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 3; } } } } class C1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetC1()[GetIndex()]; } static C1 GetC1() => new C1 { F1 = State }; static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 34 (0x22) .maxstack 3 .locals init (C1 V_0, System.Index V_1) IL_0000: nop IL_0001: call "C1 Program.GetC1()" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: call "int E.get_Length(C1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: call "int E.get_Item(C1, int)" IL_0020: pop IL_0021: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_04() { // sibling of IndexerAccess_Get_RValueReceiver_04 // generic struct rvalue receiver passed by value, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.State++; return 3; } } } } struct S1 { public int F1; } class Program { public static int State; static async Task Main() { State = 3; Test1<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<S1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new S1 { F1 = State }; static void Test1<T>() { _ = GetT<T>()[GetIndex()]; } static void Test2<T>() where T : struct { _ = GetT<T>()[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } static async Task Test3<T>() { _ = GetT<T>()[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{State} "); State++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5, GetIndexAsync:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_04_02() { // generic struct rvalue receiver passed by value, instance Length (via interface) + extension implicit this[Index] var src = """ using System.Threading.Tasks; interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : IHasLength { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); return 0; } } } } struct S1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.State++; return 3; } } } class Program { public static int State; static async Task Main() { State = 3; Test1<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<S1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() where T : IHasLength => (T)(object)new S1 { F1 = State }; static void Test1<T>() where T : IHasLength { _ = GetT<T>()[GetIndex()]; } static void Test2<T>() where T : struct, IHasLength { _ = GetT<T>()[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } static async Task Test3<T>() where T : IHasLength { _ = GetT<T>()[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{State} "); State++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5, GetIndexAsync:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_04_03() { // generic struct rvalue receiver passed by value, extension Length + instance this[Index] (via interface) var src = """ using System.Threading.Tasks; interface IHasIndexer { int this[int i] { get; } } static class E { extension<T>(T x) where T : IHasIndexer { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.State++; return 3; } } } } struct S1 : IHasIndexer { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); return 0; } } } class Program { public static int State; static async Task Main() { State = 3; Test1<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<S1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() where T : IHasIndexer => (T)(object)new S1 { F1 = State }; static void Test1<T>() where T : IHasIndexer { _ = GetT<T>()[GetIndex()]; } static void Test2<T>() where T : struct, IHasIndexer { _ = GetT<T>()[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } static async Task Test3<T>() where T : IHasIndexer { _ = GetT<T>()[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{State} "); State++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5, GetIndexAsync:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_LValueReceiver_04_04() { // generic struct lvalue receiver passed by value, extension implicit this[Index], object creation var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public Item this[int i] { get { System.Console.Write("get"); return new Item(); } } public int Length { get { System.Console.Write("length "); return 3; } } } } class Item { public int Property { get; set; } } struct S1 { } class Program { static async Task Main() { _ = Test1<S1>(); System.Console.Write(", "); _ = Test2<S1>(); System.Console.Write(", "); _ = await Test3<S1>(); } static T Test1<T>() where T : new() { return new T() { [GetIndex()] = { Property = 42 } }; } static T Test2<T>() where T : struct { return new T() { [GetIndex()] = { Property = 42 } }; } static System.Index GetIndex() { System.Console.Write("GetIndex "); return ^1; } static async Task<T> Test3<T>() where T : new() { return new T() { [await GetIndexAsync()] = { Property = 42 } }; } static async Task<System.Index> GetIndexAsync() { System.Console.Write("GetIndexAsync "); await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex length get, GetIndex length get, GetIndexAsync length get"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 93 (0x5d) .maxstack 2 .locals init (T V_0, int V_1, System.Index V_2, T V_3, T& V_4, int V_5, T V_6, T V_7) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length<T>(T)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloca.s V_0 IL_001d: stloc.s V_4 IL_001f: ldloca.s V_6 IL_0021: initobj "T" IL_0027: ldloc.s V_6 IL_0029: box "T" IL_002e: brtrue.s IL_003c IL_0030: ldloc.s V_4 IL_0032: ldobj "T" IL_0037: stloc.3 IL_0038: ldloca.s V_3 IL_003a: br.s IL_003e IL_003c: ldloc.s V_4 IL_003e: ldloc.1 IL_003f: stloc.s V_5 IL_0041: ldobj "T" IL_0046: ldloc.s V_5 IL_0048: call "Item E.get_Item<T>(T, int)" IL_004d: ldc.i4.s 42 IL_004f: callvirt "void Item.Property.set" IL_0054: nop IL_0055: ldloc.0 IL_0056: stloc.s V_7 IL_0058: br.s IL_005a IL_005a: ldloc.s V_7 IL_005c: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 58 (0x3a) .maxstack 2 .locals init (T V_0, System.Index V_1, T& V_2, int V_3, T V_4) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.1 IL_000d: ldloca.s V_1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length<T>(T)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: ldloca.s V_0 IL_001c: stloc.2 IL_001d: stloc.3 IL_001e: ldloc.2 IL_001f: ldobj "T" IL_0024: ldloc.3 IL_0025: call "Item E.get_Item<T>(T, int)" IL_002a: ldc.i4.s 42 IL_002c: callvirt "void Item.Property.set" IL_0031: nop IL_0032: ldloc.0 IL_0033: stloc.s V_4 IL_0035: br.s IL_0037 IL_0037: ldloc.s V_4 IL_0039: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_05() { // sibling of IndexerAccess_Get_RValueReceiver_05 // struct rvalue receiver passed by ref, constrained to struct, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(ref T x) where T : struct { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); return 0; } } public int Length => 3; } } struct S1 { public int F1; } class Program { static async Task Main() { Test2<S1>(); System.Console.Write(", "); await Test3<S1>(); } static void Test2<T>() where T : struct { _ = GetT<T>()[GetIndex()]; } static T GetT<T>() => (T)(object)new S1 { F1 = 3 }; static System.Index GetIndex() => ^1; static async Task Test3<T>() where T : struct { _ = GetT<T>()[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[GetIndex()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(28, 13), // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[GetIndex()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(28, 13), // (37,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[await GetIndexAsync()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(37, 13), // (37,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[await GetIndexAsync()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(37, 13)); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_06() { // sibling of IndexerAccess_Get_RValueReceiver_06 // generic class rvalue receiver passed by value, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { get { System.Console.Write($"get:{((C1)(object)x).F1} "); return 0; } } public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program.State++; return 3; } } } } class C1 { public int F1; } class Program { public static int State; static async Task Main() { State = 3; Test1<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<C1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new C1 { F1 = State }; static void Test1<T>() { _ = GetT<T>()[GetIndex()]; } static void Test2<T>() where T : class { _ = GetT<T>()[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } static async Task Test3<T>() { _ = GetT<T>()[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{State} "); State++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5, GetIndexAsync:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_06_02() { // generic class rvalue receiver passed by value, instance Length (via interface) + extension implicit this[Index] var src = """ using System.Threading.Tasks; interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : IHasLength { public int this[int i] { get { System.Console.Write($"get:{((C1)(object)x).F1} "); return 0; } } } } class C1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.State++; return 3; } } } class Program { public static int State; static async Task Main() { State = 3; Test1<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<C1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() where T : IHasLength => (T)(object)new C1 { F1 = State }; static void Test1<T>() where T : IHasLength { _ = GetT<T>()[GetIndex()]; } static void Test2<T>() where T : class, IHasLength { _ = GetT<T>()[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } static async Task Test3<T>() where T : IHasLength { _ = GetT<T>()[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{State} "); State++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5, GetIndexAsync:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Get_RValueReceiver_06_03() { // generic class rvalue receiver passed by value, extension Length + instance this[Index] (via interface) var src = """ using System.Threading.Tasks; interface IHasIndexer { int this[int i] { get; } } static class E { extension<T>(T x) where T : IHasIndexer { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program.State++; return 3; } } } } class C1 : IHasIndexer { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); return 0; } } } class Program { public static int State; static async Task Main() { State = 3; Test1<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<C1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() where T : IHasIndexer => (T)(object)new C1 { F1 = State }; static void Test1<T>() where T : IHasIndexer { _ = GetT<T>()[GetIndex()]; } static void Test2<T>() where T : class, IHasIndexer { _ = GetT<T>()[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } static async Task Test3<T>() where T : IHasIndexer { _ = GetT<T>()[await GetIndexAsync()]; } static async Task<System.Index> GetIndexAsync() { System.Console.Write($"GetIndexAsync:{State} "); State++; await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 final:5, GetIndex:3 length:3 get:3 final:5, GetIndexAsync:3 length:3 get:3 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_01() { // sibling of IndexerAccess_Get_LValueReceiver_01 // struct lvalue receiver passed by value, extension Length + extension Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return 0; } } } public struct S1 { public int F1; public void Test2() { _ = this[Program.GetRange()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetRange()]; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRange:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 76 (0x4c) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: stloc.1 IL_0018: ldloca.s V_0 IL_001a: call "System.Index System.Range.Start.get" IL_001f: stloc.s V_4 IL_0021: ldloca.s V_4 IL_0023: ldloc.1 IL_0024: call "int System.Index.GetOffset(int)" IL_0029: stloc.2 IL_002a: ldloca.s V_0 IL_002c: call "System.Index System.Range.End.get" IL_0031: stloc.s V_4 IL_0033: ldloca.s V_4 IL_0035: ldloc.1 IL_0036: call "int System.Index.GetOffset(int)" IL_003b: ldloc.2 IL_003c: sub IL_003d: stloc.3 IL_003e: ldobj "S1" IL_0043: ldloc.2 IL_0044: ldloc.3 IL_0045: call "int E.Slice(S1, int, int)" IL_004a: pop IL_004b: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 72 (0x48) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: call "int E.get_Length(S1)" IL_0013: stloc.1 IL_0014: ldloca.s V_0 IL_0016: call "System.Index System.Range.Start.get" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: ldloc.1 IL_0020: call "int System.Index.GetOffset(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_0 IL_0028: call "System.Index System.Range.End.get" IL_002d: stloc.s V_4 IL_002f: ldloca.s V_4 IL_0031: ldloc.1 IL_0032: call "int System.Index.GetOffset(int)" IL_0037: ldloc.2 IL_0038: sub IL_0039: stloc.3 IL_003a: ldobj "S1" IL_003f: ldloc.2 IL_0040: ldloc.3 IL_0041: call "int E.Slice(S1, int, int)" IL_0046: pop IL_0047: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_01_02() { // struct lvalue receiver passed by value, instance Length + extension Slice var src = """ static class E { extension(S1 x) { public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return 0; } } } public struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 5; } } public void Test() { _ = this[Program.GetRange()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetRange()]; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRange:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_01_03() { // struct lvalue receiver passed by value, extension Length + instance Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } } } public struct S1 { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F.F1++; return 0; } public void Test() { _ = this[Program.GetRange()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetRange()]; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRange:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_02(string refKind) { // sibling of IndexerAccess_Get_LValueReceiver_02 / ImplicitIndexIndexerAccess_Get_LValueReceiver_02 // struct lvalue receiver passed by ref, extension Length + extension Slice var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); return 0; } } } struct S1 { public int F1; public void Test2() { _ = this[Program.GetRange()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[Program.GetRange()]; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:5, GetRange:3 length:4 Slice:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 66 (0x42) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length({{refKind}} S1)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "int E.Slice({{refKind}} S1, int, int)" IL_0040: pop IL_0041: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 62 (0x3e) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: call "int E.get_Length({{refKind}} S1)" IL_000e: stloc.1 IL_000f: ldloca.s V_0 IL_0011: call "System.Index System.Range.Start.get" IL_0016: stloc.s V_4 IL_0018: ldloca.s V_4 IL_001a: ldloc.1 IL_001b: call "int System.Index.GetOffset(int)" IL_0020: stloc.2 IL_0021: ldloca.s V_0 IL_0023: call "System.Index System.Range.End.get" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: ldloc.1 IL_002d: call "int System.Index.GetOffset(int)" IL_0032: ldloc.2 IL_0033: sub IL_0034: stloc.3 IL_0035: ldloc.2 IL_0036: ldloc.3 IL_0037: call "int E.Slice({{refKind}} S1, int, int)" IL_003c: pop IL_003d: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_02_02() { // struct lvalue receiver passed by ref, instance Length + extension Slice var src = """ static class E { extension(ref S1 x) { public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); return 0; } } } struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 5; } } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[Program.GetRange()]; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_02_03() { // struct lvalue receiver passed by ref, extension Length + instance Slice var src = """ static class E { extension(ref S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } } } struct S1 { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); return 0; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[Program.GetRange()]; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_03() { // sibling of IndexerAccess_Get_LValueReceiver_03 // class lvalue receiver passed by value, extension Length + extension Slice var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 66 (0x42) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length(C1)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "int E.Slice(C1, int, int)" IL_0040: pop IL_0041: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_03_02() { // class lvalue receiver passed by value, instance Length + extension Slice var src = """ static class E { extension(C1 x) { public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } } } class C1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_03_03() { // class lvalue receiver passed by value, extension Length + instance Slice var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } } } class C1 { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_04() { // sibling of IndexerAccess_Get_LValueReceiver_04 // generic struct lvalue receiver passed by value, extension Length + extension Slice var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) { _ = f[GetRange()]; } static void Test2<T>(ref T f) where T : struct { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRange:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 107 (0x6b) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, System.Index V_7) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloca.s V_6 IL_0005: initobj "T" IL_000b: ldloc.s V_6 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.1 IL_0015: ldobj "T" IL_001a: stloc.0 IL_001b: ldloca.s V_0 IL_001d: br.s IL_0020 IL_001f: ldloc.1 IL_0020: call "System.Range Program.GetRange()" IL_0025: stloc.2 IL_0026: dup IL_0027: ldobj "T" IL_002c: call "int E.get_Length<T>(T)" IL_0031: stloc.3 IL_0032: ldloca.s V_2 IL_0034: call "System.Index System.Range.Start.get" IL_0039: stloc.s V_7 IL_003b: ldloca.s V_7 IL_003d: ldloc.3 IL_003e: call "int System.Index.GetOffset(int)" IL_0043: stloc.s V_4 IL_0045: ldloca.s V_2 IL_0047: call "System.Index System.Range.End.get" IL_004c: stloc.s V_7 IL_004e: ldloca.s V_7 IL_0050: ldloc.3 IL_0051: call "int System.Index.GetOffset(int)" IL_0056: ldloc.s V_4 IL_0058: sub IL_0059: stloc.s V_5 IL_005b: ldobj "T" IL_0060: ldloc.s V_4 IL_0062: ldloc.s V_5 IL_0064: call "int E.Slice<T>(T, int, int)" IL_0069: pop IL_006a: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 72 (0x48) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "T" IL_000e: call "int E.get_Length<T>(T)" IL_0013: stloc.1 IL_0014: ldloca.s V_0 IL_0016: call "System.Index System.Range.Start.get" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: ldloc.1 IL_0020: call "int System.Index.GetOffset(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_0 IL_0028: call "System.Index System.Range.End.get" IL_002d: stloc.s V_4 IL_002f: ldloca.s V_4 IL_0031: ldloc.1 IL_0032: call "int System.Index.GetOffset(int)" IL_0037: ldloc.2 IL_0038: sub IL_0039: stloc.3 IL_003a: ldobj "T" IL_003f: ldloc.2 IL_0040: ldloc.3 IL_0041: call "int E.Slice<T>(T, int, int)" IL_0046: pop IL_0047: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_04_02() { // generic struct lvalue receiver passed by value, instance Length (via interface) + extension Slice var src = """ interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : IHasLength { public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } } } struct S1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program<S1>.F.F1++; return 5; } } } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) where T : IHasLength { _ = f[GetRange()]; } static void Test2<T>(ref T f) where T : struct, IHasLength { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRange:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_04_03() { // generic struct lvalue receiver passed by value, extension Length + instance Slice (via interface) var src = """ interface IHasSlice { int Slice(int start, int length); } static class E { extension<T>(T x) where T : IHasSlice { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } } } struct S1 : IHasSlice { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program<S1>.F.F1++; return 0; } } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) where T : IHasSlice { _ = f[GetRange()]; } static void Test2<T>(ref T f) where T : struct, IHasSlice { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRange:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_04_04() { // generic struct lvalue receiver passed by value, extension implicit this[Range], object creation var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public Item Slice(int i, int j) { System.Console.Write("slice"); return new Item(); } public int Length { get { System.Console.Write("length "); return 3; } } } } class Item { public int Property { get; set; } } struct S1 { } class Program { static async Task Main() { S1 s1 = Test1<S1>(); System.Console.Write(", "); S1 s2 = Test2<S1>(); System.Console.Write(", "); S1 s3 = await Test3<S1>(); } static T Test1<T>() where T : new() { return new T() { [GetRange()] = { Property = 42 } }; } static T Test2<T>() where T : struct { return new T() { [GetRange()] = { Property = 42 } }; } static System.Range GetRange() { System.Console.Write("GetRange "); return ..^1; } static async Task<T> Test3<T>() where T : new() { return new T() { [await GetRangeAsync()] = { Property = 42 } }; } static async Task<System.Range> GetRangeAsync() { System.Console.Write("GetRangeAsync "); await Task.Yield(); return ..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange length slice, GetRange length slice, GetRangeAsync length slice"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 131 (0x83) .maxstack 3 .locals init (T V_0, T V_1, T& V_2, System.Range V_3, int V_4, int V_5, int V_6, T V_7, System.Index V_8, T V_9) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: stloc.2 IL_000a: ldloca.s V_7 IL_000c: initobj "T" IL_0012: ldloc.s V_7 IL_0014: box "T" IL_0019: brtrue.s IL_0026 IL_001b: ldloc.2 IL_001c: ldobj "T" IL_0021: stloc.1 IL_0022: ldloca.s V_1 IL_0024: br.s IL_0027 IL_0026: ldloc.2 IL_0027: call "System.Range Program.GetRange()" IL_002c: stloc.3 IL_002d: dup IL_002e: ldobj "T" IL_0033: call "int E.get_Length<T>(T)" IL_0038: stloc.s V_4 IL_003a: ldloca.s V_3 IL_003c: call "System.Index System.Range.Start.get" IL_0041: stloc.s V_8 IL_0043: ldloca.s V_8 IL_0045: ldloc.s V_4 IL_0047: call "int System.Index.GetOffset(int)" IL_004c: stloc.s V_5 IL_004e: ldloca.s V_3 IL_0050: call "System.Index System.Range.End.get" IL_0055: stloc.s V_8 IL_0057: ldloca.s V_8 IL_0059: ldloc.s V_4 IL_005b: call "int System.Index.GetOffset(int)" IL_0060: ldloc.s V_5 IL_0062: sub IL_0063: stloc.s V_6 IL_0065: ldobj "T" IL_006a: ldloc.s V_5 IL_006c: ldloc.s V_6 IL_006e: call "Item E.Slice<T>(T, int, int)" IL_0073: ldc.i4.s 42 IL_0075: callvirt "void Item.Property.set" IL_007a: nop IL_007b: ldloc.0 IL_007c: stloc.s V_9 IL_007e: br.s IL_0080 IL_0080: ldloc.s V_9 IL_0082: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 95 (0x5f) .maxstack 3 .locals init (T V_0, System.Range V_1, int V_2, int V_3, int V_4, System.Index V_5, T V_6) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: call "System.Range Program.GetRange()" IL_000e: stloc.1 IL_000f: dup IL_0010: ldobj "T" IL_0015: call "int E.get_Length<T>(T)" IL_001a: stloc.2 IL_001b: ldloca.s V_1 IL_001d: call "System.Index System.Range.Start.get" IL_0022: stloc.s V_5 IL_0024: ldloca.s V_5 IL_0026: ldloc.2 IL_0027: call "int System.Index.GetOffset(int)" IL_002c: stloc.3 IL_002d: ldloca.s V_1 IL_002f: call "System.Index System.Range.End.get" IL_0034: stloc.s V_5 IL_0036: ldloca.s V_5 IL_0038: ldloc.2 IL_0039: call "int System.Index.GetOffset(int)" IL_003e: ldloc.3 IL_003f: sub IL_0040: stloc.s V_4 IL_0042: ldobj "T" IL_0047: ldloc.3 IL_0048: ldloc.s V_4 IL_004a: call "Item E.Slice<T>(T, int, int)" IL_004f: ldc.i4.s 42 IL_0051: callvirt "void Item.Property.set" IL_0056: nop IL_0057: ldloc.0 IL_0058: stloc.s V_6 IL_005a: br.s IL_005c IL_005c: ldloc.s V_6 IL_005e: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_05() { // sibling of IndexerAccess_Get_LValueReceiver_05 // struct lvalue receiver passed by ref, constrained to struct, extension Length + extension Slice var src = """ using System.Threading.Tasks; static class E { extension<T>(ref T x) where T : struct { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } static async Task Test3<T>() where T : struct { _ = Program<T>.F[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRangeAsync:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 62 (0x3e) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: call "int E.get_Length<T>(ref T)" IL_000e: stloc.1 IL_000f: ldloca.s V_0 IL_0011: call "System.Index System.Range.Start.get" IL_0016: stloc.s V_4 IL_0018: ldloca.s V_4 IL_001a: ldloc.1 IL_001b: call "int System.Index.GetOffset(int)" IL_0020: stloc.2 IL_0021: ldloca.s V_0 IL_0023: call "System.Index System.Range.End.get" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: ldloc.1 IL_002d: call "int System.Index.GetOffset(int)" IL_0032: ldloc.2 IL_0033: sub IL_0034: stloc.3 IL_0035: ldloc.2 IL_0036: ldloc.3 IL_0037: call "int E.Slice<T>(ref T, int, int)" IL_003c: pop IL_003d: ret } """); // receiver is not a variable var src2 = """ static class E { extension<T>(ref T x) where T : struct { public int Length => 5; public int Slice(int start, int length) => 0; } } class Program { static void Test<T>() where T : struct { _ = default(T)[1..^1]; } } """; CreateCompilation(src2, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (14,13): error CS1510: A ref or out value must be an assignable variable // _ = default(T)[1..^1]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 13), // (14,13): error CS1510: A ref or out value must be an assignable variable // _ = default(T)[1..^1]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 13)); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_05_02() { // struct lvalue receiver passed by ref, constrained to struct, instance Length (via interface) + extension Slice (by ref) var src = """ using System.Threading.Tasks; interface IHasLength { int Length { get; } } static class E { extension<T>(ref T x) where T : struct, IHasLength { public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } } } struct S1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program<S1>.F.F1++; return 5; } } } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct, IHasLength { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } static async Task Test3<T>() where T : struct, IHasLength { _ = Program<T>.F[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRangeAsync:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_05_03() { // struct lvalue receiver passed by ref, constrained to struct, extension Length (by ref) + instance Slice (via interface) var src = """ using System.Threading.Tasks; interface IHasSlice { int Slice(int start, int length); } static class E { extension<T>(ref T x) where T : struct, IHasSlice { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } } } struct S1 : IHasSlice { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program<S1>.F.F1++; return 0; } } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct, IHasSlice { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } static async Task Test3<T>() where T : struct, IHasSlice { _ = Program<T>.F[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 final:6, GetRangeAsync:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_06() { // sibling of IndexerAccess_Get_LValueReceiver_06 // generic class lvalue receiver passed by value, extension Length + extension Slice var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 0; } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { static void Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(", "); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) { _ = f[GetRange()]; } static void Test2<T>(ref T f) where T : class { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 107 (0x6b) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, System.Index V_7) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloca.s V_6 IL_0005: initobj "T" IL_000b: ldloc.s V_6 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.1 IL_0015: ldobj "T" IL_001a: stloc.0 IL_001b: ldloca.s V_0 IL_001d: br.s IL_0020 IL_001f: ldloc.1 IL_0020: call "System.Range Program.GetRange()" IL_0025: stloc.2 IL_0026: dup IL_0027: ldobj "T" IL_002c: call "int E.get_Length<T>(T)" IL_0031: stloc.3 IL_0032: ldloca.s V_2 IL_0034: call "System.Index System.Range.Start.get" IL_0039: stloc.s V_7 IL_003b: ldloca.s V_7 IL_003d: ldloc.3 IL_003e: call "int System.Index.GetOffset(int)" IL_0043: stloc.s V_4 IL_0045: ldloca.s V_2 IL_0047: call "System.Index System.Range.End.get" IL_004c: stloc.s V_7 IL_004e: ldloca.s V_7 IL_0050: ldloc.3 IL_0051: call "int System.Index.GetOffset(int)" IL_0056: ldloc.s V_4 IL_0058: sub IL_0059: stloc.s V_5 IL_005b: ldobj "T" IL_0060: ldloc.s V_4 IL_0062: ldloc.s V_5 IL_0064: call "int E.Slice<T>(T, int, int)" IL_0069: pop IL_006a: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 67 (0x43) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: call "System.Range Program.GetRange()" IL_000c: stloc.0 IL_000d: dup IL_000e: call "int E.get_Length<T>(T)" IL_0013: stloc.1 IL_0014: ldloca.s V_0 IL_0016: call "System.Index System.Range.Start.get" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: ldloc.1 IL_0020: call "int System.Index.GetOffset(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_0 IL_0028: call "System.Index System.Range.End.get" IL_002d: stloc.s V_4 IL_002f: ldloca.s V_4 IL_0031: ldloc.1 IL_0032: call "int System.Index.GetOffset(int)" IL_0037: ldloc.2 IL_0038: sub IL_0039: stloc.3 IL_003a: ldloc.2 IL_003b: ldloc.3 IL_003c: call "int E.Slice<T>(T, int, int)" IL_0041: pop IL_0042: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_06_02() { // generic class lvalue receiver, instance Length (via interface) + extension Slice var src = """ interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : IHasLength { public int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 0; } } } class C1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 5; } } } class Program<T> { public static T F; } class Program { static void Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(", "); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) where T : IHasLength { _ = f[GetRange()]; } static void Test2<T>(ref T f) where T : class, IHasLength { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_06_03() { // generic class lvalue receiver, extension Length + instance Slice (via interface) var src = """ interface IHasSlice { int Slice(int start, int length); } static class E { extension<T>(T x) where T : IHasSlice { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 5; } } } } class C1 : IHasSlice { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 0; } } class Program<T> { public static T F; } class Program { static void Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(", "); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) where T : IHasSlice { _ = f[GetRange()]; } static void Test2<T>(ref T f) where T : class, IHasSlice { _ = f[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_01() { // sibling of IndexerAccess_Get_RValueReceiver_01 // struct rvalue receiver passed by value, extension Length + extension Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } public struct S1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetRange()]; } static S1 GetS1() => new S1 { F1 = State }; public static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 80 (0x50) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, S1 V_4, System.Index V_5) IL_0000: nop IL_0001: call "S1 Program.GetS1()" IL_0006: stloc.s V_4 IL_0008: ldloca.s V_4 IL_000a: call "System.Range Program.GetRange()" IL_000f: stloc.0 IL_0010: dup IL_0011: ldobj "S1" IL_0016: call "int E.get_Length(S1)" IL_001b: stloc.1 IL_001c: ldloca.s V_0 IL_001e: call "System.Index System.Range.Start.get" IL_0023: stloc.s V_5 IL_0025: ldloca.s V_5 IL_0027: ldloc.1 IL_0028: call "int System.Index.GetOffset(int)" IL_002d: stloc.2 IL_002e: ldloca.s V_0 IL_0030: call "System.Index System.Range.End.get" IL_0035: stloc.s V_5 IL_0037: ldloca.s V_5 IL_0039: ldloc.1 IL_003a: call "int System.Index.GetOffset(int)" IL_003f: ldloc.2 IL_0040: sub IL_0041: stloc.3 IL_0042: ldobj "S1" IL_0047: ldloc.2 IL_0048: ldloc.3 IL_0049: call "int E.Slice(S1, int, int)" IL_004e: pop IL_004f: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_01_02() { // struct rvalue receiver passed by value, instance Length + extension Slice var src = """ static class E { extension(S1 x) { public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } public struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.State++; return 5; } } } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetRange()]; } static S1 GetS1() => new S1 { F1 = State }; public static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_01_03() { // struct rvalue receiver passed by value, extension Length + instance Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } } } public struct S1 { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.State++; return 0; } } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetRange()]; } static S1 GetS1() => new S1 { F1 = State }; public static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_02(string refKind) { // sibling of IndexerAccess_Get_RValueReceiver_02 // struct rvalue receiver passed by ref, extension Length + extension Slice var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } struct S1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetRange()]; } static S1 GetS1() => new S1 { F1 = State }; public static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp.VerifyEmitDiagnostics( // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetS1()").WithLocation(28, 13), // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetS1()").WithLocation(28, 13)); return; case "ref readonly": comp.VerifyEmitDiagnostics( // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13), // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13), // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13)); break; case "in": comp.VerifyEmitDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped); verifier.VerifyIL("Program.Test", $$""" { // Code size 70 (0x46) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, S1 V_4, System.Index V_5) IL_0000: nop IL_0001: call "S1 Program.GetS1()" IL_0006: stloc.s V_4 IL_0008: ldloca.s V_4 IL_000a: call "System.Range Program.GetRange()" IL_000f: stloc.0 IL_0010: dup IL_0011: call "int E.get_Length({{refKind}} S1)" IL_0016: stloc.1 IL_0017: ldloca.s V_0 IL_0019: call "System.Index System.Range.Start.get" IL_001e: stloc.s V_5 IL_0020: ldloca.s V_5 IL_0022: ldloc.1 IL_0023: call "int System.Index.GetOffset(int)" IL_0028: stloc.2 IL_0029: ldloca.s V_0 IL_002b: call "System.Index System.Range.End.get" IL_0030: stloc.s V_5 IL_0032: ldloca.s V_5 IL_0034: ldloc.1 IL_0035: call "int System.Index.GetOffset(int)" IL_003a: ldloc.2 IL_003b: sub IL_003c: stloc.3 IL_003d: ldloc.2 IL_003e: ldloc.3 IL_003f: call "int E.Slice({{refKind}} S1, int, int)" IL_0044: pop IL_0045: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_02_02(string refKind) { // struct rvalue receiver passed by ref, instance Length + extension Slice var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.State++; return 5; } } } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetRange()]; } static S1 GetS1() => new S1 { F1 = State }; public static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp.VerifyEmitDiagnostics( // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetS1()").WithLocation(28, 13)); return; case "ref readonly": comp.VerifyEmitDiagnostics( // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13)); break; case "in": comp.VerifyEmitDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_02_03(string refKind) { // struct rvalue receiver passed by ref, extension Length + instance Slice var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } } } struct S1 { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.State++; return 0; } } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetRange()]; } static S1 GetS1() => new S1 { F1 = State }; public static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp.VerifyEmitDiagnostics( // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetS1()").WithLocation(28, 13)); return; case "ref readonly": comp.VerifyEmitDiagnostics( // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetRange()]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13)); break; case "in": comp.VerifyEmitDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_03() { // sibling of IndexerAccess_Get_RValueReceiver_03 // class rvalue receiver passed by value, extension Length + extension Slice var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } class C1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetC1()[GetRange()]; } static C1 GetC1() => new C1 { F1 = State }; static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 66 (0x42) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: call "C1 Program.GetC1()" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length(C1)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "int E.Slice(C1, int, int)" IL_0040: pop IL_0041: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_03_02() { // class rvalue receiver passed by value, instance Length + extension Slice var src = """ static class E { extension(C1 x) { public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } class C1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.State++; return 5; } } } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetC1()[GetRange()]; } static C1 GetC1() => new C1 { F1 = State }; static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_03_03() { // class rvalue receiver passed by value, extension Length + instance Slice var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } } } class C1 { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{this.F1} "); Program.State++; return 0; } } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetC1()[GetRange()]; } static C1 GetC1() => new C1 { F1 = State }; static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_04() { // sibling of IndexerAccess_Get_RValueReceiver_04 // generic struct rvalue receiver passed by value, extension Length + extension Slice var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program.State++; return 0; } } } struct S1 { public int F1; } class Program { public static int State; static async Task Main() { State = 3; Test1<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<S1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new S1 { F1 = State }; static void Test1<T>() { _ = GetT<T>()[GetRange()]; } static void Test2<T>() where T : struct { _ = GetT<T>()[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } static async Task Test3<T>() { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{State} "); State++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6, GetRangeAsync:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 115 (0x73) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, T V_7, System.Index V_8) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.s V_6 IL_0008: ldloca.s V_6 IL_000a: stloc.1 IL_000b: ldloca.s V_7 IL_000d: initobj "T" IL_0013: ldloc.s V_7 IL_0015: box "T" IL_001a: brtrue.s IL_0027 IL_001c: ldloc.1 IL_001d: ldobj "T" IL_0022: stloc.0 IL_0023: ldloca.s V_0 IL_0025: br.s IL_0028 IL_0027: ldloc.1 IL_0028: call "System.Range Program.GetRange()" IL_002d: stloc.2 IL_002e: dup IL_002f: ldobj "T" IL_0034: call "int E.get_Length<T>(T)" IL_0039: stloc.3 IL_003a: ldloca.s V_2 IL_003c: call "System.Index System.Range.Start.get" IL_0041: stloc.s V_8 IL_0043: ldloca.s V_8 IL_0045: ldloc.3 IL_0046: call "int System.Index.GetOffset(int)" IL_004b: stloc.s V_4 IL_004d: ldloca.s V_2 IL_004f: call "System.Index System.Range.End.get" IL_0054: stloc.s V_8 IL_0056: ldloca.s V_8 IL_0058: ldloc.3 IL_0059: call "int System.Index.GetOffset(int)" IL_005e: ldloc.s V_4 IL_0060: sub IL_0061: stloc.s V_5 IL_0063: ldobj "T" IL_0068: ldloc.s V_4 IL_006a: ldloc.s V_5 IL_006c: call "int E.Slice<T>(T, int, int)" IL_0071: pop IL_0072: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 80 (0x50) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, T V_4, System.Index V_5) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.s V_4 IL_0008: ldloca.s V_4 IL_000a: call "System.Range Program.GetRange()" IL_000f: stloc.0 IL_0010: dup IL_0011: ldobj "T" IL_0016: call "int E.get_Length<T>(T)" IL_001b: stloc.1 IL_001c: ldloca.s V_0 IL_001e: call "System.Index System.Range.Start.get" IL_0023: stloc.s V_5 IL_0025: ldloca.s V_5 IL_0027: ldloc.1 IL_0028: call "int System.Index.GetOffset(int)" IL_002d: stloc.2 IL_002e: ldloca.s V_0 IL_0030: call "System.Index System.Range.End.get" IL_0035: stloc.s V_5 IL_0037: ldloca.s V_5 IL_0039: ldloc.1 IL_003a: call "int System.Index.GetOffset(int)" IL_003f: ldloc.2 IL_0040: sub IL_0041: stloc.3 IL_0042: ldobj "T" IL_0047: ldloc.2 IL_0048: ldloc.3 IL_0049: call "int E.Slice<T>(T, int, int)" IL_004e: pop IL_004f: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_04_02() { // generic struct rvalue, instance Length (via interface) + extension Slice var src = """ using System.Threading.Tasks; interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : IHasLength { public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program.State++; return 0; } } } struct S1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.State++; return 5; } } } class Program { public static int State; static async Task Main() { State = 3; Test1<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<S1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new S1 { F1 = State }; static void Test1<T>() where T : IHasLength { _ = GetT<T>()[GetRange()]; } static void Test2<T>() where T : struct, IHasLength { _ = GetT<T>()[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } static async Task Test3<T>() where T : IHasLength { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{State} "); State++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6, GetRangeAsync:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_04_03() { // generic struct rvalue, extension Length + instance Slice (via interface) var src = """ using System.Threading.Tasks; interface IHasSlice { int Slice(int start, int length); } static class E { extension<T>(T x) where T : IHasSlice { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.State++; return 5; } } } } struct S1 : IHasSlice { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.State++; return 0; } } class Program { public static int State; static async Task Main() { State = 3; Test1<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<S1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new S1 { F1 = State }; static void Test1<T>() where T : IHasSlice { _ = GetT<T>()[GetRange()]; } static void Test2<T>() where T : struct, IHasSlice { _ = GetT<T>()[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } static async Task Test3<T>() where T : IHasSlice { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{State} "); State++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6, GetRangeAsync:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_05() { // sibling of IndexerAccess_Get_RValueReceiver_05 // struct rvalue receiver passed by ref, constrained to struct, extension Length + extension Slice var src = """ using System.Threading.Tasks; static class E { extension<T>(ref T x) where T : struct { public int Length => 5; public int Slice(int start, int length) => 0; } } struct S1 { public int F1; } class Program { static void Test2<T>() where T : struct { _ = GetT<T>()[GetRange()]; } static T GetT<T>() => (T)(object)new S1 { F1 = 3 }; static System.Range GetRange() => 1..^1; static async Task Test3<T>() where T : struct { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { await Task.Yield(); return 1..^1; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (21,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[GetRange()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(21, 13), // (21,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[GetRange()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(21, 13), // (29,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[await GetRangeAsync()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(29, 13), // (29,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[await GetRangeAsync()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(29, 13)); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_05_02() { // struct rvalue receiver, instance Length (via interface) + extension Slice (by ref) var src = """ using System.Threading.Tasks; interface IHasLength { int Length { get; } } static class E { extension<T>(ref T x) where T : struct, IHasLength { public int Slice(int start, int length) => 0; } } struct S1 : IHasLength { public int F1; public int Length => 5; } class Program { static void Test2<T>() where T : struct, IHasLength { _ = GetT<T>()[GetRange()]; } static T GetT<T>() => (T)(object)new S1 { F1 = 3 }; static System.Range GetRange() => 1..^1; static async Task Test3<T>() where T : struct, IHasLength { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { await Task.Yield(); return 1..^1; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (26,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[GetRange()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(26, 13), // (34,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[await GetRangeAsync()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(34, 13)); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_05_03() { // struct rvalue receiver, extension Length (by ref) + instance Slice (via interface) var src = """ using System.Threading.Tasks; interface IHasSlice { int Slice(int start, int length); } static class E { extension<T>(ref T x) where T : struct, IHasSlice { public int Length => 5; } } struct S1 : IHasSlice { public int F1; public int Slice(int start, int length) => 0; } class Program { static void Test2<T>() where T : struct, IHasSlice { _ = GetT<T>()[GetRange()]; } static T GetT<T>() => (T)(object)new S1 { F1 = 3 }; static System.Range GetRange() => 1..^1; static async Task Test3<T>() where T : struct, IHasSlice { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { await Task.Yield(); return 1..^1; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (26,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[GetRange()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(26, 13), // (34,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[await GetRangeAsync()]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(34, 13)); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_06() { // sibling of IndexerAccess_Get_RValueReceiver_06 // generic class rvalue receiver passed by value, extension Length + extension Slice var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program.State++; return 0; } } } class C1 { public int F1; } class Program { public static int State; static async Task Main() { State = 3; Test1<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<C1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new C1 { F1 = State }; static void Test1<T>() { _ = GetT<T>()[GetRange()]; } static void Test2<T>() where T : class { _ = GetT<T>()[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } static async Task Test3<T>() { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{State} "); State++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6, GetRangeAsync:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 115 (0x73) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, T V_7, System.Index V_8) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.s V_6 IL_0008: ldloca.s V_6 IL_000a: stloc.1 IL_000b: ldloca.s V_7 IL_000d: initobj "T" IL_0013: ldloc.s V_7 IL_0015: box "T" IL_001a: brtrue.s IL_0027 IL_001c: ldloc.1 IL_001d: ldobj "T" IL_0022: stloc.0 IL_0023: ldloca.s V_0 IL_0025: br.s IL_0028 IL_0027: ldloc.1 IL_0028: call "System.Range Program.GetRange()" IL_002d: stloc.2 IL_002e: dup IL_002f: ldobj "T" IL_0034: call "int E.get_Length<T>(T)" IL_0039: stloc.3 IL_003a: ldloca.s V_2 IL_003c: call "System.Index System.Range.Start.get" IL_0041: stloc.s V_8 IL_0043: ldloca.s V_8 IL_0045: ldloc.3 IL_0046: call "int System.Index.GetOffset(int)" IL_004b: stloc.s V_4 IL_004d: ldloca.s V_2 IL_004f: call "System.Index System.Range.End.get" IL_0054: stloc.s V_8 IL_0056: ldloca.s V_8 IL_0058: ldloc.3 IL_0059: call "int System.Index.GetOffset(int)" IL_005e: ldloc.s V_4 IL_0060: sub IL_0061: stloc.s V_5 IL_0063: ldobj "T" IL_0068: ldloc.s V_4 IL_006a: ldloc.s V_5 IL_006c: call "int E.Slice<T>(T, int, int)" IL_0071: pop IL_0072: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 66 (0x42) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length<T>(T)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "int E.Slice<T>(T, int, int)" IL_0040: pop IL_0041: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_06_02() { // generic class rvalue, instance Length (via interface) + extension Slice var src = """ using System.Threading.Tasks; interface IHasLength { int Length { get; } } static class E { extension<T>(T x) where T : IHasLength { public int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program.State++; return 0; } } } class C1 : IHasLength { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.State++; return 5; } } } class Program { public static int State; static async Task Main() { State = 3; Test1<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<C1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new C1 { F1 = State }; static void Test1<T>() where T : IHasLength { _ = GetT<T>()[GetRange()]; } static void Test2<T>() where T : class, IHasLength { _ = GetT<T>()[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } static async Task Test3<T>() where T : IHasLength { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{State} "); State++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6, GetRangeAsync:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_06_03() { // generic class rvalue, extension Length + instance Slice (via interface) var src = """ using System.Threading.Tasks; interface IHasSlice { int Slice(int start, int length); } static class E { extension<T>(T x) where T : IHasSlice { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program.State++; return 5; } } } } class C1 : IHasSlice { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.State++; return 0; } } class Program { public static int State; static async Task Main() { State = 3; Test1<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<C1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new C1 { F1 = State }; static void Test1<T>() where T : IHasSlice { _ = GetT<T>()[GetRange()]; } static void Test2<T>() where T : class, IHasSlice { _ = GetT<T>()[GetRange()]; } static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } static async Task Test3<T>() where T : IHasSlice { _ = GetT<T>()[await GetRangeAsync()]; } static async Task<System.Range> GetRangeAsync() { System.Console.Write($"GetRangeAsync:{State} "); State++; await Task.Yield(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 final:6, GetRange:3 length:3 Slice:3 final:6, GetRangeAsync:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_RefAnalysis_01() { var src = """ public ref struct MyStruct { public ref int i; } public static class E { extension(MyStruct s1) { public int Length => 1; public MyStruct this[int i] => s1; } } class Program { static MyStruct M() { int i = 0; return new MyStruct() { i = ref i }[^1]; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (20,31): error CS8352: Cannot use variable 'i = ref i' in this context because it may expose referenced variables outside of their declaration scope // return new MyStruct() { i = ref i }[^1]; Diagnostic(ErrorCode.ERR_EscapeVariable, "{ i = ref i }").WithArguments("i = ref i").WithLocation(20, 31), // (20,16): error CS8347: Cannot use a result of 'E.extension(MyStruct).this[int]' in this context because it may expose variables referenced by parameter 's1' outside of their declaration scope // return new MyStruct() { i = ref i }[^1]; Diagnostic(ErrorCode.ERR_EscapeCall, "new MyStruct() { i = ref i }[^1]").WithArguments("E.extension(MyStruct).this[int]", "s1").WithLocation(20, 16)); // instance this[int] + instance Length src = """ public ref struct MyStruct { public ref int i; public int Length => 1; public MyStruct this[int i] => throw null; } class Program { static MyStruct M() { int i = 0; return new MyStruct() { i = ref i }[^1]; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (13,31): error CS8352: Cannot use variable 'i = ref i' in this context because it may expose referenced variables outside of their declaration scope // return new MyStruct() { i = ref i }[^1]; Diagnostic(ErrorCode.ERR_EscapeVariable, "{ i = ref i }").WithArguments("i = ref i").WithLocation(13, 31)); // instance this[Index] src = """ public ref struct MyStruct { public ref int i; public MyStruct this[System.Index i] => throw null; } class Program { static MyStruct M() { int i = 0; return new MyStruct() { i = ref i }[^1]; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (12,31): error CS8352: Cannot use variable 'i = ref i' in this context because it may expose referenced variables outside of their declaration scope // return new MyStruct() { i = ref i }[^1]; Diagnostic(ErrorCode.ERR_EscapeVariable, "{ i = ref i }").WithArguments("i = ref i").WithLocation(12, 31)); } [Fact] public void ImplicitRangeIndexerAccess_RefAnalysis_01() { var src = """ public ref struct MyStruct { public ref int i; } public static class E { extension(MyStruct s1) { public int Length => 1; public MyStruct Slice(int start, int length) => s1; } } class Program { static MyStruct M() { int i = 0; return new MyStruct() { i = ref i }[0..]; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (20,31): error CS8352: Cannot use variable 'i = ref i' in this context because it may expose referenced variables outside of their declaration scope // return new MyStruct() { i = ref i }[0..]; Diagnostic(ErrorCode.ERR_EscapeVariable, "{ i = ref i }").WithArguments("i = ref i").WithLocation(20, 31), // (20,16): error CS8347: Cannot use a result of 'E.extension(MyStruct).Slice(int, int)' in this context because it may expose variables referenced by parameter 's1' outside of their declaration scope // return new MyStruct() { i = ref i }[0..]; Diagnostic(ErrorCode.ERR_EscapeCall, "new MyStruct() { i = ref i }[0..]").WithArguments("E.extension(MyStruct).Slice(int, int)", "s1").WithLocation(20, 16)); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01() { // sibling of IndexerAccess_Get_LValueReceiver_01 // struct lvalue receiver passed by value, extension Length + extension Slice(int, int) + extension Slice(int), slice from end var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return 0; } } } public struct S1 { public int F1; public void Test2() { _ = this[Program.GetStart()..]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:5 final:6, GetStart:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 42 (0x2a) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetStart()" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: stloc.1 IL_000e: dup IL_000f: ldobj "S1" IL_0014: call "int E.get_Length(S1)" IL_0019: ldloc.0 IL_001a: sub IL_001b: stloc.2 IL_001c: ldobj "S1" IL_0021: ldloc.1 IL_0022: ldloc.2 IL_0023: call "int E.Slice(S1, int, int)" IL_0028: pop IL_0029: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 38 (0x26) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetStart()" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: stloc.1 IL_000a: dup IL_000b: ldobj "S1" IL_0010: call "int E.get_Length(S1)" IL_0015: ldloc.0 IL_0016: sub IL_0017: stloc.2 IL_0018: ldobj "S1" IL_001d: ldloc.1 IL_001e: ldloc.2 IL_001f: call "int E.Slice(S1, int, int)" IL_0024: pop IL_0025: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01_02() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // struct lvalue receiver passed by value, instance Length + extension Slice, start.. var src = """ static class E { extension(S1 x) { public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return 0; } } } public struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 5; } } public void Test2() { _ = this[Program.GetStart()..]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:5 final:6, GetStart:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 37 (0x25) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetStart()" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: stloc.1 IL_000e: dup IL_000f: call "int S1.Length.get" IL_0014: ldloc.0 IL_0015: sub IL_0016: stloc.2 IL_0017: ldobj "S1" IL_001c: ldloc.1 IL_001d: ldloc.2 IL_001e: call "int E.Slice(S1, int, int)" IL_0023: pop IL_0024: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 33 (0x21) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetStart()" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: stloc.1 IL_000a: dup IL_000b: call "int S1.Length.get" IL_0010: ldloc.0 IL_0011: sub IL_0012: stloc.2 IL_0013: ldobj "S1" IL_0018: ldloc.1 IL_0019: ldloc.2 IL_001a: call "int E.Slice(S1, int, int)" IL_001f: pop IL_0020: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01_03() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // struct lvalue receiver passed by value, extension Length + instance Slice, start.. var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } } } public struct S1 { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F.F1++; return 0; } public void Test2() { _ = this[Program.GetStart()..]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:5 final:6, GetStart:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 35 (0x23) .maxstack 4 .locals init (S1& V_0, int V_1) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: ldloc.0 IL_0010: ldobj "S1" IL_0015: call "int E.get_Length(S1)" IL_001a: ldloc.1 IL_001b: sub IL_001c: call "int S1.Slice(int, int)" IL_0021: pop IL_0022: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 31 (0x1f) .maxstack 4 .locals init (S1& V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "int Program.GetStart()" IL_0008: stloc.1 IL_0009: ldloc.0 IL_000a: ldloc.1 IL_000b: ldloc.0 IL_000c: ldobj "S1" IL_0011: call "int E.get_Length(S1)" IL_0016: ldloc.1 IL_0017: sub IL_0018: call "int S1.Slice(int, int)" IL_001d: pop IL_001e: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_02(string refKind) { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // struct lvalue receiver passed by ref/ref readonly/in to extension Length + extension Slice, start.. var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); return 0; } } } struct S1 { public int F1; public void Test2() { _ = this[Program.GetStart()..]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[Program.GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:5 final:5, GetStart:3 length:4 Slice:5 final:5"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 30 (0x1e) .maxstack 4 .locals init (S1& V_0, int V_1) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length({{refKind}} S1)" IL_0015: ldloc.1 IL_0016: sub IL_0017: call "int E.Slice({{refKind}} S1, int, int)" IL_001c: pop IL_001d: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 26 (0x1a) .maxstack 4 .locals init (S1& V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "int Program.GetStart()" IL_0008: stloc.1 IL_0009: ldloc.0 IL_000a: ldloc.1 IL_000b: ldloc.0 IL_000c: call "int E.get_Length({{refKind}} S1)" IL_0011: ldloc.1 IL_0012: sub IL_0013: call "int E.Slice({{refKind}} S1, int, int)" IL_0018: pop IL_0019: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_03() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // class lvalue receiver passed by value (reference type), extension Length + extension Slice, start.. var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:4 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 30 (0x1e) .maxstack 4 .locals init (C1 V_0, int V_1) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length(C1)" IL_0015: ldloc.1 IL_0016: sub IL_0017: call "int E.Slice(C1, int, int)" IL_001c: pop IL_001d: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_03_02() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_03 // class lvalue receiver passed by value (reference type), instance Length + extension Slice, start.. var src = """ static class E { extension(C1 x) { public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } } } class C1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:4 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 30 (0x1e) .maxstack 4 .locals init (C1 V_0, int V_1) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: ldloc.0 IL_0010: callvirt "int C1.Length.get" IL_0015: ldloc.1 IL_0016: sub IL_0017: call "int E.Slice(C1, int, int)" IL_001c: pop IL_001d: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_03_03() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_03 // class lvalue receiver passed by value (reference type), extension Length + instance Slice, start.. var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } } } class C1 { public int F1; public int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:4 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 30 (0x1e) .maxstack 4 .locals init (C1 V_0, int V_1) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length(C1)" IL_0015: ldloc.1 IL_0016: sub IL_0017: callvirt "int C1.Slice(int, int)" IL_001c: pop IL_001d: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_04() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // generic struct lvalue receiver passed by value to extension Length + extension Slice(int, int) // Test1: unconstrained T (could be struct or class) // Test2: T constrained to struct, start.. var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) { _ = f[GetStart()..]; } static void Test2<T>(ref T f) where T : struct { _ = f[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:5 final:6, GetStart:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 70 (0x46) .maxstack 3 .locals init (T V_0, T& V_1, int V_2, int V_3, int V_4, T V_5) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloca.s V_5 IL_0005: initobj "T" IL_000b: ldloc.s V_5 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.1 IL_0015: ldobj "T" IL_001a: stloc.0 IL_001b: ldloca.s V_0 IL_001d: br.s IL_0020 IL_001f: ldloc.1 IL_0020: call "int Program.GetStart()" IL_0025: stloc.2 IL_0026: ldloc.2 IL_0027: stloc.3 IL_0028: dup IL_0029: ldobj "T" IL_002e: call "int E.get_Length<T>(T)" IL_0033: ldloc.2 IL_0034: sub IL_0035: stloc.s V_4 IL_0037: ldobj "T" IL_003c: ldloc.3 IL_003d: ldloc.s V_4 IL_003f: call "int E.Slice<T>(T, int, int)" IL_0044: pop IL_0045: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 38 (0x26) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetStart()" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: stloc.1 IL_000a: dup IL_000b: ldobj "T" IL_0010: call "int E.get_Length<T>(T)" IL_0015: ldloc.0 IL_0016: sub IL_0017: stloc.2 IL_0018: ldobj "T" IL_001d: ldloc.1 IL_001e: ldloc.2 IL_001f: call "int E.Slice<T>(T, int, int)" IL_0024: pop IL_0025: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_05() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // generic struct lvalue receiver passed by ref to extension Length + extension Slice(int, int) // Test2: synchronous start expression // Test3: asynchronous start expression (await), start.. var src = """ using System.Threading.Tasks; static class E { extension<T>(ref T x) where T : struct { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct { _ = f[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } static async Task Test3<T>() where T : struct { _ = Program<T>.F[(await GetStartAsync())..]; } static async Task<int> GetStartAsync() { System.Console.Write($"GetStartAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:5 final:6, GetStartAsync:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 26 (0x1a) .maxstack 4 .locals init (T& V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "int Program.GetStart()" IL_0008: stloc.1 IL_0009: ldloc.0 IL_000a: ldloc.1 IL_000b: ldloc.0 IL_000c: call "int E.get_Length<T>(ref T)" IL_0011: ldloc.1 IL_0012: sub IL_0013: call "int E.Slice<T>(ref T, int, int)" IL_0018: pop IL_0019: ret } """); // receiver is not a variable var src2 = """ static class E { extension<T>(ref T x) where T : struct { public int Length => 5; public int Slice(int start, int length) => 0; } } class Program { static void Test<T>() where T : struct { _ = default(T)[1..^1]; } } """; CreateCompilation(src2, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (14,13): error CS1510: A ref or out value must be an assignable variable // _ = default(T)[1..^1]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 13), // (14,13): error CS1510: A ref or out value must be an assignable variable // _ = default(T)[1..^1]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 13)); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_06() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // generic class lvalue receiver passed by value to extension Length + extension Slice(int, int) // Test1: unconstrained T (could be struct or class) // Test2: T constrained to class, start.. var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 0; } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { static void Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(", "); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) { _ = f[GetStart()..]; } static void Test2<T>(ref T f) where T : class { _ = f[GetStart()..]; } public static int GetStart() { System.Console.Write($"GetStart:{Program<C1>.F.F1} "); Program<C1>.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:4 final:6, GetStart:3 length:4 Slice:4 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 70 (0x46) .maxstack 3 .locals init (T V_0, T& V_1, int V_2, int V_3, int V_4, T V_5) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloca.s V_5 IL_0005: initobj "T" IL_000b: ldloc.s V_5 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.1 IL_0015: ldobj "T" IL_001a: stloc.0 IL_001b: ldloca.s V_0 IL_001d: br.s IL_0020 IL_001f: ldloc.1 IL_0020: call "int Program.GetStart()" IL_0025: stloc.2 IL_0026: ldloc.2 IL_0027: stloc.3 IL_0028: dup IL_0029: ldobj "T" IL_002e: call "int E.get_Length<T>(T)" IL_0033: ldloc.2 IL_0034: sub IL_0035: stloc.s V_4 IL_0037: ldobj "T" IL_003c: ldloc.3 IL_003d: ldloc.s V_4 IL_003f: call "int E.Slice<T>(T, int, int)" IL_0044: pop IL_0045: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 31 (0x1f) .maxstack 4 .locals init (T V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.0 IL_0008: call "int Program.GetStart()" IL_000d: stloc.1 IL_000e: ldloc.0 IL_000f: ldloc.1 IL_0010: ldloc.0 IL_0011: call "int E.get_Length<T>(T)" IL_0016: ldloc.1 IL_0017: sub IL_0018: call "int E.Slice<T>(T, int, int)" IL_001d: pop IL_001e: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_OpenStartIndexEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // struct lvalue receiver passed by value, extension Length + extension Slice, ..endIndex var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return 0; } } } public struct S1 { public int F1; public void Test2() { _ = this[..Program.GetIndex()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[..GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return new System.Index(1); } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 Slice:5 final:6, GetIndex:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 49 (0x31) .maxstack 3 .locals init (S1& V_0, int V_1, int V_2, System.Index V_3) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: ldc.i4.0 IL_0008: stloc.1 IL_0009: call "System.Index Program.GetIndex()" IL_000e: stloc.3 IL_000f: ldloca.s V_3 IL_0011: ldloc.0 IL_0012: ldobj "S1" IL_0017: call "int E.get_Length(S1)" IL_001c: call "int System.Index.GetOffset(int)" IL_0021: stloc.2 IL_0022: ldloc.0 IL_0023: ldobj "S1" IL_0028: ldloc.1 IL_0029: ldloc.2 IL_002a: call "int E.Slice(S1, int, int)" IL_002f: pop IL_0030: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 45 (0x2d) .maxstack 3 .locals init (S1& V_0, int V_1, int V_2, System.Index V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: ldc.i4.0 IL_0004: stloc.1 IL_0005: call "System.Index Program.GetIndex()" IL_000a: stloc.3 IL_000b: ldloca.s V_3 IL_000d: ldloc.0 IL_000e: ldobj "S1" IL_0013: call "int E.get_Length(S1)" IL_0018: call "int System.Index.GetOffset(int)" IL_001d: stloc.2 IL_001e: ldloc.0 IL_001f: ldobj "S1" IL_0024: ldloc.1 IL_0025: ldloc.2 IL_0026: call "int E.Slice(S1, int, int)" IL_002b: pop IL_002c: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartIndexEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // struct lvalue receiver passed by value, extension Length + extension Slice, start..endIndex var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return 0; } } } public struct S1 { public int F1; public void Test2() { _ = this[Program.GetStart()..Program.GetIndex()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetStart()..GetIndex()]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return new System.Index(2); } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 GetIndex:4 length:5 Slice:6 final:7, GetStart:3 GetIndex:4 length:5 Slice:6 final:7"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 58 (0x3a) .maxstack 3 .locals init (S1& V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.1 IL_000e: stloc.2 IL_000f: call "System.Index Program.GetIndex()" IL_0014: stloc.s V_4 IL_0016: ldloca.s V_4 IL_0018: ldloc.0 IL_0019: ldobj "S1" IL_001e: call "int E.get_Length(S1)" IL_0023: call "int System.Index.GetOffset(int)" IL_0028: ldloc.1 IL_0029: sub IL_002a: stloc.3 IL_002b: ldloc.0 IL_002c: ldobj "S1" IL_0031: ldloc.2 IL_0032: ldloc.3 IL_0033: call "int E.Slice(S1, int, int)" IL_0038: pop IL_0039: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 54 (0x36) .maxstack 3 .locals init (S1& V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "int Program.GetStart()" IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: stloc.2 IL_000b: call "System.Index Program.GetIndex()" IL_0010: stloc.s V_4 IL_0012: ldloca.s V_4 IL_0014: ldloc.0 IL_0015: ldobj "S1" IL_001a: call "int E.get_Length(S1)" IL_001f: call "int System.Index.GetOffset(int)" IL_0024: ldloc.1 IL_0025: sub IL_0026: stloc.3 IL_0027: ldloc.0 IL_0028: ldobj "S1" IL_002d: ldloc.2 IL_002e: ldloc.3 IL_002f: call "int E.Slice(S1, int, int)" IL_0034: pop IL_0035: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartIndexEndRangeExpr_03() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_03 // class lvalue receiver passed by value (reference type), extension Length + extension Slice, start..endIndex var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { _ = F[GetStart()..GetIndex()]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return new System.Index(2); } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 GetIndex:4 length:5 Slice:5 final:7"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 43 (0x2b) .maxstack 4 .locals init (C1 V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: call "System.Index Program.GetIndex()" IL_0014: stloc.2 IL_0015: ldloca.s V_2 IL_0017: ldloc.0 IL_0018: call "int E.get_Length(C1)" IL_001d: call "int System.Index.GetOffset(int)" IL_0022: ldloc.1 IL_0023: sub IL_0024: call "int E.Slice(C1, int, int)" IL_0029: pop IL_002a: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_HatStartOpenEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // struct lvalue receiver passed by value, extension Length + extension Slice, ^end.. var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return 0; } } } public struct S1 { public int F1; public void Test2() { _ = this[^Program.GetEnd()..]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[^GetEnd()..]; } public static int GetEnd() { System.Console.Write($"GetEnd:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetEnd:3 length:4 Slice:5 final:6, GetEnd:3 length:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 46 (0x2e) .maxstack 4 .locals init (int V_0, int V_1, int V_2, int V_3) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetEnd()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: dup IL_0018: ldloc.0 IL_0019: sub IL_001a: stloc.1 IL_001b: ldloc.1 IL_001c: stloc.2 IL_001d: ldloc.1 IL_001e: sub IL_001f: stloc.3 IL_0020: ldobj "S1" IL_0025: ldloc.2 IL_0026: ldloc.3 IL_0027: call "int E.Slice(S1, int, int)" IL_002c: pop IL_002d: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 42 (0x2a) .maxstack 4 .locals init (int V_0, int V_1, int V_2, int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetEnd()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: call "int E.get_Length(S1)" IL_0013: dup IL_0014: ldloc.0 IL_0015: sub IL_0016: stloc.1 IL_0017: ldloc.1 IL_0018: stloc.2 IL_0019: ldloc.1 IL_001a: sub IL_001b: stloc.3 IL_001c: ldobj "S1" IL_0021: ldloc.2 IL_0022: ldloc.3 IL_0023: call "int E.Slice(S1, int, int)" IL_0028: pop IL_0029: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartIntEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Get_LValueReceiver_IntStartOpenEndRangeExpr_01 // struct lvalue receiver passed by value, extension Length + extension Slice, start..end var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return 0; } } } public struct S1 { public int F1; public void Test2() { _ = this[Program.GetStart()..Program.GetEnd()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { _ = F[GetStart()..GetEnd()]; } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetEnd() { System.Console.Write($"GetEnd:{Program.F.F1} "); Program.F.F1++; return 2; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 GetEnd:4 Slice:5 final:6, GetStart:3 GetEnd:4 Slice:5 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 36 (0x24) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetStart()" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: stloc.1 IL_000e: call "int Program.GetEnd()" IL_0013: ldloc.0 IL_0014: sub IL_0015: stloc.2 IL_0016: ldobj "S1" IL_001b: ldloc.1 IL_001c: ldloc.2 IL_001d: call "int E.Slice(S1, int, int)" IL_0022: pop IL_0023: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 32 (0x20) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetStart()" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: stloc.1 IL_000a: call "int Program.GetEnd()" IL_000f: ldloc.0 IL_0010: sub IL_0011: stloc.2 IL_0012: ldobj "S1" IL_0017: ldloc.1 IL_0018: ldloc.2 IL_0019: call "int E.Slice(S1, int, int)" IL_001e: pop IL_001f: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_IntStartOpenEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Get_RValueReceiver_01 // struct rvalue receiver passed by value to extension Length + extension Slice, start.. var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } public struct S1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetStart()..]; } static S1 GetS1() => new S1 { F1 = State }; public static int GetStart() { System.Console.Write($"GetStart:{State} "); State++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 45 (0x2d) .maxstack 3 .locals init (int V_0, int V_1, int V_2, S1 V_3) IL_0000: nop IL_0001: call "S1 Program.GetS1()" IL_0006: stloc.3 IL_0007: ldloca.s V_3 IL_0009: call "int Program.GetStart()" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: stloc.1 IL_0011: dup IL_0012: ldobj "S1" IL_0017: call "int E.get_Length(S1)" IL_001c: ldloc.0 IL_001d: sub IL_001e: stloc.2 IL_001f: ldobj "S1" IL_0024: ldloc.1 IL_0025: ldloc.2 IL_0026: call "int E.Slice(S1, int, int)" IL_002b: pop IL_002c: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_IntStartOpenEndRangeExpr_02(string refKind) { // sibling of ImplicitRangeIndexerAccess_Get_RValueReceiver_02 // struct rvalue receiver passed by ref/in/ref readonly to extension Length + extension Slice, start.. var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } struct S1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetS1()[GetStart()..]; } static S1 GetS1() => new S1 { F1 = State }; public static int GetStart() { System.Console.Write($"GetStart:{State} "); State++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp.VerifyEmitDiagnostics( // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetS1()[GetStart()..]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetS1()").WithLocation(28, 13), // (28,13): error CS1510: A ref or out value must be an assignable variable // _ = GetS1()[GetStart()..]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetS1()").WithLocation(28, 13)); return; case "ref readonly": comp.VerifyEmitDiagnostics( // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetStart()..]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13), // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetStart()..]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13), // (28,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // _ = GetS1()[GetStart()..]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetS1()").WithArguments("0").WithLocation(28, 13)); break; case "in": comp.VerifyEmitDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:3 Slice:3 final:6"), verify: Verification.Skipped); verifier.VerifyIL("Program.Test", $$""" { // Code size 33 (0x21) .maxstack 4 .locals init (S1& V_0, int V_1, S1 V_2) IL_0000: nop IL_0001: call "S1 Program.GetS1()" IL_0006: stloc.2 IL_0007: ldloca.s V_2 IL_0009: stloc.0 IL_000a: call "int Program.GetStart()" IL_000f: stloc.1 IL_0010: ldloc.0 IL_0011: ldloc.1 IL_0012: ldloc.0 IL_0013: call "int E.get_Length({{refKind}} S1)" IL_0018: ldloc.1 IL_0019: sub IL_001a: call "int E.Slice({{refKind}} S1, int, int)" IL_001f: pop IL_0020: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_IntStartOpenEndRangeExpr_03() { // sibling of ImplicitRangeIndexerAccess_Get_RValueReceiver_03 // class rvalue receiver passed by value (reference type), extension Length + extension Slice, start.. var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.State++; return 0; } } } class C1 { public int F1; } class Program { public static int State; static void Main() { State = 3; Test(); System.Console.Write($"final:{State}"); } static void Test() { _ = GetC1()[GetStart()..]; } static C1 GetC1() => new C1 { F1 = State }; static int GetStart() { System.Console.Write($"GetStart:{State} "); State++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 30 (0x1e) .maxstack 4 .locals init (C1 V_0, int V_1) IL_0000: nop IL_0001: call "C1 Program.GetC1()" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length(C1)" IL_0015: ldloc.1 IL_0016: sub IL_0017: call "int E.Slice(C1, int, int)" IL_001c: pop IL_001d: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_IntStartOpenEndRangeExpr_04() { // sibling of ImplicitRangeIndexerAccess_Get_RValueReceiver_04 // generic struct rvalue receiver passed by value to extension Length + extension Slice(int, int) // Test1: unconstrained T (could be struct or class) // Test2: T constrained to struct // Test3: asynchronous start expression (await), start.. var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program.State++; return 0; } } } struct S1 { public int F1; } class Program { public static int State; static async Task Main() { State = 3; Test1<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<S1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new S1 { F1 = State }; static void Test1<T>() { _ = GetT<T>()[GetStart()..]; } static void Test2<T>() where T : struct { _ = GetT<T>()[GetStart()..]; } static int GetStart() { System.Console.Write($"GetStart:{State} "); State++; return 1; } static async Task Test3<T>() { _ = GetT<T>()[(await GetStartAsync())..]; } static async Task<int> GetStartAsync() { System.Console.Write($"GetStartAsync:{State} "); State++; await Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:3 Slice:3 final:6, GetStart:3 length:3 Slice:3 final:6, GetStartAsync:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 78 (0x4e) .maxstack 3 .locals init (T V_0, T& V_1, int V_2, int V_3, int V_4, T V_5, T V_6) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.s V_5 IL_0008: ldloca.s V_5 IL_000a: stloc.1 IL_000b: ldloca.s V_6 IL_000d: initobj "T" IL_0013: ldloc.s V_6 IL_0015: box "T" IL_001a: brtrue.s IL_0027 IL_001c: ldloc.1 IL_001d: ldobj "T" IL_0022: stloc.0 IL_0023: ldloca.s V_0 IL_0025: br.s IL_0028 IL_0027: ldloc.1 IL_0028: call "int Program.GetStart()" IL_002d: stloc.2 IL_002e: ldloc.2 IL_002f: stloc.3 IL_0030: dup IL_0031: ldobj "T" IL_0036: call "int E.get_Length<T>(T)" IL_003b: ldloc.2 IL_003c: sub IL_003d: stloc.s V_4 IL_003f: ldobj "T" IL_0044: ldloc.3 IL_0045: ldloc.s V_4 IL_0047: call "int E.Slice<T>(T, int, int)" IL_004c: pop IL_004d: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 45 (0x2d) .maxstack 3 .locals init (int V_0, int V_1, int V_2, T V_3) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.3 IL_0007: ldloca.s V_3 IL_0009: call "int Program.GetStart()" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: stloc.1 IL_0011: dup IL_0012: ldobj "T" IL_0017: call "int E.get_Length<T>(T)" IL_001c: ldloc.0 IL_001d: sub IL_001e: stloc.2 IL_001f: ldobj "T" IL_0024: ldloc.1 IL_0025: ldloc.2 IL_0026: call "int E.Slice<T>(T, int, int)" IL_002b: pop IL_002c: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_IntStartOpenEndRangeExpr_05() { // sibling of ImplicitRangeIndexerAccess_Get_RValueReceiver_05 // generic struct rvalue receiver passed by ref to extension Length + extension Slice(int, int) // ref-extension on rvalue receiver is an error, start.. var src = """ using System.Threading.Tasks; static class E { extension<T>(ref T x) where T : struct { public int Length => 5; public int Slice(int start, int length) => 0; } } struct S1 { public int F1; } class Program { static void Test2<T>() where T : struct { _ = GetT<T>()[GetStart()..]; } static T GetT<T>() => (T)(object)new S1 { F1 = 3 }; static int GetStart() => 1; static async Task Test3<T>() where T : struct { _ = GetT<T>()[(await GetStartAsync())..]; } static async Task<int> GetStartAsync() { await Task.Yield(); return 1; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (21,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[GetStart()..]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(21, 13), // (21,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[GetStart()..]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(21, 13), // (29,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[(await GetStartAsync())..]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(29, 13), // (29,13): error CS1510: A ref or out value must be an assignable variable // _ = GetT<T>()[(await GetStartAsync())..]; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetT<T>()").WithLocation(29, 13)); } [Fact] public void ImplicitRangeIndexerAccess_Get_RValueReceiver_IntStartOpenEndRangeExpr_06() { // sibling of ImplicitRangeIndexerAccess_Get_RValueReceiver_06 // generic class rvalue receiver passed by value to extension Length + extension Slice(int, int) // Test1: unconstrained T (could be struct or class) // Test2: T constrained to class // Test3: asynchronous start expression (await), start.. var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program.State++; return 5; } } public int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program.State++; return 0; } } } class C1 { public int F1; } class Program { public static int State; static async Task Main() { State = 3; Test1<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<C1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new C1 { F1 = State }; static void Test1<T>() { _ = GetT<T>()[GetStart()..]; } static void Test2<T>() where T : class { _ = GetT<T>()[GetStart()..]; } static int GetStart() { System.Console.Write($"GetStart:{State} "); State++; return 1; } static async Task Test3<T>() { _ = GetT<T>()[(await GetStartAsync())..]; } static async Task<int> GetStartAsync() { System.Console.Write($"GetStartAsync:{State} "); State++; await Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:3 Slice:3 final:6, GetStart:3 length:3 Slice:3 final:6, GetStartAsync:3 length:3 Slice:3 final:6"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 78 (0x4e) .maxstack 3 .locals init (T V_0, T& V_1, int V_2, int V_3, int V_4, T V_5, T V_6) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.s V_5 IL_0008: ldloca.s V_5 IL_000a: stloc.1 IL_000b: ldloca.s V_6 IL_000d: initobj "T" IL_0013: ldloc.s V_6 IL_0015: box "T" IL_001a: brtrue.s IL_0027 IL_001c: ldloc.1 IL_001d: ldobj "T" IL_0022: stloc.0 IL_0023: ldloca.s V_0 IL_0025: br.s IL_0028 IL_0027: ldloc.1 IL_0028: call "int Program.GetStart()" IL_002d: stloc.2 IL_002e: ldloc.2 IL_002f: stloc.3 IL_0030: dup IL_0031: ldobj "T" IL_0036: call "int E.get_Length<T>(T)" IL_003b: ldloc.2 IL_003c: sub IL_003d: stloc.s V_4 IL_003f: ldobj "T" IL_0044: ldloc.3 IL_0045: ldloc.s V_4 IL_0047: call "int E.Slice<T>(T, int, int)" IL_004c: pop IL_004d: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 30 (0x1e) .maxstack 4 .locals init (T V_0, int V_1) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.0 IL_0007: call "int Program.GetStart()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length<T>(T)" IL_0015: ldloc.1 IL_0016: sub IL_0017: call "int E.Slice<T>(T, int, int)" IL_001c: pop IL_001d: ret } """); } [Fact] public void RefTypeReceiverSwap_01() { // Method invocation on unconstrained-T receiver where the argument mutates the receiver via a ref parameter. var src = """ interface I { void M(int arg); } class C : I { public int Counter; public void M(int arg) { System.Console.Write($"M(C{Counter},{arg}) "); } } class Driver { public static void Test<T>(ref T t) where T : I, new() { t.M(Swap(ref t)); } public static int Swap<T>(ref T t) where T : I, new() { var newC = new C { Counter = 99 }; t = (T)(object)newC; return 0; } static void Main() { C original = new C { Counter = 1 }; Test<C>(ref original); System.Console.Write($"final=C{original.Counter}"); } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("M(C1,0) final=C99"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void RefTypeReceiverSwap_02() { // sibling of RefTypeReceiverSwap_01 with explicit indexer regular assignment instead of method invocation. var src = """ interface I { int this[int i] { get; set; } } class C : I { public int Counter; public int this[int i] { get => 0; set { System.Console.Write($"set_Item(C{Counter},{i},{value}) "); } } } class Driver { public static void Test<T>(ref T t) where T : I, new() { t[0] = Swap(ref t); } public static int Swap<T>(ref T t) where T : I, new() { var newC = new C { Counter = 99 }; t = (T)(object)newC; return 0; } static void Main() { C original = new C { Counter = 1 }; Test<C>(ref original); System.Console.Write($"final=C{original.Counter}"); } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set_Item(C1,0,0) final=C99"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void RefTypeReceiverSwap_03() { // sibling of RefTypeReceiverSwap_02 with implicit Index indexer instead of explicit indexer. var src = """ interface I { int Length { get; } int this[int i] { get; set; } } class C : I { public int Counter; public int Length => 5; public int this[int i] { get => 0; set { System.Console.Write($"set_Item(C{Counter},{i},{value}) "); } } } class Driver { public static void Test<T>(ref T t) where T : I, new() { t[^1] = Swap(ref t); } public static int Swap<T>(ref T t) where T : I, new() { var newC = new C { Counter = 99 }; t = (T)(object)newC; return 0; } static void Main() { C original = new C { Counter = 1 }; Test<C>(ref original); System.Console.Write($"final=C{original.Counter}"); } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("set_Item(C1,4,0) final=C99"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void RefTypeReceiverSwap_04() { // sibling of RefTypeReceiverSwap_03 with implicit Range indexer read instead of implicit Index set. var src = """ interface I { int Length { get; } int Slice(int start, int length); } class C : I { public int Counter; public int Length { get { System.Console.Write($"Length(C{Counter}) "); return 5; } } public int Slice(int s, int l) { System.Console.Write($"Slice(C{Counter},{s},{l}) "); return 0; } } class Driver { public static void Test<T>(ref T t) where T : I, new() { _ = t[Swap(ref t)..]; } public static int Swap<T>(ref T t) where T : I, new() { var newC = new C { Counter = 99 }; t = (T)(object)newC; return 0; } static void Main() { C original = new C { Counter = 1 }; Test<C>(ref original); System.Console.Write($"final=C{original.Counter}"); } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Length(C1) Slice(C1,0,5) final=C99"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void RefTypeReceiverSwap_05() { // sibling of RefTypeReceiverSwap_04 with ref-returning Slice setter scenario. var src = """ interface I { int Length { get; } ref int Slice(int start, int length); } class C : I { public int Counter; public int Length { get { System.Console.Write($"Length(C{Counter}) "); return 5; } } public ref int Slice(int s, int l) { System.Console.Write($"Slice(C{Counter},{s},{l}) "); return ref Driver.Result; } } class Driver { public static int Result; public static void Test<T>(ref T t) where T : I, new() { t[Swap(ref t)..] = 42; } public static int Swap<T>(ref T t) where T : I, new() { var newC = new C { Counter = 99 }; t = (T)(object)newC; return 0; } static void Main() { Result = 0; C original = new C { Counter = 1 }; Test<C>(ref original); System.Console.Write($"final=C{original.Counter} result={Result}"); } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("Length(C1) Slice(C1,0,5) final=C99 result=42"), verify: Verification.Skipped).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_Set_01() { // sibling of IndexerAccess_Set_01 // struct receiver, extension implicit this[Index] var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } public int this[int i] { set { System.Console.Write($"set:{x.F1} "); Program.F.F1++; } } } } public struct S1 { public int F1; public void Test2() { this[Program.GetIndex()] = Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] = GetValue(); } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetValue:5 set:6 final:7, GetIndex:3 length:4 GetValue:5 set:6 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 53 (0x35) .maxstack 3 .locals init (S1& V_0, int V_1, int V_2, System.Index V_3) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.3 IL_000d: ldloca.s V_3 IL_000f: ldloc.0 IL_0010: ldobj "S1" IL_0015: call "int E.get_Length(S1)" IL_001a: call "int System.Index.GetOffset(int)" IL_001f: stloc.1 IL_0020: call "int Program.GetValue()" IL_0025: stloc.2 IL_0026: ldloc.0 IL_0027: ldobj "S1" IL_002c: ldloc.1 IL_002d: ldloc.2 IL_002e: call "void E.set_Item(S1, int, int)" IL_0033: nop IL_0034: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 49 (0x31) .maxstack 3 .locals init (S1& V_0, int V_1, int V_2, System.Index V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.3 IL_0009: ldloca.s V_3 IL_000b: ldloc.0 IL_000c: ldobj "S1" IL_0011: call "int E.get_Length(S1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: stloc.1 IL_001c: call "int Program.GetValue()" IL_0021: stloc.2 IL_0022: ldloc.0 IL_0023: ldobj "S1" IL_0028: ldloc.1 IL_0029: ldloc.2 IL_002a: call "void E.set_Item(S1, int, int)" IL_002f: nop IL_0030: ret } """); // receiver not a variable var src2 = """ static class E { extension(S1 x) { public int Length => 3; public int this[int i] { get => 0; set {} } } } struct S1; class Program { static void Test() { default(S1)[^1] = 1; } } """; var comp2 = CreateCompilation([src2], targetFramework: TargetFramework.Net100); comp2.VerifyDiagnostics( // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(16, 9) ); } [Fact] public void ImplicitIndexIndexerAccess_Set_01_02() { // struct receiver, instance this[int] + extension Length var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } public struct S1 { public int F1; public int this[int i] { set { System.Console.Write($"set:{F1} "); Program.F.F1++; } } public void Test2() { this[Program.GetIndex()] = Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] = GetValue(); } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetValue:5 set:6 final:7, GetIndex:3 length:4 GetValue:5 set:6 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 44 (0x2c) .maxstack 3 .locals init (S1& V_0, System.Index V_1) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: ldobj "S1" IL_0016: call "int E.get_Length(S1)" IL_001b: call "int System.Index.GetOffset(int)" IL_0020: call "int Program.GetValue()" IL_0025: call "void S1.this[int].set" IL_002a: nop IL_002b: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 40 (0x28) .maxstack 3 .locals init (S1& V_0, System.Index V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call "System.Index Program.GetIndex()" IL_0009: stloc.1 IL_000a: ldloca.s V_1 IL_000c: ldloc.0 IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: call "int System.Index.GetOffset(int)" IL_001c: call "int Program.GetValue()" IL_0021: call "void S1.this[int].set" IL_0026: nop IL_0027: ret } """); // receiver not a variable var src2 = """ static class E { extension(S1 x) { public int Length => 3; } } struct S1 { public int this[int i] { get => 0; set {} } } class Program { static void Test() { default(S1)[^1] = 1; } } """; var comp2 = CreateCompilation([src2], targetFramework: TargetFramework.Net100); comp2.VerifyDiagnostics( // (18,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(18, 9)); } [Fact] public void ImplicitIndexIndexerAccess_Set_01_03() { // struct receiver, extension this[int] + instance Length var src = """ static class E { extension(S1 x) { public int this[int i] { set { System.Console.Write($"set:{x.F1} "); Program.F.F1++; } } } } public struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 3; } } public void Test2() { this[Program.GetIndex()] = Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] = GetValue(); } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetValue:5 set:6 final:7, GetIndex:3 length:4 GetValue:5 set:6 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 48 (0x30) .maxstack 3 .locals init (S1& V_0, int V_1, int V_2, System.Index V_3) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.3 IL_000d: ldloca.s V_3 IL_000f: ldloc.0 IL_0010: call "int S1.Length.get" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: call "int Program.GetValue()" IL_0020: stloc.2 IL_0021: ldloc.0 IL_0022: ldobj "S1" IL_0027: ldloc.1 IL_0028: ldloc.2 IL_0029: call "void E.set_Item(S1, int, int)" IL_002e: nop IL_002f: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 44 (0x2c) .maxstack 3 .locals init (S1& V_0, int V_1, int V_2, System.Index V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.3 IL_0009: ldloca.s V_3 IL_000b: ldloc.0 IL_000c: call "int S1.Length.get" IL_0011: call "int System.Index.GetOffset(int)" IL_0016: stloc.1 IL_0017: call "int Program.GetValue()" IL_001c: stloc.2 IL_001d: ldloc.0 IL_001e: ldobj "S1" IL_0023: ldloc.1 IL_0024: ldloc.2 IL_0025: call "void E.set_Item(S1, int, int)" IL_002a: nop IL_002b: ret } """); // receiver not a variable var src2 = """ static class E { extension(S1 x) { public int this[int i] { get => 0; set {} } } } struct S1 { public int Length => 3; } class Program { static void Test() { default(S1)[^1] = 1; } } """; var comp2 = CreateCompilation([src2], targetFramework: TargetFramework.Net100); comp2.VerifyDiagnostics( // (18,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(18, 9)); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_Set_02(string refKind) { // sibling of IndexerAccess_Set_02 // struct receiver passed by ref, extension implicit this[Index] var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[Program.GetIndex()] = Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] = GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetValue:5 set:6 final:6, GetIndex:3 length:4 GetValue:5 set:6 final:6"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 39 (0x27) .maxstack 3 .locals init (S1& V_0, System.Index V_1) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: call "int E.get_Length({{refKind}} S1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: call "int Program.GetValue()" IL_0020: call "void E.set_Item({{refKind}} S1, int, int)" IL_0025: nop IL_0026: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 35 (0x23) .maxstack 3 .locals init (S1& V_0, System.Index V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call "System.Index Program.GetIndex()" IL_0009: stloc.1 IL_000a: ldloca.s V_1 IL_000c: ldloc.0 IL_000d: call "int E.get_Length({{refKind}} S1)" IL_0012: call "int System.Index.GetOffset(int)" IL_0017: call "int Program.GetValue()" IL_001c: call "void E.set_Item({{refKind}} S1, int, int)" IL_0021: nop IL_0022: ret } """); var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get => 0; set {} } public int Length => 3; } } struct S1; class Program { static void Test() { default(S1)[^1] = 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); if (refKind == "ref") { // One diagnostic for extension(ref default).Length and one for extension(ref default).this[int] comp2.VerifyDiagnostics( // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9) ); } else if (refKind == "ref readonly") { // One warning for extension(default).Length and one for extension(default).this[int] // Error for assignment target not being a variable comp2.VerifyDiagnostics( // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[^1] = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[^1] = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(16, 9) ); } else { // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyDiagnostics( // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(16, 9) ); } } [Fact] public void ImplicitIndexIndexerAccess_Set_03() { // sibling of IndexerAccess_Set_03 // class receiver, extension implicit this[Index] var src = """ static class E { extension(C1 x) { public int this[int i] { set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); return 3; } } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { F[GetIndex()] = GetValue(); } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 GetValue:4 set:3 final:5"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 39 (0x27) .maxstack 3 .locals init (C1 V_0, System.Index V_1) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: call "int E.get_Length(C1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: call "int Program.GetValue()" IL_0020: call "void E.set_Item(C1, int, int)" IL_0025: nop IL_0026: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_04() { // sibling of IndexerAccess_Set_04 // generic receiver passed by value, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //System.Console.Write(", "); //Program<S1>.F = new S1 { F1 = 3 }; //await Test3<S1>(); //System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) { f[GetIndex()] = GetValue(); } static void Test2<T>(ref T f) where T : struct { f[GetIndex()] = GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //static async Task Test3<T>() //{ // Program<T>.F[GetIndex()] = await GetValueAsync(); //} //static async Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetValue:5 set:6 final:6, GetIndex:3 length:4 GetValue:5 set:6 final:6"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); var src2 = """ static class E { extension<T>(T x) where T : struct { public int this[int i] { get => 0; set {} } public int Length => 3; } } class Program { static void Test<T>() where T : struct { default(T)[^1] = 1; } } namespace NS1 { static class E { extension<T>(in T x) where T : struct { } } } namespace NS2 { static class E { extension<T>(ref readonly T x) where T : struct { } } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); comp2.VerifyDiagnostics( // (14,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(T)[^1] = 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[^1]").WithLocation(14, 9), // (22,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(22, 25), // (32,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(ref readonly T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(32, 35)); } [Fact] public void ImplicitIndexIndexerAccess_Set_LValueReceiver_04_04() { // generic struct lvalue receiver passed by value, extension implicit this[Index], object creation, instance Property var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public Item this[int i] { get { System.Console.Write("get"); return new Item(); } } public int Length { get { System.Console.Write("length "); return 3; } } } } class Item { public int Property { get; set; } } struct S1 { } class Program { static async Task Main() { _ = Test1<S1>(); System.Console.Write(", "); _ = Test2<S1>(); System.Console.Write(", "); _ = await Test3<S1>(); } static T Test1<T>() where T : new() { return new T() { [GetIndex()] = { Property = 42 } }; } static T Test2<T>() where T : struct { return new T() { [GetIndex()] = { Property = 42 } }; } static System.Index GetIndex() { System.Console.Write("GetIndex "); return ^1; } static async Task<T> Test3<T>() where T : new() { return new T() { [await GetIndexAsync()] = { Property = 42 } }; } static async Task<System.Index> GetIndexAsync() { System.Console.Write("GetIndexAsync "); await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex length get, GetIndex length get, GetIndexAsync length get"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 93 (0x5d) .maxstack 2 .locals init (T V_0, int V_1, System.Index V_2, T V_3, T& V_4, int V_5, T V_6, T V_7) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length<T>(T)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloca.s V_0 IL_001d: stloc.s V_4 IL_001f: ldloca.s V_6 IL_0021: initobj "T" IL_0027: ldloc.s V_6 IL_0029: box "T" IL_002e: brtrue.s IL_003c IL_0030: ldloc.s V_4 IL_0032: ldobj "T" IL_0037: stloc.3 IL_0038: ldloca.s V_3 IL_003a: br.s IL_003e IL_003c: ldloc.s V_4 IL_003e: ldloc.1 IL_003f: stloc.s V_5 IL_0041: ldobj "T" IL_0046: ldloc.s V_5 IL_0048: call "Item E.get_Item<T>(T, int)" IL_004d: ldc.i4.s 42 IL_004f: callvirt "void Item.Property.set" IL_0054: nop IL_0055: ldloc.0 IL_0056: stloc.s V_7 IL_0058: br.s IL_005a IL_005a: ldloc.s V_7 IL_005c: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 58 (0x3a) .maxstack 2 .locals init (T V_0, System.Index V_1, T& V_2, int V_3, T V_4) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.1 IL_000d: ldloca.s V_1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length<T>(T)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: ldloca.s V_0 IL_001c: stloc.2 IL_001d: stloc.3 IL_001e: ldloc.2 IL_001f: ldobj "T" IL_0024: ldloc.3 IL_0025: call "Item E.get_Item<T>(T, int)" IL_002a: ldc.i4.s 42 IL_002c: callvirt "void Item.Property.set" IL_0031: nop IL_0032: ldloc.0 IL_0033: stloc.s V_4 IL_0035: br.s IL_0037 IL_0037: ldloc.s V_4 IL_0039: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_LValueReceiver_04_05() { // generic struct lvalue receiver passed by value, extension implicit this[Index], object creation, extension Property var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public T this[int i] { get { System.Console.Write("get "); return x; } } public int Length { get { System.Console.Write("length "); return 3; } } public int Property { set { System.Console.Write("set"); } } } } struct S1 { } class Program { static async Task Main() { _ = Test1<S1>(); System.Console.Write(", "); _ = await Test3<S1>(); } static T Test1<T>() where T : new() { return new T() { [GetIndex()] = { Property = 42 } }; } static System.Index GetIndex() { System.Console.Write("GetIndex "); return ^1; } static async Task<T> Test3<T>() where T : new() { return new T() { [await GetIndexAsync()] = { Property = 42 } }; } static async Task<System.Index> GetIndexAsync() { System.Console.Write("GetIndexAsync "); await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex length get set, GetIndexAsync length get set"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 93 (0x5d) .maxstack 2 .locals init (T V_0, int V_1, System.Index V_2, T V_3, T& V_4, int V_5, T V_6, T V_7) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length<T>(T)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloca.s V_0 IL_001d: stloc.s V_4 IL_001f: ldloca.s V_6 IL_0021: initobj "T" IL_0027: ldloc.s V_6 IL_0029: box "T" IL_002e: brtrue.s IL_003c IL_0030: ldloc.s V_4 IL_0032: ldobj "T" IL_0037: stloc.3 IL_0038: ldloca.s V_3 IL_003a: br.s IL_003e IL_003c: ldloc.s V_4 IL_003e: ldloc.1 IL_003f: stloc.s V_5 IL_0041: ldobj "T" IL_0046: ldloc.s V_5 IL_0048: call "T E.get_Item<T>(T, int)" IL_004d: ldc.i4.s 42 IL_004f: call "void E.set_Property<T>(T, int)" IL_0054: nop IL_0055: ldloc.0 IL_0056: stloc.s V_7 IL_0058: br.s IL_005a IL_005a: ldloc.s V_7 IL_005c: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_LValueReceiver_04_06() { // generic struct lvalue receiver passed by value, extension implicit this[Index], object creation var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { set { System.Console.Write("set"); } } public int Length { get { System.Console.Write("length "); return 3; } } } } struct S1 { } class Program { static async Task Main() { _ = Test1<S1>(); System.Console.Write(", "); _ = Test2<S1>(); System.Console.Write(", "); _ = await Test3<S1>(); } static T Test1<T>() where T : new() { return new T() { [GetIndex()] = 42 }; } static T Test2<T>() where T : struct { return new T() { [GetIndex()] = 42 }; } static System.Index GetIndex() { System.Console.Write("GetIndex "); return ^1; } static async Task<T> Test3<T>() where T : new() { return new T() { [await GetIndexAsync()] = 42 }; } static async Task<System.Index> GetIndexAsync() { System.Console.Write("GetIndexAsync "); await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex length set, GetIndex length set, GetIndexAsync length set"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 43 (0x2b) .maxstack 3 .locals init (T V_0, System.Index V_1, int V_2, T V_3) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.1 IL_000d: ldloca.s V_1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length<T>(T)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.2 IL_001b: ldloc.0 IL_001c: ldloc.2 IL_001d: ldc.i4.s 42 IL_001f: call "void E.set_Item<T>(T, int, int)" IL_0024: nop IL_0025: ldloc.0 IL_0026: stloc.3 IL_0027: br.s IL_0029 IL_0029: ldloc.3 IL_002a: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 43 (0x2b) .maxstack 3 .locals init (T V_0, System.Index V_1, int V_2, T V_3) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.1 IL_000d: ldloca.s V_1 IL_000f: ldloc.0 IL_0010: call "int E.get_Length<T>(T)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.2 IL_001b: ldloc.0 IL_001c: ldloc.2 IL_001d: ldc.i4.s 42 IL_001f: call "void E.set_Item<T>(T, int, int)" IL_0024: nop IL_0025: ldloc.0 IL_0026: stloc.3 IL_0027: br.s IL_0029 IL_0029: ldloc.3 IL_002a: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_05() { // sibling of IndexerAccess_Set_05 // struct receiver passed by ref, constrained to struct, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(ref T x) where T : struct { public int this[int i] { set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static async Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct { f[GetIndex()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } static async Task Test3<T>() where T : struct { Program<T>.F[GetIndex()] = await GetValueAsync(); } static async Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetValue:5 set:6 final:6, GetIndex:3 length:4 GetValueAsync:5 set:6 final:6"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); var src2 = """ static class E { extension<T>(ref T x) where T : struct { public int this[int i] { get => 0; set {} } public int Length => 3; } } class Program { static void Test<T>() where T : struct { default(T)[^1] = 1; } } namespace NS1 { static class E { extension<T>(in T x) where T : struct { } } } namespace NS2 { static class E { extension<T>(ref readonly T x) where T : struct { } } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); comp2.VerifyDiagnostics( // (14,9): error CS1510: A ref or out value must be an assignable variable // default(T)[^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), // (14,9): error CS1510: A ref or out value must be an assignable variable // default(T)[^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), // (22,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(22, 25), // (32,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(ref readonly T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(32, 35) ); } [Fact] public void ImplicitIndexIndexerAccess_Set_06() { // sibling of IndexerAccess_Set_06 // generic receiver (unconstrained and class-constrained), extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { set { System.Console.Write($"set:{((C1)(object)x).F1} "); } } public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F.F1++; return 3; } } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { static async Task Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(", "); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //System.Console.Write(", "); //Program<C1>.F = new C1 { F1 = 3 }; //await Test3<C1>(); //System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) { f[GetIndex()] = GetValue(); } static void Test2<T>(ref T f) where T : class { f[GetIndex()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<C1>.F.F1} "); Program<C1>.F.F1++; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<C1>.F.F1} "); Program<C1>.F.F1++; return ^1; } // https://github.com/dotnet/roslyn/issues/79416 - uncomment the following code once fixed //static async Task Test3<T>() //{ // Program<T>.F[GetIndex()] = await GetValueAsync(); //} //static async Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<C1>.F.F1} "); Program<C1>.F.F1++; await Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetValue:5 set:6 final:6, GetIndex:3 length:4 GetValue:5 set:6 final:6"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 83 (0x53) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, T V_3, int V_4, int V_5, System.Index V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 IL_0003: ldloca.s V_3 IL_0005: initobj "T" IL_000b: ldloc.3 IL_000c: box "T" IL_0011: brtrue.s IL_001e IL_0013: ldloc.2 IL_0014: ldobj "T" IL_0019: stloc.1 IL_001a: ldloca.s V_1 IL_001c: br.s IL_001f IL_001e: ldloc.2 IL_001f: stloc.0 IL_0020: call "System.Index Program.GetIndex()" IL_0025: stloc.s V_6 IL_0027: ldloca.s V_6 IL_0029: ldloc.0 IL_002a: ldobj "T" IL_002f: call "int E.get_Length<T>(T)" IL_0034: call "int System.Index.GetOffset(int)" IL_0039: stloc.s V_4 IL_003b: call "int Program.GetValue()" IL_0040: stloc.s V_5 IL_0042: ldloc.0 IL_0043: ldobj "T" IL_0048: ldloc.s V_4 IL_004a: ldloc.s V_5 IL_004c: call "void E.set_Item<T>(T, int, int)" IL_0051: nop IL_0052: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 40 (0x28) .maxstack 3 .locals init (T V_0, System.Index V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: call "System.Index Program.GetIndex()" IL_000e: stloc.1 IL_000f: ldloca.s V_1 IL_0011: ldloc.0 IL_0012: call "int E.get_Length<T>(T)" IL_0017: call "int System.Index.GetOffset(int)" IL_001c: call "int Program.GetValue()" IL_0021: call "void E.set_Item<T>(T, int, int)" IL_0026: nop IL_0027: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_07() { // sibling of IndexerAccess_Set_07 // generic rvalue struct receiver, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.State++; return 3; } } } } struct S1 { public int F1; } class Program { public static int State; static async Task Main() { State = 3; Test1<S1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<S1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new S1 { F1 = State }; static void Test1<T>() { GetT<T>()[GetIndex()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{State} "); State++; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } static async Task Test3<T>() { GetT<T>()[GetIndex()] = await GetValueAsync(); } static async Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{State} "); State++; await Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 GetValue:5 set:3 final:6, GetIndex:3 length:3 GetValueAsync:5 set:3 final:6"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 91 (0x5b) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, T V_3, T V_4, int V_5, int V_6, System.Index V_7) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.3 IL_0007: ldloca.s V_3 IL_0009: stloc.2 IL_000a: ldloca.s V_4 IL_000c: initobj "T" IL_0012: ldloc.s V_4 IL_0014: box "T" IL_0019: brtrue.s IL_0026 IL_001b: ldloc.2 IL_001c: ldobj "T" IL_0021: stloc.1 IL_0022: ldloca.s V_1 IL_0024: br.s IL_0027 IL_0026: ldloc.2 IL_0027: stloc.0 IL_0028: call "System.Index Program.GetIndex()" IL_002d: stloc.s V_7 IL_002f: ldloca.s V_7 IL_0031: ldloc.0 IL_0032: ldobj "T" IL_0037: call "int E.get_Length<T>(T)" IL_003c: call "int System.Index.GetOffset(int)" IL_0041: stloc.s V_5 IL_0043: call "int Program.GetValue()" IL_0048: stloc.s V_6 IL_004a: ldloc.0 IL_004b: ldobj "T" IL_0050: ldloc.s V_5 IL_0052: ldloc.s V_6 IL_0054: call "void E.set_Item<T>(T, int, int)" IL_0059: nop IL_005a: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_08() { // sibling of IndexerAccess_Set_08 // generic rvalue class receiver, extension implicit this[Index] var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int this[int i] { set { System.Console.Write($"set:{((C1)(object)x).F1} "); } } public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program.State++; return 3; } } } } class C1 { public int F1; } class Program { public static int State; static async Task Main() { State = 3; Test1<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; Test2<C1>(); System.Console.Write($"final:{State}"); System.Console.Write(", "); State = 3; await Test3<C1>(); System.Console.Write($"final:{State}"); } static T GetT<T>() => (T)(object)new C1 { F1 = State }; static void Test1<T>() { GetT<T>()[GetIndex()] = GetValue(); } static void Test2<T>() where T : class { GetT<T>()[GetIndex()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{State} "); State++; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{State} "); State++; return ^1; } static async Task Test3<T>() { GetT<T>()[GetIndex()] = await GetValueAsync(); } static async Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{State} "); State++; await Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 GetValue:5 set:3 final:6, GetIndex:3 length:3 GetValue:5 set:3 final:6, GetIndex:3 length:3 GetValueAsync:5 set:3 final:6"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 91 (0x5b) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, T V_3, T V_4, int V_5, int V_6, System.Index V_7) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.3 IL_0007: ldloca.s V_3 IL_0009: stloc.2 IL_000a: ldloca.s V_4 IL_000c: initobj "T" IL_0012: ldloc.s V_4 IL_0014: box "T" IL_0019: brtrue.s IL_0026 IL_001b: ldloc.2 IL_001c: ldobj "T" IL_0021: stloc.1 IL_0022: ldloca.s V_1 IL_0024: br.s IL_0027 IL_0026: ldloc.2 IL_0027: stloc.0 IL_0028: call "System.Index Program.GetIndex()" IL_002d: stloc.s V_7 IL_002f: ldloca.s V_7 IL_0031: ldloc.0 IL_0032: ldobj "T" IL_0037: call "int E.get_Length<T>(T)" IL_003c: call "int System.Index.GetOffset(int)" IL_0041: stloc.s V_5 IL_0043: call "int Program.GetValue()" IL_0048: stloc.s V_6 IL_004a: ldloc.0 IL_004b: ldobj "T" IL_0050: ldloc.s V_5 IL_0052: ldloc.s V_6 IL_0054: call "void E.set_Item<T>(T, int, int)" IL_0059: nop IL_005a: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 39 (0x27) .maxstack 3 .locals init (T V_0, System.Index V_1) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: call "int E.get_Length<T>(T)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: call "int Program.GetValue()" IL_0020: call "void E.set_Item<T>(T, int, int)" IL_0025: nop IL_0026: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Set_01() { // sibling of IndexerAccess_Set_01 // struct receiver, extension Length + ref-returning extension Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } public struct S1 { public int F1; public void Test2() { this[Program.GetRange()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] = GetValue(); } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 81 (0x51) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: stloc.1 IL_0018: ldloca.s V_0 IL_001a: call "System.Index System.Range.Start.get" IL_001f: stloc.s V_4 IL_0021: ldloca.s V_4 IL_0023: ldloc.1 IL_0024: call "int System.Index.GetOffset(int)" IL_0029: stloc.2 IL_002a: ldloca.s V_0 IL_002c: call "System.Index System.Range.End.get" IL_0031: stloc.s V_4 IL_0033: ldloca.s V_4 IL_0035: ldloc.1 IL_0036: call "int System.Index.GetOffset(int)" IL_003b: ldloc.2 IL_003c: sub IL_003d: stloc.3 IL_003e: ldobj "S1" IL_0043: ldloc.2 IL_0044: ldloc.3 IL_0045: call "ref int E.Slice(S1, int, int)" IL_004a: call "int Program.GetValue()" IL_004f: stind.i4 IL_0050: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 77 (0x4d) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: call "int E.get_Length(S1)" IL_0013: stloc.1 IL_0014: ldloca.s V_0 IL_0016: call "System.Index System.Range.Start.get" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: ldloc.1 IL_0020: call "int System.Index.GetOffset(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_0 IL_0028: call "System.Index System.Range.End.get" IL_002d: stloc.s V_4 IL_002f: ldloca.s V_4 IL_0031: ldloc.1 IL_0032: call "int System.Index.GetOffset(int)" IL_0037: ldloc.2 IL_0038: sub IL_0039: stloc.3 IL_003a: ldobj "S1" IL_003f: ldloc.2 IL_0040: ldloc.3 IL_0041: call "ref int E.Slice(S1, int, int)" IL_0046: call "int Program.GetValue()" IL_004b: stind.i4 IL_004c: ret } """); var src2 = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write("length "); return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{start},{length} "); return ref Program.Result; } } } struct S1; class Program { public static int Result; static void Main() { Result = 10; Test(); System.Console.Write($"result:{Result}"); } static void Test() { default(S1)[1..^1] = 1; } } """; var comp2 = CreateCompilation(src2, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier2 = CompileAndVerify(comp2, expectedOutput: ExpectedOutput("length Slice:1,3 result:1"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier2.VerifyIL("Program.Test", """ { // Code size 43 (0x2b) .maxstack 3 .locals init (int V_0, int V_1, S1 V_2) IL_0000: nop IL_0001: ldloca.s V_2 IL_0003: dup IL_0004: initobj "S1" IL_000a: ldc.i4.1 IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: ldc.i4.1 IL_0018: sub IL_0019: ldc.i4.1 IL_001a: sub IL_001b: stloc.1 IL_001c: ldobj "S1" IL_0021: ldloc.0 IL_0022: ldloc.1 IL_0023: call "ref int E.Slice(S1, int, int)" IL_0028: ldc.i4.1 IL_0029: stind.i4 IL_002a: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Set_01_02() { // sibling of IndexerAccess_Set_01 // struct receiver, instance Length + ref-returning extension Slice var src = """ static class E { extension(S1 x) { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } public struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 5; } } public void Test2() { this[Program.GetRange()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] = GetValue(); } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 76 (0x4c) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int S1.Length.get" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldobj "S1" IL_003e: ldloc.2 IL_003f: ldloc.3 IL_0040: call "ref int E.Slice(S1, int, int)" IL_0045: call "int Program.GetValue()" IL_004a: stind.i4 IL_004b: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 72 (0x48) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: call "int S1.Length.get" IL_000e: stloc.1 IL_000f: ldloca.s V_0 IL_0011: call "System.Index System.Range.Start.get" IL_0016: stloc.s V_4 IL_0018: ldloca.s V_4 IL_001a: ldloc.1 IL_001b: call "int System.Index.GetOffset(int)" IL_0020: stloc.2 IL_0021: ldloca.s V_0 IL_0023: call "System.Index System.Range.End.get" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: ldloc.1 IL_002d: call "int System.Index.GetOffset(int)" IL_0032: ldloc.2 IL_0033: sub IL_0034: stloc.3 IL_0035: ldobj "S1" IL_003a: ldloc.2 IL_003b: ldloc.3 IL_003c: call "ref int E.Slice(S1, int, int)" IL_0041: call "int Program.GetValue()" IL_0046: stind.i4 IL_0047: ret } """); var src2 = """ static class E { extension(S1 x) { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{start},{length} "); return ref Program.Result; } } } struct S1 { public int Length { get { System.Console.Write("length "); return 5; } } } class Program { public static int Result; static void Main() { Result = 10; Test(); System.Console.Write($"result:{Result}"); } static void Test() { default(S1)[1..^1] = 1; } } """; comp = CreateCompilation(src2, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("length Slice:1,3 result:1"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 38 (0x26) .maxstack 3 .locals init (int V_0, int V_1, S1 V_2) IL_0000: nop IL_0001: ldloca.s V_2 IL_0003: dup IL_0004: initobj "S1" IL_000a: ldc.i4.1 IL_000b: stloc.0 IL_000c: dup IL_000d: call "int S1.Length.get" IL_0012: ldc.i4.1 IL_0013: sub IL_0014: ldc.i4.1 IL_0015: sub IL_0016: stloc.1 IL_0017: ldobj "S1" IL_001c: ldloc.0 IL_001d: ldloc.1 IL_001e: call "ref int E.Slice(S1, int, int)" IL_0023: ldc.i4.1 IL_0024: stind.i4 IL_0025: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Set_01_03() { // sibling of IndexerAccess_Set_01 // struct receiver, extension Length + ref-returning instance Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } } } public struct S1 { public int F1; public ref int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F.F1++; return ref Program.Result; } public void Test2() { this[Program.GetRange()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] = GetValue(); } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.Fails) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 76 (0x4c) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: stloc.1 IL_0018: ldloca.s V_0 IL_001a: call "System.Index System.Range.Start.get" IL_001f: stloc.s V_4 IL_0021: ldloca.s V_4 IL_0023: ldloc.1 IL_0024: call "int System.Index.GetOffset(int)" IL_0029: stloc.2 IL_002a: ldloca.s V_0 IL_002c: call "System.Index System.Range.End.get" IL_0031: stloc.s V_4 IL_0033: ldloca.s V_4 IL_0035: ldloc.1 IL_0036: call "int System.Index.GetOffset(int)" IL_003b: ldloc.2 IL_003c: sub IL_003d: stloc.3 IL_003e: ldloc.2 IL_003f: ldloc.3 IL_0040: call "ref int S1.Slice(int, int)" IL_0045: call "int Program.GetValue()" IL_004a: stind.i4 IL_004b: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 72 (0x48) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: call "int E.get_Length(S1)" IL_0013: stloc.1 IL_0014: ldloca.s V_0 IL_0016: call "System.Index System.Range.Start.get" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: ldloc.1 IL_0020: call "int System.Index.GetOffset(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_0 IL_0028: call "System.Index System.Range.End.get" IL_002d: stloc.s V_4 IL_002f: ldloca.s V_4 IL_0031: ldloc.1 IL_0032: call "int System.Index.GetOffset(int)" IL_0037: ldloc.2 IL_0038: sub IL_0039: stloc.3 IL_003a: ldloc.2 IL_003b: ldloc.3 IL_003c: call "ref int S1.Slice(int, int)" IL_0041: call "int Program.GetValue()" IL_0046: stind.i4 IL_0047: ret } """); var src2 = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write("length "); return 5; } } } } struct S1 { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{start},{length} "); return ref Program.Result; } } class Program { public static int Result; static void Main() { Result = 10; Test(); System.Console.Write($"result:{Result}"); } static void Test() { default(S1)[1..^1] = 1; } } """; comp = CreateCompilation(src2, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("length Slice:1,3 result:1"), verify: Verification.Fails) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 36 (0x24) .maxstack 4 .locals init (S1& V_0, S1 V_1) IL_0000: nop IL_0001: ldloca.s V_1 IL_0003: dup IL_0004: initobj "S1" IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: ldc.i4.1 IL_000d: ldloc.0 IL_000e: ldobj "S1" IL_0013: call "int E.get_Length(S1)" IL_0018: ldc.i4.1 IL_0019: sub IL_001a: ldc.i4.1 IL_001b: sub IL_001c: call "ref int S1.Slice(int, int)" IL_0021: ldc.i4.1 IL_0022: stind.i4 IL_0023: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Set_02(string refKind) { // sibling of IndexerAccess_Set_02 // struct receiver passed by ref, extension Length + ref-returning extension Slice var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public void Test2() { this[Program.GetRange()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] = GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$$""" { // Code size 71 (0x47) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length({{{refKind}}} S1)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "ref int E.Slice({{{refKind}}} S1, int, int)" IL_0040: call "int Program.GetValue()" IL_0045: stind.i4 IL_0046: ret } """); verifier.VerifyIL("S1.Test2", $$$""" { // Code size 67 (0x43) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: call "int E.get_Length({{{refKind}}} S1)" IL_000e: stloc.1 IL_000f: ldloca.s V_0 IL_0011: call "System.Index System.Range.Start.get" IL_0016: stloc.s V_4 IL_0018: ldloca.s V_4 IL_001a: ldloc.1 IL_001b: call "int System.Index.GetOffset(int)" IL_0020: stloc.2 IL_0021: ldloca.s V_0 IL_0023: call "System.Index System.Range.End.get" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: ldloc.1 IL_002d: call "int System.Index.GetOffset(int)" IL_0032: ldloc.2 IL_0033: sub IL_0034: stloc.3 IL_0035: ldloc.2 IL_0036: ldloc.3 IL_0037: call "ref int E.Slice({{{refKind}}} S1, int, int)" IL_003c: call "int Program.GetValue()" IL_0041: stind.i4 IL_0042: ret } """); var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write("length "); return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{start},{length} "); return ref Program.Result; } } } struct S1; class Program { public static int Result; static void Main() { Result = 10; Test(); System.Console.Write($"result:{Result}"); } static void Test() { default(S1)[1..^1] = 1; } } """; var comp2 = CreateCompilation(src2, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp2.VerifyDiagnostics( // (25,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(25, 9), // (25,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(25, 9)); break; case "ref readonly": verifier = CompileAndVerify(comp2, expectedOutput: ExpectedOutput("length Slice:1,3 result:1"), verify: Verification.FailsPEVerify) .VerifyDiagnostics( // (25,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(25, 9), // (25,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(25, 9), // (25,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(25, 9)); break; case "in": verifier = CompileAndVerify(comp2, expectedOutput: ExpectedOutput("length Slice:1,3 result:1"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Set_02_02(string refKind) { // struct receiver passed by ref, instance Length + ref-returning extension Slice var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 5; } } public void Test2() { this[Program.GetRange()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] = GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{start},{length} "); return ref Program.Result; } } } struct S1 { public int Length { get { System.Console.Write("length "); return 5; } } } class Program { public static int Result = 10; static void Main() { default(S1)[1..^1] = 1; } } """; var comp2 = CreateCompilation(src2, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp2.VerifyEmitDiagnostics( // (19,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(19, 9)); break; case "ref readonly": CompileAndVerify(comp2, expectedOutput: ExpectedOutput("length Slice:1,3"), verify: Verification.FailsPEVerify) .VerifyDiagnostics( // (19,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(19, 9)); break; case "in": CompileAndVerify(comp2, expectedOutput: ExpectedOutput("length Slice:1,3"), verify: Verification.FailsPEVerify); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_Set_02_03(string refKind) { // struct receiver passed by ref, extension Length + ref-returning instance Slice var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } } } struct S1 { public int F1; public ref int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F.F1++; return ref Program.Result; } public void Test2() { this[Program.GetRange()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] = GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.Fails) .VerifyDiagnostics(); var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write("length "); return 5; } } } } struct S1 { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{start},{length} "); return ref Program.Result; } } class Program { public static int Result = 10; static void Main() { default(S1)[1..^1] = 1; } } """; var comp2 = CreateCompilation(src2, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp2.VerifyEmitDiagnostics( // (19,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(19, 9)); break; case "ref readonly": CompileAndVerify(comp2, expectedOutput: ExpectedOutput("length Slice:1,3"), verify: Verification.Fails) .VerifyDiagnostics( // (19,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] = 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(19, 9)); break; case "in": CompileAndVerify(comp2, expectedOutput: ExpectedOutput("length Slice:1,3"), verify: Verification.Fails) .VerifyDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Fact] public void ImplicitRangeIndexerAccess_Set_03() { // sibling of IndexerAccess_Set_03 // class receiver, extension Length + ref-returning extension Slice var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ref Program.Result; } } } class C1 { public int F1; } class Program { public static C1 F; public static int Result; static void Main() { Result = 10; F = new C1 { F1 = 3 }; Test(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test() { F[GetRange()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Set_03_02() { // class receiver, instance Length + ref-returning extension Slice var src = """ static class E { extension(C1 x) { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ref Program.Result; } } } class C1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } } class Program { public static C1 F; public static int Result; static void Main() { Result = 10; F = new C1 { F1 = 3 }; Test(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test() { F[GetRange()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Set_03_03() { // class receiver, extension Length + ref-returning instance Slice var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } } } class C1 { public int F1; public ref int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ref Program.Result; } } class Program { public static C1 F; public static int Result; static void Main() { Result = 10; F = new C1 { F1 = 3 }; Test(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test() { F[GetRange()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:1 final:7"), verify: Verification.Fails) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Set_04() { // sibling of IndexerAccess_Set_04 // generic extension parameter, extension Length + ref-returning extension Slice, struct-constrained receiver value var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { public static int Result; static void Main() { Result = 10; Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"result:{Result} final:{Program<S1>.F.F1}"); System.Console.Write(", "); Result = 10; Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"result:{Result} final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) { f[GetRange()] = GetValue(); } static void Test2<T>(ref T f) where T : struct { f[GetRange()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); // receiver is not a variable var src2 = """ static class E { extension<T>(T x) where T : struct { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{start},{length} "); return ref Program.Result; } public int Length { get { System.Console.Write("length "); return 5; } } } } class Program { public static int Result = 10; static void Main() { Test<int>(); } static void Test<T>() where T : struct { default(T)[1..^1] = 1; } } """; var comp2 = CreateCompilation(src2, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp2, expectedOutput: ExpectedOutput("length Slice:1,3"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Set_LValueReceiver_04_04() { // generic struct lvalue receiver passed by value, extension implicit this[Range], object creation var src = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public ref int Slice(int i, int j) { System.Console.Write("slice"); return ref Program.F; } public int Length { get { System.Console.Write("length "); return 3; } } } } struct S1 { } class Program { public static int F; static async Task Main() { S1 s1 = Test1<S1>(); System.Console.Write(", "); S1 s2 = Test2<S1>(); System.Console.Write(", "); S1 s3 = await Test3<S1>(); } static T Test1<T>() where T : new() { return new T() { [GetRange()] = 42 }; } static T Test2<T>() where T : struct { return new T() { [GetRange()] = 42 }; } static System.Range GetRange() { System.Console.Write("GetRange "); return ..^1; } static async Task<T> Test3<T>() where T : new() { return new T() { [await GetRangeAsync()] = 42 }; } static async Task<System.Range> GetRangeAsync() { System.Console.Write("GetRangeAsync "); await Task.Yield(); return ..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange length slice, GetRange length slice, GetRangeAsync length slice"), verify: Verification.Skipped) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 126 (0x7e) .maxstack 3 .locals init (T V_0, T V_1, T& V_2, System.Range V_3, int V_4, int V_5, int V_6, T V_7, System.Index V_8, T V_9) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: stloc.2 IL_000a: ldloca.s V_7 IL_000c: initobj "T" IL_0012: ldloc.s V_7 IL_0014: box "T" IL_0019: brtrue.s IL_0026 IL_001b: ldloc.2 IL_001c: ldobj "T" IL_0021: stloc.1 IL_0022: ldloca.s V_1 IL_0024: br.s IL_0027 IL_0026: ldloc.2 IL_0027: call "System.Range Program.GetRange()" IL_002c: stloc.3 IL_002d: dup IL_002e: ldobj "T" IL_0033: call "int E.get_Length<T>(T)" IL_0038: stloc.s V_4 IL_003a: ldloca.s V_3 IL_003c: call "System.Index System.Range.Start.get" IL_0041: stloc.s V_8 IL_0043: ldloca.s V_8 IL_0045: ldloc.s V_4 IL_0047: call "int System.Index.GetOffset(int)" IL_004c: stloc.s V_5 IL_004e: ldloca.s V_3 IL_0050: call "System.Index System.Range.End.get" IL_0055: stloc.s V_8 IL_0057: ldloca.s V_8 IL_0059: ldloc.s V_4 IL_005b: call "int System.Index.GetOffset(int)" IL_0060: ldloc.s V_5 IL_0062: sub IL_0063: stloc.s V_6 IL_0065: ldobj "T" IL_006a: ldloc.s V_5 IL_006c: ldloc.s V_6 IL_006e: call "ref int E.Slice<T>(T, int, int)" IL_0073: ldc.i4.s 42 IL_0075: stind.i4 IL_0076: ldloc.0 IL_0077: stloc.s V_9 IL_0079: br.s IL_007b IL_007b: ldloc.s V_9 IL_007d: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 90 (0x5a) .maxstack 3 .locals init (T V_0, System.Range V_1, int V_2, int V_3, int V_4, System.Index V_5, T V_6) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: call "System.Range Program.GetRange()" IL_000e: stloc.1 IL_000f: dup IL_0010: ldobj "T" IL_0015: call "int E.get_Length<T>(T)" IL_001a: stloc.2 IL_001b: ldloca.s V_1 IL_001d: call "System.Index System.Range.Start.get" IL_0022: stloc.s V_5 IL_0024: ldloca.s V_5 IL_0026: ldloc.2 IL_0027: call "int System.Index.GetOffset(int)" IL_002c: stloc.3 IL_002d: ldloca.s V_1 IL_002f: call "System.Index System.Range.End.get" IL_0034: stloc.s V_5 IL_0036: ldloca.s V_5 IL_0038: ldloc.2 IL_0039: call "int System.Index.GetOffset(int)" IL_003e: ldloc.3 IL_003f: sub IL_0040: stloc.s V_4 IL_0042: ldobj "T" IL_0047: ldloc.3 IL_0048: ldloc.s V_4 IL_004a: call "ref int E.Slice<T>(T, int, int)" IL_004f: ldc.i4.s 42 IL_0051: stind.i4 IL_0052: ldloc.0 IL_0053: stloc.s V_6 IL_0055: br.s IL_0057 IL_0057: ldloc.s V_6 IL_0059: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Set_05() { // sibling of IndexerAccess_Set_05 // ref struct-constrained extension parameter, extension Length + ref-returning extension Slice var src = """ static class E { extension<T>(ref T x) where T : struct { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { public static int Result; static void Main() { Result = 10; Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"result:{Result} final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct { f[GetRange()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); // spilling for await var src2 = """ using System.Threading.Tasks; static class E { extension<T>(ref T x) where T : struct { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } class Program<T> { public static T F; } class Program { static async Task Test3<T>() where T : struct { Program<T>.F[GetRange()] = await GetValueAsync(); } static System.Range GetRange() => 1..^1; static async Task<int> GetValueAsync() { await Task.Yield(); return 1; } } """; comp = CreateCompilation(src2, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (21,9): error CS8178: A reference returned by a call to 'E.Slice<T>(ref T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // Program<T>.F[GetRange()] = await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "Program<T>.F[GetRange()]").WithArguments("E.Slice<T>(ref T, int, int)").WithLocation(21, 9)); comp = CreateRuntimeAsyncCompilation(src2); comp.VerifyEmitDiagnostics( // (21,9): error CS8178: A reference returned by a call to 'E.Slice<T>(ref T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // Program<T>.F[GetRange()] = await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "Program<T>.F[GetRange()]").WithArguments("E.Slice<T>(ref T, int, int)").WithLocation(21, 9)); // receiver is not a variable var src3 = """ static class E { extension<T>(ref T x) where T : struct { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } class Program { static void Test<T>() where T : struct { default(T)[1..^1] = 1; } } """; CreateCompilation(src3, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (14,9): error CS1510: A ref or out value must be an assignable variable // default(T)[1..^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), // (14,9): error CS1510: A ref or out value must be an assignable variable // default(T)[1..^1] = 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9)); } [Fact] public void ImplicitRangeIndexerAccess_Set_06() { // sibling of IndexerAccess_Set_06 // generic extension parameter, extension Length + ref-returning extension Slice, class-constrained receiver value var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return ref Program.Result; } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { public static int Result; static void Main() { Result = 10; Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"result:{Result} final:{Program<C1>.F.F1}"); System.Console.Write(", "); Result = 10; Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"result:{Result} final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) { f[GetRange()] = GetValue(); } static void Test2<T>(ref T f) where T : class { f[GetRange()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:1 final:7, GetRange:3 length:3 Slice:3 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_Set_07() { // sibling of IndexerAccess_Set_07 // generic rvalue struct receiver, extension Length + ref-returning extension Slice var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.State++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program.State++; return ref Program.Result; } } } struct S1 { public int F1; } class Program { static T GetT<T>() => (T)(object)new S1 { F1 = State }; public static int Result; public static int State; static void Main() { Result = 10; State = 3; Test1<S1>(); System.Console.Write($"result:{Result} final:{State}"); } static void Test1<T>() { GetT<T>()[GetRange()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{State} "); State++; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); // spilling for await var src2 = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } class Program { static T GetT<T>() => default; static async Task Test3<T>() { GetT<T>()[GetRange()] = await GetValueAsync(); } static System.Range GetRange() => 1..^1; static async Task<int> GetValueAsync() { await Task.Yield(); return 1; } } """; comp = CreateCompilation(src2, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (18,9): error CS8178: A reference returned by a call to 'E.Slice<T>(T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // GetT<T>()[GetRange()] = await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "GetT<T>()[GetRange()]").WithArguments("E.Slice<T>(T, int, int)").WithLocation(18, 9)); comp = CreateRuntimeAsyncCompilation(src2); comp.VerifyEmitDiagnostics( // (18,9): error CS8178: A reference returned by a call to 'E.Slice<T>(T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // GetT<T>()[GetRange()] = await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "GetT<T>()[GetRange()]").WithArguments("E.Slice<T>(T, int, int)").WithLocation(18, 9)); } [Fact] public void ImplicitRangeIndexerAccess_Set_08() { // sibling of IndexerAccess_Set_08 // generic rvalue class receiver, extension Length + ref-returning extension Slice var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program.State++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program.State++; return ref Program.Result; } } } class C1 { public int F1; } class Program { static T GetT<T>() => (T)(object)new C1 { F1 = State }; public static int Result; public static int State; static void Main() { Result = 10; State = 3; Test1<C1>(); System.Console.Write($"result:{Result} final:{State}"); System.Console.Write(", "); Result = 10; State = 3; Test2<C1>(); System.Console.Write($"result:{Result} final:{State}"); } static void Test1<T>() { GetT<T>()[GetRange()] = GetValue(); } static void Test2<T>() where T : class { GetT<T>()[GetRange()] = GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{State} "); State++; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{State} "); State++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:1 final:7, GetRange:3 length:3 Slice:3 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 120 (0x78) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, T V_7, System.Index V_8) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: stloc.s V_6 IL_0008: ldloca.s V_6 IL_000a: stloc.1 IL_000b: ldloca.s V_7 IL_000d: initobj "T" IL_0013: ldloc.s V_7 IL_0015: box "T" IL_001a: brtrue.s IL_0027 IL_001c: ldloc.1 IL_001d: ldobj "T" IL_0022: stloc.0 IL_0023: ldloca.s V_0 IL_0025: br.s IL_0028 IL_0027: ldloc.1 IL_0028: call "System.Range Program.GetRange()" IL_002d: stloc.2 IL_002e: dup IL_002f: ldobj "T" IL_0034: call "int E.get_Length<T>(T)" IL_0039: stloc.3 IL_003a: ldloca.s V_2 IL_003c: call "System.Index System.Range.Start.get" IL_0041: stloc.s V_8 IL_0043: ldloca.s V_8 IL_0045: ldloc.3 IL_0046: call "int System.Index.GetOffset(int)" IL_004b: stloc.s V_4 IL_004d: ldloca.s V_2 IL_004f: call "System.Index System.Range.End.get" IL_0054: stloc.s V_8 IL_0056: ldloca.s V_8 IL_0058: ldloc.3 IL_0059: call "int System.Index.GetOffset(int)" IL_005e: ldloc.s V_4 IL_0060: sub IL_0061: stloc.s V_5 IL_0063: ldobj "T" IL_0068: ldloc.s V_4 IL_006a: ldloc.s V_5 IL_006c: call "ref int E.Slice<T>(T, int, int)" IL_0071: call "int Program.GetValue()" IL_0076: stind.i4 IL_0077: ret } """); verifier.VerifyIL("Program.Test2<T>()", """ { // Code size 71 (0x47) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: call "T Program.GetT<T>()" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length<T>(T)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "ref int E.Slice<T>(T, int, int)" IL_0040: call "int Program.GetValue()" IL_0045: stind.i4 IL_0046: ret } """); // spilling for await var src2 = """ using System.Threading.Tasks; static class E { extension<T>(T x) { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } class Program { static T GetT<T>() => default; static async Task Test3<T>() { GetT<T>()[GetRange()] = await GetValueAsync(); } static System.Range GetRange() => 1..^1; static async Task<int> GetValueAsync() { await Task.Yield(); return 1; } } """; comp = CreateCompilation(src2, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (18,9): error CS8178: A reference returned by a call to 'E.Slice<T>(T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // GetT<T>()[GetRange()] = await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "GetT<T>()[GetRange()]").WithArguments("E.Slice<T>(T, int, int)").WithLocation(18, 9)); comp = CreateRuntimeAsyncCompilation(src2); comp.VerifyEmitDiagnostics( // (18,9): error CS8178: A reference returned by a call to 'E.Slice<T>(T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // GetT<T>()[GetRange()] = await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "GetT<T>()[GetRange()]").WithArguments("E.Slice<T>(T, int, int)").WithLocation(18, 9)); } [Fact] public void ImplicitRangeIndexerAccess_Set_IntStartOpenEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Set_01 // struct receiver, extension Length + ref-returning extension Slice, slice from int start, open end (start..) var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } public struct S1 { public int F1; public void Test2() { this[Program.GetStart()..] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetStart()..] = GetValue(); } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetStart:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 47 (0x2f) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetStart()" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: stloc.1 IL_000e: dup IL_000f: ldobj "S1" IL_0014: call "int E.get_Length(S1)" IL_0019: ldloc.0 IL_001a: sub IL_001b: stloc.2 IL_001c: ldobj "S1" IL_0021: ldloc.1 IL_0022: ldloc.2 IL_0023: call "ref int E.Slice(S1, int, int)" IL_0028: call "int Program.GetValue()" IL_002d: stind.i4 IL_002e: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 43 (0x2b) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetStart()" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: stloc.1 IL_000a: dup IL_000b: ldobj "S1" IL_0010: call "int E.get_Length(S1)" IL_0015: ldloc.0 IL_0016: sub IL_0017: stloc.2 IL_0018: ldobj "S1" IL_001d: ldloc.1 IL_001e: ldloc.2 IL_001f: call "ref int E.Slice(S1, int, int)" IL_0024: call "int Program.GetValue()" IL_0029: stind.i4 IL_002a: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Set_HatStartOpenEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Set_IntStartOpenEndRangeExpr_01 // struct receiver, extension Length + ref-returning extension Slice, slice from hat start, open end (^end..) var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } public struct S1 { public int F1; public void Test2() { this[^Program.GetStart()..] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[^GetStart()..] = GetValue(); } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetStart:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 51 (0x33) .maxstack 4 .locals init (int V_0, int V_1, int V_2, int V_3) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetStart()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: dup IL_0018: ldloc.0 IL_0019: sub IL_001a: stloc.1 IL_001b: ldloc.1 IL_001c: stloc.2 IL_001d: ldloc.1 IL_001e: sub IL_001f: stloc.3 IL_0020: ldobj "S1" IL_0025: ldloc.2 IL_0026: ldloc.3 IL_0027: call "ref int E.Slice(S1, int, int)" IL_002c: call "int Program.GetValue()" IL_0031: stind.i4 IL_0032: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 47 (0x2f) .maxstack 4 .locals init (int V_0, int V_1, int V_2, int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetStart()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: call "int E.get_Length(S1)" IL_0013: dup IL_0014: ldloc.0 IL_0015: sub IL_0016: stloc.1 IL_0017: ldloc.1 IL_0018: stloc.2 IL_0019: ldloc.1 IL_001a: sub IL_001b: stloc.3 IL_001c: ldobj "S1" IL_0021: ldloc.2 IL_0022: ldloc.3 IL_0023: call "ref int E.Slice(S1, int, int)" IL_0028: call "int Program.GetValue()" IL_002d: stind.i4 IL_002e: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Set_IntStartIntEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Set_IntStartOpenEndRangeExpr_01 // struct receiver, extension Length + ref-returning extension Slice, slice from int start to int end (start..end) var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } public struct S1 { public int F1; public void Test2() { this[Program.GetStart()..Program.GetEnd()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetStart()..GetEnd()] = GetValue(); } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetEnd() { System.Console.Write($"GetEnd:{Program.F.F1} "); Program.F.F1++; return 4; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 GetEnd:4 Slice:5 GetValue:6 result:1 final:7, GetStart:3 GetEnd:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 41 (0x29) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetStart()" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: stloc.1 IL_000e: call "int Program.GetEnd()" IL_0013: ldloc.0 IL_0014: sub IL_0015: stloc.2 IL_0016: ldobj "S1" IL_001b: ldloc.1 IL_001c: ldloc.2 IL_001d: call "ref int E.Slice(S1, int, int)" IL_0022: call "int Program.GetValue()" IL_0027: stind.i4 IL_0028: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 37 (0x25) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetStart()" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: stloc.1 IL_000a: call "int Program.GetEnd()" IL_000f: ldloc.0 IL_0010: sub IL_0011: stloc.2 IL_0012: ldobj "S1" IL_0017: ldloc.1 IL_0018: ldloc.2 IL_0019: call "ref int E.Slice(S1, int, int)" IL_001e: call "int Program.GetValue()" IL_0023: stind.i4 IL_0024: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Set_IntStartIndexEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Set_IntStartOpenEndRangeExpr_01 // struct receiver, extension Length + ref-returning extension Slice, slice from int start to Index end (start..^end) var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } public struct S1 { public int F1; public void Test2() { this[Program.GetStart()..^Program.GetEnd()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetStart()..^GetEnd()] = GetValue(); } public static int GetStart() { System.Console.Write($"GetStart:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetEnd() { System.Console.Write($"GetEnd:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetStart:3 GetEnd:4 length:5 Slice:6 GetValue:7 result:1 final:8, GetStart:3 GetEnd:4 length:5 Slice:6 GetValue:7 result:1 final:8"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 55 (0x37) .maxstack 3 .locals init (int V_0, int V_1, int V_2, int V_3) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetStart()" IL_000b: stloc.0 IL_000c: call "int Program.GetEnd()" IL_0011: stloc.1 IL_0012: ldloc.0 IL_0013: stloc.2 IL_0014: dup IL_0015: ldobj "S1" IL_001a: call "int E.get_Length(S1)" IL_001f: ldloc.1 IL_0020: sub IL_0021: ldloc.0 IL_0022: sub IL_0023: stloc.3 IL_0024: ldobj "S1" IL_0029: ldloc.2 IL_002a: ldloc.3 IL_002b: call "ref int E.Slice(S1, int, int)" IL_0030: call "int Program.GetValue()" IL_0035: stind.i4 IL_0036: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 51 (0x33) .maxstack 3 .locals init (int V_0, int V_1, int V_2, int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetStart()" IL_0007: stloc.0 IL_0008: call "int Program.GetEnd()" IL_000d: stloc.1 IL_000e: ldloc.0 IL_000f: stloc.2 IL_0010: dup IL_0011: ldobj "S1" IL_0016: call "int E.get_Length(S1)" IL_001b: ldloc.1 IL_001c: sub IL_001d: ldloc.0 IL_001e: sub IL_001f: stloc.3 IL_0020: ldobj "S1" IL_0025: ldloc.2 IL_0026: ldloc.3 IL_0027: call "ref int E.Slice(S1, int, int)" IL_002c: call "int Program.GetValue()" IL_0031: stind.i4 IL_0032: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_Set_OpenStartIndexEndRangeExpr_01() { // sibling of ImplicitRangeIndexerAccess_Set_IntStartOpenEndRangeExpr_01 // struct receiver, extension Length + ref-returning extension Slice, slice from open start to Index end (..^end) var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } public struct S1 { public int F1; public void Test2() { this[..^Program.GetEnd()] = Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[..^GetEnd()] = GetValue(); } public static int GetEnd() { System.Console.Write($"GetEnd:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetEnd:3 length:4 Slice:5 GetValue:6 result:1 final:7, GetEnd:3 length:4 Slice:5 GetValue:6 result:1 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 47 (0x2f) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetEnd()" IL_000b: stloc.0 IL_000c: ldc.i4.0 IL_000d: stloc.1 IL_000e: dup IL_000f: ldobj "S1" IL_0014: call "int E.get_Length(S1)" IL_0019: ldloc.0 IL_001a: sub IL_001b: stloc.2 IL_001c: ldobj "S1" IL_0021: ldloc.1 IL_0022: ldloc.2 IL_0023: call "ref int E.Slice(S1, int, int)" IL_0028: call "int Program.GetValue()" IL_002d: stind.i4 IL_002e: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 43 (0x2b) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetEnd()" IL_0007: stloc.0 IL_0008: ldc.i4.0 IL_0009: stloc.1 IL_000a: dup IL_000b: ldobj "S1" IL_0010: call "int E.get_Length(S1)" IL_0015: ldloc.0 IL_0016: sub IL_0017: stloc.2 IL_0018: ldobj "S1" IL_001d: ldloc.1 IL_001e: ldloc.2 IL_001f: call "ref int E.Slice(S1, int, int)" IL_0024: call "int Program.GetValue()" IL_0029: stind.i4 IL_002a: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_LValueReceiver_04_06_01() { // sibling of ImplicitIndexIndexerAccess_Set_LValueReceiver_04_06 with non-generic struct receiver var src = """ using System.Threading.Tasks; static class E { extension(S1 x) { public int this[int i] { set { System.Console.Write("set"); } } public int Length { get { System.Console.Write("length "); return 3; } } } } struct S1 { } class Program { static async Task Main() { _ = Test1(); System.Console.Write(", "); _ = await Test2(); } static S1 Test1() { return new S1() { [GetIndex()] = 42 }; } static System.Index GetIndex() { System.Console.Write("GetIndex "); return ^1; } static async Task<S1> Test2() { return new S1() { [await GetIndexAsync()] = 42 }; } static async Task<System.Index> GetIndexAsync() { System.Console.Write("GetIndexAsync "); await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex length set, GetIndexAsync length set"), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 45 (0x2d) .maxstack 3 .locals init (S1 V_0, System.Index V_1, int V_2, S1 V_3) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: initobj "S1" IL_0009: call "System.Index Program.GetIndex()" IL_000e: stloc.1 IL_000f: ldloca.s V_1 IL_0011: ldloc.0 IL_0012: call "int E.get_Length(S1)" IL_0017: call "int System.Index.GetOffset(int)" IL_001c: stloc.2 IL_001d: ldloc.0 IL_001e: ldloc.2 IL_001f: ldc.i4.s 42 IL_0021: call "void E.set_Item(S1, int, int)" IL_0026: nop IL_0027: ldloc.0 IL_0028: stloc.3 IL_0029: br.s IL_002b IL_002b: ldloc.3 IL_002c: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_LValueReceiver_04_06_02() { // sibling of ImplicitIndexIndexerAccess_Set_LValueReceiver_04_06 with non-generic class receiver var src = """ using System.Threading.Tasks; static class E { extension(C1 x) { public int this[int i] { set { System.Console.Write("set"); } } public int Length { get { System.Console.Write("length "); return 3; } } } } class C1 { } class Program { static async Task Main() { _ = Test1(); System.Console.Write(", "); _ = await Test2(); } static C1 Test1() { return new C1() { [GetIndex()] = 42 }; } static System.Index GetIndex() { System.Console.Write("GetIndex "); return ^1; } static async Task<C1> Test2() { return new C1() { [await GetIndexAsync()] = 42 }; } static async Task<System.Index> GetIndexAsync() { System.Console.Write("GetIndexAsync "); await Task.Yield(); return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex length set, GetIndexAsync length set"), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 43 (0x2b) .maxstack 3 .locals init (C1 V_0, int V_1, System.Index V_2, C1 V_3) IL_0000: nop IL_0001: newobj "C1..ctor()" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length(C1)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: ldc.i4.s 42 IL_001f: call "void E.set_Item(C1, int, int)" IL_0024: nop IL_0025: ldloc.0 IL_0026: stloc.3 IL_0027: br.s IL_0029 IL_0029: ldloc.3 IL_002a: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_RValueReceiver_01() { // sibling of ImplicitIndexIndexerAccess_Get_RValueReceiver_01 // struct rvalue receiver, extension implicit this[Index] setter (CS1612 error). // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members var src = """ static class E { extension(S1 x) { public int Length { get { return 3; } } public int this[int i] { set { } } } } public struct S1 { } class Program { static S1 GetS1() => default; static void Main() { GetS1()[^1] = 42; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (17,9): error CS1612: Cannot modify the return value of 'Program.GetS1()' because it is not a variable // GetS1()[^1] = 42; Diagnostic(ErrorCode.ERR_ReturnNotLValue, "GetS1()").WithArguments("Program.GetS1()").WithLocation(18, 9)); } [Fact] public void ImplicitIndexIndexerAccess_Set_RValueReceiver_03() { // sibling of ImplicitIndexIndexerAccess_Get_RValueReceiver_03 // class rvalue receiver, extension implicit this[Index] setter var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); return 3; } } public int this[int i] { set { System.Console.Write($"set:{x.F1},i={i},v={value} "); } } } } public class C1 { public int F1; } class Program { static void Main() { GetC1()[^GetStart()] = GetValue(); } static C1 GetC1() { System.Console.Write("GetC1 "); return new C1 { F1 = 7 }; } static int GetStart() { System.Console.Write("GetStart "); return 1; } static int GetValue() { System.Console.Write("GetValue "); return 42; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetC1 GetStart length:7 GetValue set:7,i=2,v=42"), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 32 (0x20) .maxstack 3 .locals init (int V_0) IL_0000: nop IL_0001: call "C1 Program.GetC1()" IL_0006: call "int Program.GetStart()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length(C1)" IL_0012: ldloc.0 IL_0013: sub IL_0014: call "int Program.GetValue()" IL_0019: call "void E.set_Item(C1, int, int)" IL_001e: nop IL_001f: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_LValueReceiver_04_02_02() { // sibling of ImplicitIndexIndexerAccess_Get_LValueReceiver_04_02_02 // generic struct lvalue receiver passed by value, instance Length (via interface) + extension this[int], // struct constraint var src = """ public interface IHasLength { int Length { get; } } public static class E { extension<T>(T x) where T : struct, IHasLength { public int this[int i] { set { System.Console.Write($"set,i={i},v={value} "); } } } } public struct S1 : IHasLength { public int Length { get { System.Console.Write("length "); return 3; } } } class Program { static void Main() { Test<S1>(); } static void Test<T>() where T : struct, IHasLength { var t = default(T); t[^1] = 42; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("length set,i=2,v=42"), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test<T>()", """ { // Code size 41 (0x29) .maxstack 3 .locals init (T V_0, //t int V_1) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: initobj "T" IL_0009: ldloca.s V_0 IL_000b: dup IL_000c: constrained. "T" IL_0012: callvirt "int IHasLength.Length.get" IL_0017: ldc.i4.1 IL_0018: sub IL_0019: stloc.1 IL_001a: ldobj "T" IL_001f: ldloc.1 IL_0020: ldc.i4.s 42 IL_0022: call "void E.set_Item<T>(T, int, int)" IL_0027: nop IL_0028: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_LValueReceiver_04_02_03() { // sibling of ImplicitIndexIndexerAccess_Get_LValueReceiver_04_02_03 // generic class lvalue receiver passed by value, instance Length (via interface) + extension this[int], // class constraint var src = """ public interface IHasLength { int Length { get; } } public static class E { extension<T>(T x) where T : class, IHasLength { public int this[int i] { set { System.Console.Write($"set,i={i},v={value} "); } } } } public class C1 : IHasLength { public int Length { get { System.Console.Write("length "); return 3; } } } class Program { static void Main() { Test<C1>(); } static void Test<T>() where T : class, IHasLength, new() { new T() { [^1] = 42 }; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("length set,i=2,v=42"), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test<T>()", """ { // Code size 30 (0x1e) .maxstack 3 .locals init (int V_0) IL_0000: nop IL_0001: call "T System.Activator.CreateInstance<T>()" IL_0006: dup IL_0007: box "T" IL_000c: callvirt "int IHasLength.Length.get" IL_0011: ldc.i4.1 IL_0012: sub IL_0013: stloc.0 IL_0014: ldloc.0 IL_0015: ldc.i4.s 42 IL_0017: call "void E.set_Item<T>(T, int, int)" IL_001c: nop IL_001d: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_Set_RefAnalysis_01() { // sibling of ImplicitIndexIndexerAccess_RefAnalysis_01 // struct rvalue indexer setter is rejected with CS0131 var src = """ public ref struct MyStruct { public ref int i; } public static class E { extension(MyStruct s1) { public int Length => 1; public MyStruct this[int i] { set { } } } } class Program { static void M() { int i = 0; new MyStruct() { i = ref i }[^1] = default; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (20,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // new MyStruct() { i = ref i }[^1] = default; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new MyStruct() { i = ref i }[^1]").WithLocation(20, 9)); src = """ public ref struct MyStruct { public ref int i; public int Length => 1; public MyStruct this[int i] { set { } } } class Program { static void M() { int i = 0; new MyStruct() { i = ref i }[^1] = default; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (13,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // new MyStruct() { i = ref i }[^1] = default; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "new MyStruct() { i = ref i }[^1]").WithLocation(13, 9)); } [Fact] public void ImplicitRangeIndexerAccess_Set_RefAnalysis_01() { // sibling of ImplicitRangeIndexerAccess_RefAnalysis_01 // ref-returning Slice used immediately for assignment compiles var src = """ public ref struct MyStruct { public ref int i; } public static class E { extension(MyStruct s1) { public int Length => 1; public ref int Slice(int start, int length) => ref s1.i; } } class Program { static void M() { int i = 0; new MyStruct() { i = ref i }[0..] = 42; } } """; CreateCompilation(src, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics(); var src2 = """ public ref struct MyStruct { public ref int i; } public static class E { extension(MyStruct s1) { public int Length => 1; public ref int Slice(int start, int length) => ref s1.i; } } class Program { static ref int M() { int i = 0; return ref new MyStruct() { i = ref i }[0..]; } } """; CreateCompilation(src2, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (20,20): error CS8347: Cannot use a result of 'E.extension(MyStruct).Slice(int, int)' in this context because it may expose variables referenced by parameter 's1' outside of their declaration scope // return ref new MyStruct() { i = ref i }[0..]; Diagnostic(ErrorCode.ERR_EscapeCall, "new MyStruct() { i = ref i }[0..]").WithArguments("E.extension(MyStruct).Slice(int, int)", "s1").WithLocation(20, 20), // (20,35): error CS8352: Cannot use variable 'i = ref i' in this context because it may expose referenced variables outside of their declaration scope // return ref new MyStruct() { i = ref i }[0..]; Diagnostic(ErrorCode.ERR_EscapeVariable, "{ i = ref i }").WithArguments("i = ref i").WithLocation(20, 35)); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_01() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_01 // struct extension parameter var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[Program.GetIndex()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 66 (0x42) .maxstack 3 .locals init (S1& V_0, int V_1, System.Index V_2, int V_3) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: ldobj "S1" IL_0015: call "int E.get_Length(S1)" IL_001a: call "int System.Index.GetOffset(int)" IL_001f: stloc.1 IL_0020: ldloc.0 IL_0021: ldobj "S1" IL_0026: ldloc.1 IL_0027: call "int E.get_Item(S1, int)" IL_002c: call "int Program.GetValue()" IL_0031: add IL_0032: stloc.3 IL_0033: ldloc.0 IL_0034: ldobj "S1" IL_0039: ldloc.1 IL_003a: ldloc.3 IL_003b: call "void E.set_Item(S1, int, int)" IL_0040: nop IL_0041: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 62 (0x3e) .maxstack 3 .locals init (S1& V_0, int V_1, System.Index V_2, int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: ldobj "S1" IL_0011: call "int E.get_Length(S1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: stloc.1 IL_001c: ldloc.0 IL_001d: ldobj "S1" IL_0022: ldloc.1 IL_0023: call "int E.get_Item(S1, int)" IL_0028: call "int Program.GetValue()" IL_002d: add IL_002e: stloc.3 IL_002f: ldloc.0 IL_0030: ldobj "S1" IL_0035: ldloc.1 IL_0036: ldloc.3 IL_0037: call "void E.set_Item(S1, int, int)" IL_003c: nop IL_003d: ret } """); // receiver is not a variable var src2 = $$$""" static class E { extension(S1 x) { public int this[int i] { get => 0; set {} } public int Length => 3; } } struct S1; class Program { static void Test() { default(S1)[^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyEmitDiagnostics( // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(16, 9)); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_01_02() { // struct extension parameter, instance this[int] + extension Length var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{F1} "); } } public void Test2() { this[Program.GetIndex()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 54 (0x36) .maxstack 4 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: ldobj "S1" IL_0015: call "int E.get_Length(S1)" IL_001a: call "int System.Index.GetOffset(int)" IL_001f: stloc.1 IL_0020: ldloc.0 IL_0021: ldloc.1 IL_0022: ldloc.0 IL_0023: ldloc.1 IL_0024: call "int S1.this[int].get" IL_0029: call "int Program.GetValue()" IL_002e: add IL_002f: call "void S1.this[int].set" IL_0034: nop IL_0035: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 50 (0x32) .maxstack 4 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: ldobj "S1" IL_0011: call "int E.get_Length(S1)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: stloc.1 IL_001c: ldloc.0 IL_001d: ldloc.1 IL_001e: ldloc.0 IL_001f: ldloc.1 IL_0020: call "int S1.this[int].get" IL_0025: call "int Program.GetValue()" IL_002a: add IL_002b: call "void S1.this[int].set" IL_0030: nop IL_0031: ret } """); // receiver is not a variable var src2 = $$$""" static class E { extension(S1 x) { public int Length => 3; } } struct S1 { public int this[int i] { get => 0; set {} } } class Program { static void Test() { default(S1)[^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyEmitDiagnostics( // (18,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(18, 9)); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_01_03() { // struct extension parameter, extension this[int] + instance Length var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } } } struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 3; } } public void Test2() { this[Program.GetIndex()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 61 (0x3d) .maxstack 3 .locals init (S1& V_0, int V_1, System.Index V_2, int V_3) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int S1.Length.get" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldobj "S1" IL_0021: ldloc.1 IL_0022: call "int E.get_Item(S1, int)" IL_0027: call "int Program.GetValue()" IL_002c: add IL_002d: stloc.3 IL_002e: ldloc.0 IL_002f: ldobj "S1" IL_0034: ldloc.1 IL_0035: ldloc.3 IL_0036: call "void E.set_Item(S1, int, int)" IL_003b: nop IL_003c: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 57 (0x39) .maxstack 3 .locals init (S1& V_0, int V_1, System.Index V_2, int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: call "int S1.Length.get" IL_0011: call "int System.Index.GetOffset(int)" IL_0016: stloc.1 IL_0017: ldloc.0 IL_0018: ldobj "S1" IL_001d: ldloc.1 IL_001e: call "int E.get_Item(S1, int)" IL_0023: call "int Program.GetValue()" IL_0028: add IL_0029: stloc.3 IL_002a: ldloc.0 IL_002b: ldobj "S1" IL_0030: ldloc.1 IL_0031: ldloc.3 IL_0032: call "void E.set_Item(S1, int, int)" IL_0037: nop IL_0038: ret } """); // receiver is not a variable var src2 = """ static class E { extension(S1 x) { public int this[int i] { get => 0; set {} } } } struct S1 { public int Length => 3; } class Program { static void Test() { default(S1)[^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyEmitDiagnostics( // (18,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(18, 9)); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_CompoundAssignment_02(string refKind) { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_02 // struct extension parameter passed by ref var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[Program.GetIndex()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 49 (0x31) .maxstack 4 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length({{refKind}} S1)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: ldloc.0 IL_001e: ldloc.1 IL_001f: call "int E.get_Item({{refKind}} S1, int)" IL_0024: call "int Program.GetValue()" IL_0029: add IL_002a: call "void E.set_Item({{refKind}} S1, int, int)" IL_002f: nop IL_0030: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 45 (0x2d) .maxstack 4 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: call "int E.get_Length({{refKind}} S1)" IL_0011: call "int System.Index.GetOffset(int)" IL_0016: stloc.1 IL_0017: ldloc.0 IL_0018: ldloc.1 IL_0019: ldloc.0 IL_001a: ldloc.1 IL_001b: call "int E.get_Item({{refKind}} S1, int)" IL_0020: call "int Program.GetValue()" IL_0025: add IL_0026: call "void E.set_Item({{refKind}} S1, int, int)" IL_002b: nop IL_002c: ret } """); // receiver is not a variable var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get => 0; set {} } public int Length => 3; } } struct S1; class Program { static void Test() { default(S1)[^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": // One diagnostic for extension(ref default).Length and one for extension(ref default).this[int] comp2.VerifyDiagnostics( // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9) ); break; case "ref readonly": // One warning for extension(default).Length and one for extension(default).this[int] // Error for compound assignment target not being a variable // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyDiagnostics( // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(16, 9) ); break; case "in": // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyDiagnostics( // (16,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(16, 9) ); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_CompoundAssignment_02_02(string refKind) { // struct extension parameter passed by ref, instance this[int] + extension Length var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{F1} "); } } public void Test2() { this[Program.GetIndex()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 49 (0x31) .maxstack 4 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length({{refKind}} S1)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: ldloc.0 IL_001e: ldloc.1 IL_001f: call "int S1.this[int].get" IL_0024: call "int Program.GetValue()" IL_0029: add IL_002a: call "void S1.this[int].set" IL_002f: nop IL_0030: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 45 (0x2d) .maxstack 4 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: call "int E.get_Length({{refKind}} S1)" IL_0011: call "int System.Index.GetOffset(int)" IL_0016: stloc.1 IL_0017: ldloc.0 IL_0018: ldloc.1 IL_0019: ldloc.0 IL_001a: ldloc.1 IL_001b: call "int S1.this[int].get" IL_0020: call "int Program.GetValue()" IL_0025: add IL_0026: call "void S1.this[int].set" IL_002b: nop IL_002c: ret } """); // receiver is not a variable var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length => 3; } } struct S1 { public int this[int i] { get => 0; set {} } } class Program { static void Test() { default(S1)[^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp2.VerifyDiagnostics( // (18,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(18, 9)); break; case "ref readonly": // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyDiagnostics( // (18,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(18, 9), // (18,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(18, 9)); break; case "in": // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyDiagnostics( // (18,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(18, 9)); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_CompoundAssignment_02_03(string refKind) { // struct extension parameter passed by ref, extension this[int] + instance Length var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } } } struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 3; } } public void Test2() { this[Program.GetIndex()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 49 (0x31) .maxstack 4 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int S1.Length.get" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: ldloc.0 IL_001e: ldloc.1 IL_001f: call "int E.get_Item({{refKind}} S1, int)" IL_0024: call "int Program.GetValue()" IL_0029: add IL_002a: call "void E.set_Item({{refKind}} S1, int, int)" IL_002f: nop IL_0030: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 45 (0x2d) .maxstack 4 .locals init (S1& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: call "int S1.Length.get" IL_0011: call "int System.Index.GetOffset(int)" IL_0016: stloc.1 IL_0017: ldloc.0 IL_0018: ldloc.1 IL_0019: ldloc.0 IL_001a: ldloc.1 IL_001b: call "int E.get_Item({{refKind}} S1, int)" IL_0020: call "int Program.GetValue()" IL_0025: add IL_0026: call "void E.set_Item({{refKind}} S1, int, int)" IL_002b: nop IL_002c: ret } """); // receiver is not a variable var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get => 0; set {} } } } struct S1 { public int Length => 3; } class Program { static void Test() { default(S1)[^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp2.VerifyDiagnostics( // (18,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(18, 9)); break; case "ref readonly": // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyDiagnostics( // (18,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(18, 9), // (18,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(18, 9)); break; case "in": // Tracked by https://github.com/dotnet/roslyn/issues/79451 : consider adjusting receiver requirements for extension members comp2.VerifyDiagnostics( // (18,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(S1)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(S1)[^1]").WithLocation(18, 9)); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_03() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_03 // class extension parameter var src = """ static class E { extension(C1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 3; } } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { F[GetIndex()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 49 (0x31) .maxstack 4 .locals init (C1 V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length(C1)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: ldloc.0 IL_001e: ldloc.1 IL_001f: call "int E.get_Item(C1, int)" IL_0024: call "int Program.GetValue()" IL_0029: add IL_002a: call "void E.set_Item(C1, int, int)" IL_002f: nop IL_0030: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_03_02() { // class extension parameter, instance this[int] + extension Length var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 3; } } } } class C1 { public int F1; public int this[int i] { get { System.Console.Write($"get:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } set { System.Console.Write($"set:{F1} "); } } } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { F[GetIndex()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 49 (0x31) .maxstack 4 .locals init (C1 V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length(C1)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: ldloc.0 IL_001e: ldloc.1 IL_001f: callvirt "int C1.this[int].get" IL_0024: call "int Program.GetValue()" IL_0029: add IL_002a: callvirt "void C1.this[int].set" IL_002f: nop IL_0030: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_03_03() { // class extension parameter, extension this[int] + instance Length var src = """ static class E { extension(C1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } set { System.Console.Write($"set:{x.F1} "); } } } } class C1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 3; } } } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { F[GetIndex()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 49 (0x31) .maxstack 4 .locals init (C1 V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: callvirt "int C1.Length.get" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: ldloc.0 IL_001e: ldloc.1 IL_001f: call "int E.get_Item(C1, int)" IL_0024: call "int Program.GetValue()" IL_0029: add IL_002a: call "void E.set_Item(C1, int, int)" IL_002f: nop IL_0030: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_04() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_04 // unconstrained extension parameter var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static async System.Threading.Tasks.Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) { f[GetIndex()] += GetValue(); } static void Test2<T>(ref T f) where T : struct { f[GetIndex()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } static async System.Threading.Tasks.Task Test3<T>() { Program<T>.F[GetIndex()] += await GetValueAsync(); } static async System.Threading.Tasks.Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await System.Threading.Tasks.Task.Yield(); return 1; } } """; var expectedOutput = "GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValueAsync:6 set:7 final:7"; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expectedOutput), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 95 (0x5f) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, T V_4, System.Index V_5, int V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 IL_0003: ldloca.s V_4 IL_0005: initobj "T" IL_000b: ldloc.s V_4 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.2 IL_0015: ldobj "T" IL_001a: stloc.1 IL_001b: ldloca.s V_1 IL_001d: br.s IL_0020 IL_001f: ldloc.2 IL_0020: stloc.0 IL_0021: call "System.Index Program.GetIndex()" IL_0026: stloc.s V_5 IL_0028: ldloca.s V_5 IL_002a: ldloc.0 IL_002b: ldobj "T" IL_0030: call "int E.get_Length<T>(T)" IL_0035: call "int System.Index.GetOffset(int)" IL_003a: stloc.3 IL_003b: ldloc.0 IL_003c: ldobj "T" IL_0041: ldloc.3 IL_0042: call "int E.get_Item<T>(T, int)" IL_0047: call "int Program.GetValue()" IL_004c: add IL_004d: stloc.s V_6 IL_004f: ldloc.0 IL_0050: ldobj "T" IL_0055: ldloc.3 IL_0056: ldloc.s V_6 IL_0058: call "void E.set_Item<T>(T, int, int)" IL_005d: nop IL_005e: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 62 (0x3e) .maxstack 3 .locals init (T& V_0, int V_1, System.Index V_2, int V_3) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: ldobj "T" IL_0011: call "int E.get_Length<T>(T)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: stloc.1 IL_001c: ldloc.0 IL_001d: ldobj "T" IL_0022: ldloc.1 IL_0023: call "int E.get_Item<T>(T, int)" IL_0028: call "int Program.GetValue()" IL_002d: add IL_002e: stloc.3 IL_002f: ldloc.0 IL_0030: ldobj "T" IL_0035: ldloc.1 IL_0036: ldloc.3 IL_0037: call "void E.set_Item<T>(T, int, int)" IL_003c: nop IL_003d: ret } """); comp = CreateRuntimeAsyncCompilation(src, options: TestOptions.DebugExe); verifier = CompileAndVerify(comp, expectedOutput: RuntimeAsyncTestHelpers.ExpectedOutput(expectedOutput), verify: Verification.Fails with { ILVerifyMessage = """ [Main]: Return value missing on the stack. { Offset = 0x119 } [Test3]: Return value missing on the stack. { Offset = 0xa1 } [GetValueAsync]: Unexpected type on the stack. { Offset = 0x7e, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1<int32>' } """ }); verifier.VerifyIL("Program.Test3<T>()", """ { // Code size 162 (0xa2) .maxstack 3 .locals init (T V_0, T& V_1, int V_2, int V_3, int V_4, int V_5, T V_6, System.Index V_7) IL_0000: nop IL_0001: ldloca.s V_6 IL_0003: initobj "T" IL_0009: ldloc.s V_6 IL_000b: box "T" IL_0010: brtrue.s IL_001a IL_0012: ldsfld "T Program<T>.F" IL_0017: stloc.0 IL_0018: br.s IL_0020 IL_001a: ldsfld "T Program<T>.F" IL_001f: pop IL_0020: call "System.Index Program.GetIndex()" IL_0025: stloc.s V_7 IL_0027: ldloca.s V_7 IL_0029: ldloca.s V_6 IL_002b: initobj "T" IL_0031: ldloc.s V_6 IL_0033: box "T" IL_0038: brtrue.s IL_003d IL_003a: ldloc.0 IL_003b: br.s IL_0042 IL_003d: ldsfld "T Program<T>.F" IL_0042: call "int E.get_Length<T>(T)" IL_0047: call "int System.Index.GetOffset(int)" IL_004c: stloc.2 IL_004d: ldloca.s V_6 IL_004f: initobj "T" IL_0055: ldloc.s V_6 IL_0057: box "T" IL_005c: brtrue.s IL_0061 IL_005e: ldloc.0 IL_005f: br.s IL_0066 IL_0061: ldsfld "T Program<T>.F" IL_0066: ldloc.2 IL_0067: call "int E.get_Item<T>(T, int)" IL_006c: stloc.3 IL_006d: call "System.Threading.Tasks.Task<int> Program.GetValueAsync()" IL_0072: call "int System.Runtime.CompilerServices.AsyncHelpers.Await<int>(System.Threading.Tasks.Task<int>)" IL_0077: stloc.s V_4 IL_0079: ldloc.3 IL_007a: ldloc.s V_4 IL_007c: add IL_007d: stloc.s V_5 IL_007f: ldloca.s V_6 IL_0081: initobj "T" IL_0087: ldloc.s V_6 IL_0089: box "T" IL_008e: brtrue.s IL_0093 IL_0090: ldloc.0 IL_0091: br.s IL_0098 IL_0093: ldsfld "T Program<T>.F" IL_0098: ldloc.2 IL_0099: ldloc.s V_5 IL_009b: call "void E.set_Item<T>(T, int, int)" IL_00a0: nop IL_00a1: ret } """); // receiver not a variable var src2 = """ static class E { extension<T>(T x) where T : struct { public int Length => 3; public int this[int i] { get => 0; set {} } } } class Program { static void Test<T>() where T : struct { default(T)[^1] += 1; } } namespace NS1 { static class E { extension<T>(in T x) where T : struct { } } } namespace NS2 { static class E { extension<T>(ref readonly T x) where T : struct { } } } """; CreateCompilation(src2, targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (14,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // default(T)[^1] += 1; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "default(T)[^1]").WithLocation(14, 9), // (22,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(22, 25), // (32,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(ref readonly T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(32, 35)); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_05() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_05 // struct-constrained ref extension parameter var src = """ static class E { extension<T>(ref T x) where T : struct { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static async System.Threading.Tasks.Task Main() { Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); System.Console.Write(", "); Program<S1>.F = new S1 { F1 = 3 }; await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct { f[GetIndex()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } static async System.Threading.Tasks.Task Test3<T>() where T : struct { Program<T>.F[GetIndex()] += await GetValueAsync(); } static async System.Threading.Tasks.Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await System.Threading.Tasks.Task.Yield(); return 1; } } """; var expectedOutput = "GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValueAsync:6 set:7 final:7"; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expectedOutput), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 45 (0x2d) .maxstack 4 .locals init (T& V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "System.Index Program.GetIndex()" IL_0008: stloc.2 IL_0009: ldloca.s V_2 IL_000b: ldloc.0 IL_000c: call "int E.get_Length<T>(ref T)" IL_0011: call "int System.Index.GetOffset(int)" IL_0016: stloc.1 IL_0017: ldloc.0 IL_0018: ldloc.1 IL_0019: ldloc.0 IL_001a: ldloc.1 IL_001b: call "int E.get_Item<T>(ref T, int)" IL_0020: call "int Program.GetValue()" IL_0025: add IL_0026: call "void E.set_Item<T>(ref T, int, int)" IL_002b: nop IL_002c: ret } """); comp = CreateRuntimeAsyncCompilation(src, options: TestOptions.DebugExe); verifier = CompileAndVerify(comp, expectedOutput: RuntimeAsyncTestHelpers.ExpectedOutput(expectedOutput), verify: Verification.Fails with { ILVerifyMessage = """ [Main]: Return value missing on the stack. { Offset = 0xb8 } [Test3]: Return value missing on the stack. { Offset = 0x44 } [GetValueAsync]: Unexpected type on the stack. { Offset = 0x7e, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1<int32>' } """ }); verifier.VerifyIL("Program.Test3<T>()", """ { // Code size 69 (0x45) .maxstack 4 .locals init (T& V_0, int V_1, int V_2, int V_3, int V_4, System.Index V_5) IL_0000: nop IL_0001: call "System.Index Program.GetIndex()" IL_0006: stloc.s V_5 IL_0008: ldloca.s V_5 IL_000a: ldsflda "T Program<T>.F" IL_000f: call "int E.get_Length<T>(ref T)" IL_0014: call "int System.Index.GetOffset(int)" IL_0019: stloc.1 IL_001a: ldloc.1 IL_001b: stloc.2 IL_001c: ldsflda "T Program<T>.F" IL_0021: ldloc.1 IL_0022: call "int E.get_Item<T>(ref T, int)" IL_0027: stloc.3 IL_0028: call "System.Threading.Tasks.Task<int> Program.GetValueAsync()" IL_002d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await<int>(System.Threading.Tasks.Task<int>)" IL_0032: stloc.s V_4 IL_0034: ldsflda "T Program<T>.F" IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: ldloc.s V_4 IL_003d: add IL_003e: call "void E.set_Item<T>(ref T, int, int)" IL_0043: nop IL_0044: ret } """); // receiver not a variable var src2 = """ static class E { extension<T>(ref T x) where T : struct { public int Length => 3; public int this[int i] { get => 0; set {} } } } class Program { static void Test<T>() where T : struct { default(T)[^1] += 1; } } namespace NS1 { static class E { extension<T>(in T x) where T : struct { } } } namespace NS2 { static class E { extension<T>(ref readonly T x) where T : struct { } } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); comp2.VerifyDiagnostics( // (14,9): error CS1510: A ref or out value must be an assignable variable // default(T)[^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), // (14,9): error CS1510: A ref or out value must be an assignable variable // default(T)[^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), // (22,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(22, 25), // (32,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(ref readonly T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(32, 35) ); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_06() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_06 // unconstrained extension parameter, ref (unconstrained or class-constrained) receiver var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 3; } } public int this[int i] { get { System.Console.Write($"get:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 0; } set { System.Console.Write($"set:{((C1)(object)x).F1} "); } } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { static async System.Threading.Tasks.Task Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(":"); Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); System.Console.Write(":"); Program<C1>.F = new C1 { F1 = 3 }; await Test3<C1>(); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) { f[GetIndex()] += GetValue(); } static void Test2<T>(ref T f) where T : class { f[GetIndex()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return ^1; } static async System.Threading.Tasks.Task Test3<T>() { Program<T>.F[GetIndex()] += await GetValueAsync(); } static async System.Threading.Tasks.Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; await System.Threading.Tasks.Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var expectedOutput = "GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7:GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7:GetIndex:3 length:3 get:3 GetValueAsync:6 set:3 final:7"; var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput(expectedOutput), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 95 (0x5f) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, T V_4, System.Index V_5, int V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 IL_0003: ldloca.s V_4 IL_0005: initobj "T" IL_000b: ldloc.s V_4 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.2 IL_0015: ldobj "T" IL_001a: stloc.1 IL_001b: ldloca.s V_1 IL_001d: br.s IL_0020 IL_001f: ldloc.2 IL_0020: stloc.0 IL_0021: call "System.Index Program.GetIndex()" IL_0026: stloc.s V_5 IL_0028: ldloca.s V_5 IL_002a: ldloc.0 IL_002b: ldobj "T" IL_0030: call "int E.get_Length<T>(T)" IL_0035: call "int System.Index.GetOffset(int)" IL_003a: stloc.3 IL_003b: ldloc.0 IL_003c: ldobj "T" IL_0041: ldloc.3 IL_0042: call "int E.get_Item<T>(T, int)" IL_0047: call "int Program.GetValue()" IL_004c: add IL_004d: stloc.s V_6 IL_004f: ldloc.0 IL_0050: ldobj "T" IL_0055: ldloc.3 IL_0056: ldloc.s V_6 IL_0058: call "void E.set_Item<T>(T, int, int)" IL_005d: nop IL_005e: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 50 (0x32) .maxstack 4 .locals init (T V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.0 IL_0008: call "System.Index Program.GetIndex()" IL_000d: stloc.2 IL_000e: ldloca.s V_2 IL_0010: ldloc.0 IL_0011: call "int E.get_Length<T>(T)" IL_0016: call "int System.Index.GetOffset(int)" IL_001b: stloc.1 IL_001c: ldloc.0 IL_001d: ldloc.1 IL_001e: ldloc.0 IL_001f: ldloc.1 IL_0020: call "int E.get_Item<T>(T, int)" IL_0025: call "int Program.GetValue()" IL_002a: add IL_002b: call "void E.set_Item<T>(T, int, int)" IL_0030: nop IL_0031: ret } """); comp = CreateRuntimeAsyncCompilation(src, options: TestOptions.DebugExe); verifier = CompileAndVerify(comp, expectedOutput: RuntimeAsyncTestHelpers.ExpectedOutput(expectedOutput), verify: Verification.Fails with { ILVerifyMessage = """ [Main]: Return value missing on the stack. { Offset = 0x10a } [Test3]: Return value missing on the stack. { Offset = 0xa1 } [GetValueAsync]: Unexpected type on the stack. { Offset = 0x8b, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1<int32>' } """ }); verifier.VerifyIL("Program.Test3<T>()", """ { // Code size 162 (0xa2) .maxstack 3 .locals init (T V_0, T& V_1, int V_2, int V_3, int V_4, int V_5, T V_6, System.Index V_7) IL_0000: nop IL_0001: ldloca.s V_6 IL_0003: initobj "T" IL_0009: ldloc.s V_6 IL_000b: box "T" IL_0010: brtrue.s IL_001a IL_0012: ldsfld "T Program<T>.F" IL_0017: stloc.0 IL_0018: br.s IL_0020 IL_001a: ldsfld "T Program<T>.F" IL_001f: pop IL_0020: call "System.Index Program.GetIndex()" IL_0025: stloc.s V_7 IL_0027: ldloca.s V_7 IL_0029: ldloca.s V_6 IL_002b: initobj "T" IL_0031: ldloc.s V_6 IL_0033: box "T" IL_0038: brtrue.s IL_003d IL_003a: ldloc.0 IL_003b: br.s IL_0042 IL_003d: ldsfld "T Program<T>.F" IL_0042: call "int E.get_Length<T>(T)" IL_0047: call "int System.Index.GetOffset(int)" IL_004c: stloc.2 IL_004d: ldloca.s V_6 IL_004f: initobj "T" IL_0055: ldloc.s V_6 IL_0057: box "T" IL_005c: brtrue.s IL_0061 IL_005e: ldloc.0 IL_005f: br.s IL_0066 IL_0061: ldsfld "T Program<T>.F" IL_0066: ldloc.2 IL_0067: call "int E.get_Item<T>(T, int)" IL_006c: stloc.3 IL_006d: call "System.Threading.Tasks.Task<int> Program.GetValueAsync()" IL_0072: call "int System.Runtime.CompilerServices.AsyncHelpers.Await<int>(System.Threading.Tasks.Task<int>)" IL_0077: stloc.s V_4 IL_0079: ldloc.3 IL_007a: ldloc.s V_4 IL_007c: add IL_007d: stloc.s V_5 IL_007f: ldloca.s V_6 IL_0081: initobj "T" IL_0087: ldloc.s V_6 IL_0089: box "T" IL_008e: brtrue.s IL_0093 IL_0090: ldloc.0 IL_0091: br.s IL_0098 IL_0093: ldsfld "T Program<T>.F" IL_0098: ldloc.2 IL_0099: ldloc.s V_5 IL_009b: call "void E.set_Item<T>(T, int, int)" IL_00a0: nop IL_00a1: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_ReadonlyReceiver_040() { // unconstrained extension parameter, readonly field receiver var src = """ static class E { extension<T>(T x) { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); Program.Increment(); return 0; } set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.Increment(); return 3; } } } } struct S1 { public int F1; } class Program<T> { public static readonly T F; } class Program { static async System.Threading.Tasks.Task Main() { Initialize(); Test1<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}, "); Initialize(); await Test3<S1>(); System.Console.Write($"final:{Program<S1>.F.F1}"); } static unsafe void Initialize() { fixed (int* f1 = &Program<S1>.F.F1) { *f1 = 3; } } public static unsafe void Increment() { fixed (int* f1 = &Program<S1>.F.F1) { (*f1)++; } } static void Test1<T>() { Program<T>.F[GetIndex()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Increment(); return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Increment(); return ^1; } static async System.Threading.Tasks.Task Test3<T>() { Program<T>.F[GetIndex()] += await GetValueAsync(); } static async System.Threading.Tasks.Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<S1>.F.F1} "); Increment(); await System.Threading.Tasks.Task.Yield(); return 1; } } """; var expectedOutput = ExpectedOutput("GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7, GetIndex:3 length:4 get:5 GetValueAsync:6 set:7 final:7"); var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithAllowUnsafe(true), targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>()", """ { // Code size 103 (0x67) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, T V_4, T V_5, System.Index V_6, int V_7) IL_0000: nop IL_0001: ldsfld "T Program<T>.F" IL_0006: stloc.s V_4 IL_0008: ldloca.s V_4 IL_000a: stloc.2 IL_000b: ldloca.s V_5 IL_000d: initobj "T" IL_0013: ldloc.s V_5 IL_0015: box "T" IL_001a: brtrue.s IL_0027 IL_001c: ldloc.2 IL_001d: ldobj "T" IL_0022: stloc.1 IL_0023: ldloca.s V_1 IL_0025: br.s IL_0028 IL_0027: ldloc.2 IL_0028: stloc.0 IL_0029: call "System.Index Program.GetIndex()" IL_002e: stloc.s V_6 IL_0030: ldloca.s V_6 IL_0032: ldloc.0 IL_0033: ldobj "T" IL_0038: call "int E.get_Length<T>(T)" IL_003d: call "int System.Index.GetOffset(int)" IL_0042: stloc.3 IL_0043: ldloc.0 IL_0044: ldobj "T" IL_0049: ldloc.3 IL_004a: call "int E.get_Item<T>(T, int)" IL_004f: call "int Program.GetValue()" IL_0054: add IL_0055: stloc.s V_7 IL_0057: ldloc.0 IL_0058: ldobj "T" IL_005d: ldloc.3 IL_005e: ldloc.s V_7 IL_0060: call "void E.set_Item<T>(T, int, int)" IL_0065: nop IL_0066: ret } """); comp = CreateRuntimeAsyncCompilation(src, options: TestOptions.UnsafeReleaseExe); // The goal of this test is to validate that there's a `ldsfld "T Program<T>.F"` after the async call. Regular state machine code is so large that it's // very hard to verify this by reading the IL, so it does what is undefined behavior (modifying a static readonly field) to observe this. In runtime async // mode, this undefined behavior results in a different output, but the IL is also smaller so we can easily verify that the read occurs where it should in the IL. verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with { ILVerifyMessage = """ [Main]: Cannot change initonly field outside its .ctor. { Offset = 0x21 } [Main]: Cannot change initonly field outside its .ctor. { Offset = 0x6e } [Main]: Return value missing on the stack. { Offset = 0x89 } [Initialize]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } [Initialize]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } [Increment]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } [Increment]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } [GetValue]: Cannot change initonly field outside its .ctor. { Offset = 0x18 } [GetIndex]: Cannot change initonly field outside its .ctor. { Offset = 0x18 } [Test3]: Return value missing on the stack. { Offset = 0x99 } [GetValueAsync]: Cannot change initonly field outside its .ctor. { Offset = 0x18 } """ }); verifier.VerifyIL("Program.Test3<T>()", """ { // Code size 154 (0x9a) .maxstack 3 .locals init (T V_0, int V_1, int V_2, int V_3, T V_4, System.Index V_5) IL_0000: ldloca.s V_4 IL_0002: initobj "T" IL_0008: ldloc.s V_4 IL_000a: box "T" IL_000f: brtrue.s IL_0019 IL_0011: ldsfld "T Program<T>.F" IL_0016: stloc.0 IL_0017: br.s IL_001f IL_0019: ldsfld "T Program<T>.F" IL_001e: pop IL_001f: call "System.Index Program.GetIndex()" IL_0024: stloc.s V_5 IL_0026: ldloca.s V_5 IL_0028: ldloca.s V_4 IL_002a: initobj "T" IL_0030: ldloc.s V_4 IL_0032: box "T" IL_0037: brtrue.s IL_003c IL_0039: ldloc.0 IL_003a: br.s IL_0041 IL_003c: ldsfld "T Program<T>.F" IL_0041: call "int E.get_Length<T>(T)" IL_0046: call "int System.Index.GetOffset(int)" IL_004b: stloc.1 IL_004c: ldloca.s V_4 IL_004e: initobj "T" IL_0054: ldloc.s V_4 IL_0056: box "T" IL_005b: brtrue.s IL_0060 IL_005d: ldloc.0 IL_005e: br.s IL_0065 IL_0060: ldsfld "T Program<T>.F" IL_0065: ldloc.1 IL_0066: call "int E.get_Item<T>(T, int)" IL_006b: call "System.Threading.Tasks.Task<int> Program.GetValueAsync()" IL_0070: call "int System.Runtime.CompilerServices.AsyncHelpers.Await<int>(System.Threading.Tasks.Task<int>)" IL_0075: stloc.2 IL_0076: ldloc.2 IL_0077: add IL_0078: stloc.3 IL_0079: ldloca.s V_4 IL_007b: initobj "T" IL_0081: ldloc.s V_4 IL_0083: box "T" IL_0088: brtrue.s IL_008d IL_008a: ldloc.0 IL_008b: br.s IL_0092 IL_008d: ldsfld "T Program<T>.F" IL_0092: ldloc.1 IL_0093: ldloc.3 IL_0094: call "void E.set_Item<T>(T, int, int)" IL_0099: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_CompoundAssignment_ReadonlyReceiver_041(string refKind) { // sibling to IndexerAccess_CompoundAssignment_ReadonlyReceiver_041 // unconstrained extension parameter, ref/ref readonly/in struct value var src = $$$""" static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 3; } } public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>({{{refKind}}} T f) { f[GetIndex()] += GetValue(); } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return ^1; } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); if (refKind == "ref") { var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL($"Program.Test1<T>({refKind} T)", """ { // Code size 95 (0x5f) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, T V_4, System.Index V_5, int V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 IL_0003: ldloca.s V_4 IL_0005: initobj "T" IL_000b: ldloc.s V_4 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.2 IL_0015: ldobj "T" IL_001a: stloc.1 IL_001b: ldloca.s V_1 IL_001d: br.s IL_0020 IL_001f: ldloc.2 IL_0020: stloc.0 IL_0021: call "System.Index Program.GetIndex()" IL_0026: stloc.s V_5 IL_0028: ldloca.s V_5 IL_002a: ldloc.0 IL_002b: ldobj "T" IL_0030: call "int E.get_Length<T>(T)" IL_0035: call "int System.Index.GetOffset(int)" IL_003a: stloc.3 IL_003b: ldloc.0 IL_003c: ldobj "T" IL_0041: ldloc.3 IL_0042: call "int E.get_Item<T>(T, int)" IL_0047: call "int Program.GetValue()" IL_004c: add IL_004d: stloc.s V_6 IL_004f: ldloc.0 IL_0050: ldobj "T" IL_0055: ldloc.3 IL_0056: ldloc.s V_6 IL_0058: call "void E.set_Item<T>(T, int, int)" IL_005d: nop IL_005e: ret } """); } else { var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); verifier.VerifyIL($"Program.Test1<T>({refKind} T)", """ { // Code size 104 (0x68) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, T V_4, T V_5, System.Index V_6, int V_7) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.s V_4 IL_0009: ldloca.s V_4 IL_000b: stloc.2 IL_000c: ldloca.s V_5 IL_000e: initobj "T" IL_0014: ldloc.s V_5 IL_0016: box "T" IL_001b: brtrue.s IL_0028 IL_001d: ldloc.2 IL_001e: ldobj "T" IL_0023: stloc.1 IL_0024: ldloca.s V_1 IL_0026: br.s IL_0029 IL_0028: ldloc.2 IL_0029: stloc.0 IL_002a: call "System.Index Program.GetIndex()" IL_002f: stloc.s V_6 IL_0031: ldloca.s V_6 IL_0033: ldloc.0 IL_0034: ldobj "T" IL_0039: call "int E.get_Length<T>(T)" IL_003e: call "int System.Index.GetOffset(int)" IL_0043: stloc.3 IL_0044: ldloc.0 IL_0045: ldobj "T" IL_004a: ldloc.3 IL_004b: call "int E.get_Item<T>(T, int)" IL_0050: call "int Program.GetValue()" IL_0055: add IL_0056: stloc.s V_7 IL_0058: ldloc.0 IL_0059: ldobj "T" IL_005e: ldloc.3 IL_005f: ldloc.s V_7 IL_0061: call "void E.set_Item<T>(T, int, int)" IL_0066: nop IL_0067: ret } """); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_CompoundAssignment_ReadonlyReceiver_061(string refKind) { // unconstrained extension parameter, ref/ref readonly/in class value var src = $$$""" static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 3; } } public int this[int i] { get { System.Console.Write($"get:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 0; } set { System.Console.Write($"set:{((C1)(object)x).F1} "); } } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { static void Main() { Program<C1>.F = new C1 { F1 = 3 }; Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program<C1>.F); System.Console.Write($"final:{Program<C1>.F.F1}"); } static void Test1<T>({{{refKind}}} T f) { f[GetIndex()] += GetValue(); } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return ^1; } static int GetValue() { System.Console.Write($"GetValue:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1; } } """; var verifier = CompileAndVerify( CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100), expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); if (refKind == "ref") { verifier.VerifyIL($"Program.Test1<T>({refKind} T)", """ { // Code size 95 (0x5f) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, T V_4, System.Index V_5, int V_6) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.2 IL_0003: ldloca.s V_4 IL_0005: initobj "T" IL_000b: ldloc.s V_4 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.2 IL_0015: ldobj "T" IL_001a: stloc.1 IL_001b: ldloca.s V_1 IL_001d: br.s IL_0020 IL_001f: ldloc.2 IL_0020: stloc.0 IL_0021: call "System.Index Program.GetIndex()" IL_0026: stloc.s V_5 IL_0028: ldloca.s V_5 IL_002a: ldloc.0 IL_002b: ldobj "T" IL_0030: call "int E.get_Length<T>(T)" IL_0035: call "int System.Index.GetOffset(int)" IL_003a: stloc.3 IL_003b: ldloc.0 IL_003c: ldobj "T" IL_0041: ldloc.3 IL_0042: call "int E.get_Item<T>(T, int)" IL_0047: call "int Program.GetValue()" IL_004c: add IL_004d: stloc.s V_6 IL_004f: ldloc.0 IL_0050: ldobj "T" IL_0055: ldloc.3 IL_0056: ldloc.s V_6 IL_0058: call "void E.set_Item<T>(T, int, int)" IL_005d: nop IL_005e: ret } """); } else { verifier.VerifyIL($"Program.Test1<T>({refKind} T)", """ { // Code size 104 (0x68) .maxstack 3 .locals init (T& V_0, T V_1, T& V_2, int V_3, T V_4, T V_5, System.Index V_6, int V_7) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.s V_4 IL_0009: ldloca.s V_4 IL_000b: stloc.2 IL_000c: ldloca.s V_5 IL_000e: initobj "T" IL_0014: ldloc.s V_5 IL_0016: box "T" IL_001b: brtrue.s IL_0028 IL_001d: ldloc.2 IL_001e: ldobj "T" IL_0023: stloc.1 IL_0024: ldloca.s V_1 IL_0026: br.s IL_0029 IL_0028: ldloc.2 IL_0029: stloc.0 IL_002a: call "System.Index Program.GetIndex()" IL_002f: stloc.s V_6 IL_0031: ldloca.s V_6 IL_0033: ldloc.0 IL_0034: ldobj "T" IL_0039: call "int E.get_Length<T>(T)" IL_003e: call "int System.Index.GetOffset(int)" IL_0043: stloc.3 IL_0044: ldloc.0 IL_0045: ldobj "T" IL_004a: ldloc.3 IL_004b: call "int E.get_Item<T>(T, int)" IL_0050: call "int Program.GetValue()" IL_0055: add IL_0056: stloc.s V_7 IL_0058: ldloc.0 IL_0059: ldobj "T" IL_005e: ldloc.3 IL_005f: ldloc.s V_7 IL_0061: call "void E.set_Item<T>(T, int, int)" IL_0066: nop IL_0067: ret } """); } } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_01() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_01 // struct extension parameter, extension Length + ref-returning extension Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public void Test2() { this[Program.GetRange()] += Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 84 (0x54) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: stloc.1 IL_0018: ldloca.s V_0 IL_001a: call "System.Index System.Range.Start.get" IL_001f: stloc.s V_4 IL_0021: ldloca.s V_4 IL_0023: ldloc.1 IL_0024: call "int System.Index.GetOffset(int)" IL_0029: stloc.2 IL_002a: ldloca.s V_0 IL_002c: call "System.Index System.Range.End.get" IL_0031: stloc.s V_4 IL_0033: ldloca.s V_4 IL_0035: ldloc.1 IL_0036: call "int System.Index.GetOffset(int)" IL_003b: ldloc.2 IL_003c: sub IL_003d: stloc.3 IL_003e: ldobj "S1" IL_0043: ldloc.2 IL_0044: ldloc.3 IL_0045: call "ref int E.Slice(S1, int, int)" IL_004a: dup IL_004b: ldind.i4 IL_004c: call "int Program.GetValue()" IL_0051: add IL_0052: stind.i4 IL_0053: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 80 (0x50) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: call "int E.get_Length(S1)" IL_0013: stloc.1 IL_0014: ldloca.s V_0 IL_0016: call "System.Index System.Range.Start.get" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: ldloc.1 IL_0020: call "int System.Index.GetOffset(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_0 IL_0028: call "System.Index System.Range.End.get" IL_002d: stloc.s V_4 IL_002f: ldloca.s V_4 IL_0031: ldloc.1 IL_0032: call "int System.Index.GetOffset(int)" IL_0037: ldloc.2 IL_0038: sub IL_0039: stloc.3 IL_003a: ldobj "S1" IL_003f: ldloc.2 IL_0040: ldloc.3 IL_0041: call "ref int E.Slice(S1, int, int)" IL_0046: dup IL_0047: ldind.i4 IL_0048: call "int Program.GetValue()" IL_004d: add IL_004e: stind.i4 IL_004f: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_01_02() { // struct extension parameter, instance Length + ref-returning extension Slice var src = """ static class E { extension(S1 x) { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 5; } } public void Test2() { this[Program.GetRange()] += Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 79 (0x4f) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int S1.Length.get" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldobj "S1" IL_003e: ldloc.2 IL_003f: ldloc.3 IL_0040: call "ref int E.Slice(S1, int, int)" IL_0045: dup IL_0046: ldind.i4 IL_0047: call "int Program.GetValue()" IL_004c: add IL_004d: stind.i4 IL_004e: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 75 (0x4b) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: call "int S1.Length.get" IL_000e: stloc.1 IL_000f: ldloca.s V_0 IL_0011: call "System.Index System.Range.Start.get" IL_0016: stloc.s V_4 IL_0018: ldloca.s V_4 IL_001a: ldloc.1 IL_001b: call "int System.Index.GetOffset(int)" IL_0020: stloc.2 IL_0021: ldloca.s V_0 IL_0023: call "System.Index System.Range.End.get" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: ldloc.1 IL_002d: call "int System.Index.GetOffset(int)" IL_0032: ldloc.2 IL_0033: sub IL_0034: stloc.3 IL_0035: ldobj "S1" IL_003a: ldloc.2 IL_003b: ldloc.3 IL_003c: call "ref int E.Slice(S1, int, int)" IL_0041: dup IL_0042: ldind.i4 IL_0043: call "int Program.GetValue()" IL_0048: add IL_0049: stind.i4 IL_004a: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_01_03() { // struct extension parameter, extension Length + ref-returning instance Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } } } struct S1 { public int F1; public ref int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F.F1++; return ref Program.Result; } public void Test2() { this[Program.GetRange()] += Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7"), verify: Verification.Fails).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 79 (0x4f) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: stloc.1 IL_0018: ldloca.s V_0 IL_001a: call "System.Index System.Range.Start.get" IL_001f: stloc.s V_4 IL_0021: ldloca.s V_4 IL_0023: ldloc.1 IL_0024: call "int System.Index.GetOffset(int)" IL_0029: stloc.2 IL_002a: ldloca.s V_0 IL_002c: call "System.Index System.Range.End.get" IL_0031: stloc.s V_4 IL_0033: ldloca.s V_4 IL_0035: ldloc.1 IL_0036: call "int System.Index.GetOffset(int)" IL_003b: ldloc.2 IL_003c: sub IL_003d: stloc.3 IL_003e: ldloc.2 IL_003f: ldloc.3 IL_0040: call "ref int S1.Slice(int, int)" IL_0045: dup IL_0046: ldind.i4 IL_0047: call "int Program.GetValue()" IL_004c: add IL_004d: stind.i4 IL_004e: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 75 (0x4b) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: call "int E.get_Length(S1)" IL_0013: stloc.1 IL_0014: ldloca.s V_0 IL_0016: call "System.Index System.Range.Start.get" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: ldloc.1 IL_0020: call "int System.Index.GetOffset(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_0 IL_0028: call "System.Index System.Range.End.get" IL_002d: stloc.s V_4 IL_002f: ldloca.s V_4 IL_0031: ldloc.1 IL_0032: call "int System.Index.GetOffset(int)" IL_0037: ldloc.2 IL_0038: sub IL_0039: stloc.3 IL_003a: ldloc.2 IL_003b: ldloc.3 IL_003c: call "ref int S1.Slice(int, int)" IL_0041: dup IL_0042: ldind.i4 IL_0043: call "int Program.GetValue()" IL_0048: add IL_0049: stind.i4 IL_004a: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_CompoundAssignment_02(string refKind) { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_02 // struct ref extension parameter, extension Length + ref-returning extension Slice var src = $$""" static class E { extension({{refKind}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public void Test2() { this[Program.GetRange()] += Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 74 (0x4a) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length({{refKind}} S1)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "ref int E.Slice({{refKind}} S1, int, int)" IL_0040: dup IL_0041: ldind.i4 IL_0042: call "int Program.GetValue()" IL_0047: add IL_0048: stind.i4 IL_0049: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 70 (0x46) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: call "int E.get_Length({{refKind}} S1)" IL_000e: stloc.1 IL_000f: ldloca.s V_0 IL_0011: call "System.Index System.Range.Start.get" IL_0016: stloc.s V_4 IL_0018: ldloca.s V_4 IL_001a: ldloc.1 IL_001b: call "int System.Index.GetOffset(int)" IL_0020: stloc.2 IL_0021: ldloca.s V_0 IL_0023: call "System.Index System.Range.End.get" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: ldloc.1 IL_002d: call "int System.Index.GetOffset(int)" IL_0032: ldloc.2 IL_0033: sub IL_0034: stloc.3 IL_0035: ldloc.2 IL_0036: ldloc.3 IL_0037: call "ref int E.Slice({{refKind}} S1, int, int)" IL_003c: dup IL_003d: ldind.i4 IL_003e: call "int Program.GetValue()" IL_0043: add IL_0044: stind.i4 IL_0045: ret } """); // receiver is not a variable var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } struct S1; class Program { static void Test() { default(S1)[1..^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp2.VerifyEmitDiagnostics( // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9)); break; case "ref readonly": comp2.VerifyEmitDiagnostics( // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9)); break; case "in": comp2.VerifyEmitDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_CompoundAssignment_02_02(string refKind) { // struct ref extension parameter, instance Length + ref-returning extension Slice var src = $$""" static class E { extension({{refKind}} S1 x) { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F.F1++; return 5; } } public void Test2() { this[Program.GetRange()] += Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 74 (0x4a) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int S1.Length.get" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "ref int E.Slice({{refKind}} S1, int, int)" IL_0040: dup IL_0041: ldind.i4 IL_0042: call "int Program.GetValue()" IL_0047: add IL_0048: stind.i4 IL_0049: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 70 (0x46) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: call "int {{"S1.Length.get"}}" IL_000e: stloc.1 IL_000f: ldloca.s V_0 IL_0011: call "System.Index System.Range.Start.get" IL_0016: stloc.s V_4 IL_0018: ldloca.s V_4 IL_001a: ldloc.1 IL_001b: call "int System.Index.GetOffset(int)" IL_0020: stloc.2 IL_0021: ldloca.s V_0 IL_0023: call "System.Index System.Range.End.get" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: ldloc.1 IL_002d: call "int System.Index.GetOffset(int)" IL_0032: ldloc.2 IL_0033: sub IL_0034: stloc.3 IL_0035: ldloc.2 IL_0036: ldloc.3 IL_0037: call "ref int E.Slice({{refKind}} S1, int, int)" IL_003c: dup IL_003d: ldind.i4 IL_003e: call "int Program.GetValue()" IL_0043: add IL_0044: stind.i4 IL_0045: ret } """); // receiver is not a variable var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } struct S1; class Program { static void Test() { default(S1)[1..^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp2.VerifyEmitDiagnostics( // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9)); break; case "ref readonly": comp2.VerifyEmitDiagnostics( // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9)); break; case "in": comp2.VerifyEmitDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_CompoundAssignment_02_03(string refKind) { // struct ref extension parameter, extension Length + ref-returning instance Slice var src = $$""" static class E { extension({{refKind}} S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } } } struct S1 { public int F1; public ref int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F.F1++; return ref Program.Result; } public void Test2() { this[Program.GetRange()] += Program.GetValue(); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7"), verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 74 (0x4a) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length({{refKind}} S1)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "ref int S1.Slice(int, int)" IL_0040: dup IL_0041: ldind.i4 IL_0042: call "int Program.GetValue()" IL_0047: add IL_0048: stind.i4 IL_0049: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 70 (0x46) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: call "int E.get_Length({{refKind}} S1)" IL_000e: stloc.1 IL_000f: ldloca.s V_0 IL_0011: call "System.Index System.Range.Start.get" IL_0016: stloc.s V_4 IL_0018: ldloca.s V_4 IL_001a: ldloc.1 IL_001b: call "int System.Index.GetOffset(int)" IL_0020: stloc.2 IL_0021: ldloca.s V_0 IL_0023: call "System.Index System.Range.End.get" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: ldloc.1 IL_002d: call "int System.Index.GetOffset(int)" IL_0032: ldloc.2 IL_0033: sub IL_0034: stloc.3 IL_0035: ldloc.2 IL_0036: ldloc.3 IL_0037: call "ref int S1.Slice(int, int)" IL_003c: dup IL_003d: ldind.i4 IL_003e: call "int Program.GetValue()" IL_0043: add IL_0044: stind.i4 IL_0045: ret } """); // receiver is not a variable var src2 = $$$""" static class E { extension({{{refKind}}} S1 x) { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } struct S1; class Program { static void Test() { default(S1)[1..^1] += 1; } } """; var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.Net100); switch (refKind) { case "ref": comp2.VerifyDiagnostics( // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9), // (16,9): error CS1510: A ref or out value must be an assignable variable // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(S1)").WithLocation(16, 9)); break; case "ref readonly": comp2.VerifyDiagnostics( // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9), // (16,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter // default(S1)[1..^1] += 1; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "default(S1)").WithArguments("0").WithLocation(16, 9)); break; case "in": comp2.VerifyDiagnostics(); break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_03() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_03 // class extension parameter, extension Length + ref-returning extension Slice var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ref Program.Result; } } } class C1 { public int F1; } class Program { public static C1 F; public static int Result; static void Main() { Result = 10; F = new C1 { F1 = 3 }; Test(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test() { F[GetRange()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 74 (0x4a) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length(C1)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "ref int E.Slice(C1, int, int)" IL_0040: dup IL_0041: ldind.i4 IL_0042: call "int Program.GetValue()" IL_0047: add IL_0048: stind.i4 IL_0049: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_03_02() { // class extension parameter, instance Length + ref-returning extension Slice var src = """ static class E { extension(C1 x) { public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ref Program.Result; } } } class C1 { public int F1; public int Length { get { System.Console.Write($"length:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } } class Program { public static C1 F; public static int Result; static void Main() { Result = 10; F = new C1 { F1 = 3 }; Test(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test() { F[GetRange()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 74 (0x4a) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: callvirt "int C1.Length.get" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: call "ref int E.Slice(C1, int, int)" IL_0040: dup IL_0041: ldind.i4 IL_0042: call "int Program.GetValue()" IL_0047: add IL_0048: stind.i4 IL_0049: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_03_03() { // class extension parameter, extension Length + ref-returning instance Slice var src = """ static class E { extension(C1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 5; } } } } class C1 { public int F1; public ref int Slice(int start, int length) { System.Console.Write($"Slice:{F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ref Program.Result; } } class Program { public static C1 F; public static int Result; static void Main() { Result = 10; F = new C1 { F1 = 3 }; Test(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test() { F[GetRange()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:11 final:7"), verify: Verification.Fails).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 74 (0x4a) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: call "System.Range Program.GetRange()" IL_000b: stloc.0 IL_000c: dup IL_000d: call "int E.get_Length(C1)" IL_0012: stloc.1 IL_0013: ldloca.s V_0 IL_0015: call "System.Index System.Range.Start.get" IL_001a: stloc.s V_4 IL_001c: ldloca.s V_4 IL_001e: ldloc.1 IL_001f: call "int System.Index.GetOffset(int)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "System.Index System.Range.End.get" IL_002c: stloc.s V_4 IL_002e: ldloca.s V_4 IL_0030: ldloc.1 IL_0031: call "int System.Index.GetOffset(int)" IL_0036: ldloc.2 IL_0037: sub IL_0038: stloc.3 IL_0039: ldloc.2 IL_003a: ldloc.3 IL_003b: callvirt "ref int C1.Slice(int, int)" IL_0040: dup IL_0041: ldind.i4 IL_0042: call "int Program.GetValue()" IL_0047: add IL_0048: stind.i4 IL_0049: ret } """); } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_04() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_04 // unconstrained extension parameter, unconstrained or struct-constrained reference var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { public static int Result; static void Main() { Result = 10; Program<S1>.F = new S1 { F1 = 3 }; Test1(ref Program<S1>.F); System.Console.Write($"result:{Result} final:{Program<S1>.F.F1}"); System.Console.Write(", "); Result = 10; Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"result:{Result} final:{Program<S1>.F.F1}"); } static void Test1<T>(ref T f) { f[GetRange()] += GetValue(); } static void Test2<T>(ref T f) where T : struct { f[GetRange()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } static async System.Threading.Tasks.Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await System.Threading.Tasks.Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", """ { // Code size 115 (0x73) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, System.Index V_7) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloca.s V_6 IL_0005: initobj "T" IL_000b: ldloc.s V_6 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.1 IL_0015: ldobj "T" IL_001a: stloc.0 IL_001b: ldloca.s V_0 IL_001d: br.s IL_0020 IL_001f: ldloc.1 IL_0020: call "System.Range Program.GetRange()" IL_0025: stloc.2 IL_0026: dup IL_0027: ldobj "T" IL_002c: call "int E.get_Length<T>(T)" IL_0031: stloc.3 IL_0032: ldloca.s V_2 IL_0034: call "System.Index System.Range.Start.get" IL_0039: stloc.s V_7 IL_003b: ldloca.s V_7 IL_003d: ldloc.3 IL_003e: call "int System.Index.GetOffset(int)" IL_0043: stloc.s V_4 IL_0045: ldloca.s V_2 IL_0047: call "System.Index System.Range.End.get" IL_004c: stloc.s V_7 IL_004e: ldloca.s V_7 IL_0050: ldloc.3 IL_0051: call "int System.Index.GetOffset(int)" IL_0056: ldloc.s V_4 IL_0058: sub IL_0059: stloc.s V_5 IL_005b: ldobj "T" IL_0060: ldloc.s V_4 IL_0062: ldloc.s V_5 IL_0064: call "ref int E.Slice<T>(T, int, int)" IL_0069: dup IL_006a: ldind.i4 IL_006b: call "int Program.GetValue()" IL_0070: add IL_0071: stind.i4 IL_0072: ret } """); verifier.VerifyIL("Program.Test2<T>(ref T)", """ { // Code size 80 (0x50) .maxstack 3 .locals init (System.Range V_0, int V_1, int V_2, int V_3, System.Index V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "System.Range Program.GetRange()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "T" IL_000e: call "int E.get_Length<T>(T)" IL_0013: stloc.1 IL_0014: ldloca.s V_0 IL_0016: call "System.Index System.Range.Start.get" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: ldloc.1 IL_0020: call "int System.Index.GetOffset(int)" IL_0025: stloc.2 IL_0026: ldloca.s V_0 IL_0028: call "System.Index System.Range.End.get" IL_002d: stloc.s V_4 IL_002f: ldloca.s V_4 IL_0031: ldloc.1 IL_0032: call "int System.Index.GetOffset(int)" IL_0037: ldloc.2 IL_0038: sub IL_0039: stloc.3 IL_003a: ldobj "T" IL_003f: ldloc.2 IL_0040: ldloc.3 IL_0041: call "ref int E.Slice<T>(T, int, int)" IL_0046: dup IL_0047: ldind.i4 IL_0048: call "int Program.GetValue()" IL_004d: add IL_004e: stind.i4 IL_004f: ret } """); // async src = """ static class E { extension<T>(T x) { public int Length => throw null; public ref int Slice(int start, int length) => throw null; } } struct S1 { } class Program<T> { public static T F; } class Program { static int GetValue() => throw null; static System.Range GetRange() => throw null; static async System.Threading.Tasks.Task Test3<T>() { Program<T>.F[GetRange()] += await GetValueAsync(); } static System.Threading.Tasks.Task<int> GetValueAsync() => throw null; } """; comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (25,9): error CS8178: A reference returned by a call to 'E.Slice<T>(T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // Program<T>.F[GetRange()] += await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "Program<T>.F[GetRange()]").WithArguments("E.Slice<T>(T, int, int)").WithLocation(25, 9)); // receiver is not a variable var src2 = """ static class E { extension<T>(T x) where T : struct { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } class Program { static void Test<T>() where T : struct { default(T)[1..^1] += 1; } } namespace NS1 { static class E { extension<T>(in T x) where T : struct { } } } namespace NS2 { static class E { extension<T>(ref readonly T x) where T : struct { } } } """; CreateCompilation(src2, targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (22,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(22, 25), // (32,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(ref readonly T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(32, 35)); } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_05() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_05 // struct-constrained ref extension parameter var src = """ static class E { extension<T>(ref T x) where T : struct { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { public static int Result; static async System.Threading.Tasks.Task Main() { Result = 10; Program<S1>.F = new S1 { F1 = 3 }; Test2(ref Program<S1>.F); System.Console.Write($"result:{Result} final:{Program<S1>.F.F1}"); } static void Test2<T>(ref T f) where T : struct { f[GetRange()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } static async System.Threading.Tasks.Task<int> GetValueAsync() { System.Console.Write($"GetValueAsync:{Program<S1>.F.F1} "); Program<S1>.F.F1++; await System.Threading.Tasks.Task.Yield(); return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); // awaited value src = """ static class E { extension<T>(ref T x) where T : struct { public int Length => throw null; public ref int Slice(int start, int length) => throw null; } } struct S1 { } class Program<T> { public static T F; } class Program { static System.Range GetRange() => throw null; static async System.Threading.Tasks.Task Test3<T>() where T : struct { Program<T>.F[GetRange()] += await GetValueAsync(); } static System.Threading.Tasks.Task<int> GetValueAsync() => throw null; } """; comp = CreateCompilation(src, targetFramework: TargetFramework.Net100) .VerifyEmitDiagnostics( // (23,9): error CS8178: A reference returned by a call to 'E.Slice<T>(ref T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // Program<T>.F[GetRange()] += await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "Program<T>.F[GetRange()]").WithArguments("E.Slice<T>(ref T, int, int)").WithLocation(23, 9)); // receiver not a variable var src2 = """ static class E { extension<T>(ref T x) where T : struct { public int Length => 5; public ref int Slice(int start, int length) => throw null; } } class Program { static void Test<T>() where T : struct { default(T)[1..^1] += 1; } } namespace NS1 { static class E { extension<T>(in T x) where T : struct { } } } namespace NS2 { static class E { extension<T>(ref readonly T x) where T : struct { } } } """; CreateCompilation(src2, targetFramework: TargetFramework.Net100).VerifyDiagnostics( // (14,9): error CS1510: A ref or out value must be an assignable variable // default(T)[1..^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), // (14,9): error CS1510: A ref or out value must be an assignable variable // default(T)[1..^1] += 1; Diagnostic(ErrorCode.ERR_RefLvalueExpected, "default(T)").WithLocation(14, 9), // (22,25): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(in T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(22, 25), // (32,35): error CS9301: The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type. // extension<T>(ref readonly T x) where T : struct Diagnostic(ErrorCode.ERR_InExtensionParameterMustBeValueType, "T").WithLocation(32, 35)); } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_06() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_06 // unconstrained extension parameter, unconstrained or class-constrained receiver var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return ref Program.Result; } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { public static int Result; static async System.Threading.Tasks.Task Main() { Result = 10; Program<C1>.F = new C1 { F1 = 3 }; Test1(ref Program<C1>.F); System.Console.Write($"result:{Result} final:{Program<C1>.F.F1}"); System.Console.Write(":"); Result = 10; Program<C1>.F = new C1 { F1 = 3 }; Test2(ref Program<C1>.F); System.Console.Write($"result:{Result} final:{Program<C1>.F.F1}"); } static void Test1<T>(ref T f) { f[GetRange()] += GetValue(); } static void Test2<T>(ref T f) where T : class { f[GetRange()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:11 final:7:GetRange:3 length:3 Slice:3 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify) .VerifyDiagnostics(); // awaited value src = """ static class E { extension<T>(T x) { public int Length => throw null; public ref int Slice(int start, int length) => throw null; } } class C1 { } class Program<T> { public static T F; } class Program { static System.Range GetRange() => throw null; static async System.Threading.Tasks.Task Test3<T>() { Program<T>.F[GetRange()] += await GetValueAsync(); } static System.Threading.Tasks.Task<int> GetValueAsync() => throw null; } """; comp = CreateCompilation(src, targetFramework: TargetFramework.Net100); comp.VerifyEmitDiagnostics( // (23,9): error CS8178: A reference returned by a call to 'E.Slice<T>(T, int, int)' cannot be preserved across 'await' or 'yield' boundary. // Program<T>.F[GetRange()] += await GetValueAsync(); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "Program<T>.F[GetRange()]").WithArguments("E.Slice<T>(T, int, int)").WithLocation(23, 9)); } [Fact] public void ImplicitRangeIndexerAccess_CompoundAssignment_ReadonlyReceiver_040() { // unconstrained extension parameter, readonly field receiver var src = """ static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program.Increment(); return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program.Increment(); return ref Program.Result; } } } struct S1 { public int F1; } class Program<T> { public static readonly T F; } class Program { public static int Result; static async System.Threading.Tasks.Task Main() { Result = 10; Initialize(); Test1<S1>(); System.Console.Write($"result:{Result} final:{Program<S1>.F.F1}"); } static unsafe void Initialize() { fixed (int* f1 = &Program<S1>.F.F1) { *f1 = 3; } } public static unsafe void Increment() { fixed (int* f1 = &Program<S1>.F.F1) { (*f1)++; } } static void Test1<T>() { Program<T>.F[GetRange()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Increment(); return 1; } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Increment(); return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithAllowUnsafe(true), targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:11 final:7"), verify: Verification.Fails) .VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_CompoundAssignment_ReadonlyReceiver_041(string refKind) { // unconstrained extension parameter, ref/ref readonly/in struct value var src = $$$""" static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { public static int Result; static void Main() { Result = 10; Program<S1>.F = new S1 { F1 = 3 }; Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program<S1>.F); System.Console.Write($"result:{Result} final:{Program<S1>.F.F1}"); } static void Test1<T>({{{refKind}}} T f) { f[GetRange()] += GetValue(); } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1..^1; } static int GetValue() { System.Console.Write($"GetValue:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } } """; var verifier = CompileAndVerify( CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100), expectedOutput: refKind == "ref" ? ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:11 final:7") : ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); if (refKind == "ref") { verifier.VerifyIL($"Program.Test1<T>({refKind} T)", """ { // Code size 115 (0x73) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, System.Index V_7) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloca.s V_6 IL_0005: initobj "T" IL_000b: ldloc.s V_6 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.1 IL_0015: ldobj "T" IL_001a: stloc.0 IL_001b: ldloca.s V_0 IL_001d: br.s IL_0020 IL_001f: ldloc.1 IL_0020: call "System.Range Program.GetRange()" IL_0025: stloc.2 IL_0026: dup IL_0027: ldobj "T" IL_002c: call "int E.get_Length<T>(T)" IL_0031: stloc.3 IL_0032: ldloca.s V_2 IL_0034: call "System.Index System.Range.Start.get" IL_0039: stloc.s V_7 IL_003b: ldloca.s V_7 IL_003d: ldloc.3 IL_003e: call "int System.Index.GetOffset(int)" IL_0043: stloc.s V_4 IL_0045: ldloca.s V_2 IL_0047: call "System.Index System.Range.End.get" IL_004c: stloc.s V_7 IL_004e: ldloca.s V_7 IL_0050: ldloc.3 IL_0051: call "int System.Index.GetOffset(int)" IL_0056: ldloc.s V_4 IL_0058: sub IL_0059: stloc.s V_5 IL_005b: ldobj "T" IL_0060: ldloc.s V_4 IL_0062: ldloc.s V_5 IL_0064: call "ref int E.Slice<T>(T, int, int)" IL_0069: dup IL_006a: ldind.i4 IL_006b: call "int Program.GetValue()" IL_0070: add IL_0071: stind.i4 IL_0072: ret } """); } else { verifier.VerifyIL($"Program.Test1<T>({refKind} T)", """ { // Code size 124 (0x7c) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, T V_7, System.Index V_8) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.s V_6 IL_0009: ldloca.s V_6 IL_000b: stloc.1 IL_000c: ldloca.s V_7 IL_000e: initobj "T" IL_0014: ldloc.s V_7 IL_0016: box "T" IL_001b: brtrue.s IL_0028 IL_001d: ldloc.1 IL_001e: ldobj "T" IL_0023: stloc.0 IL_0024: ldloca.s V_0 IL_0026: br.s IL_0029 IL_0028: ldloc.1 IL_0029: call "System.Range Program.GetRange()" IL_002e: stloc.2 IL_002f: dup IL_0030: ldobj "T" IL_0035: call "int E.get_Length<T>(T)" IL_003a: stloc.3 IL_003b: ldloca.s V_2 IL_003d: call "System.Index System.Range.Start.get" IL_0042: stloc.s V_8 IL_0044: ldloca.s V_8 IL_0046: ldloc.3 IL_0047: call "int System.Index.GetOffset(int)" IL_004c: stloc.s V_4 IL_004e: ldloca.s V_2 IL_0050: call "System.Index System.Range.End.get" IL_0055: stloc.s V_8 IL_0057: ldloca.s V_8 IL_0059: ldloc.3 IL_005a: call "int System.Index.GetOffset(int)" IL_005f: ldloc.s V_4 IL_0061: sub IL_0062: stloc.s V_5 IL_0064: ldobj "T" IL_0069: ldloc.s V_4 IL_006b: ldloc.s V_5 IL_006d: call "ref int E.Slice<T>(T, int, int)" IL_0072: dup IL_0073: ldind.i4 IL_0074: call "int Program.GetValue()" IL_0079: add IL_007a: stind.i4 IL_007b: ret } """); } } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitRangeIndexerAccess_CompoundAssignment_ReadonlyReceiver_061(string refKind) { // unconstrained extension parameter, ref/ref readonly/in class value var src = $$$""" static class E { extension<T>(T x) { public int Length { get { System.Console.Write($"length:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{((C1)(object)x).F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return ref Program.Result; } } } class C1 { public int F1; } class Program<T> { public static T F; } class Program { public static int Result; static void Main() { Result = 10; Program<C1>.F = new C1 { F1 = 3 }; Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program<C1>.F); System.Console.Write($"result:{Result} final:{Program<C1>.F.F1}"); } static void Test1<T>({{{refKind}}} T f) { f[GetRange()] += GetValue(); } static System.Range GetRange() { System.Console.Write($"GetRange:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1..^1; } static int GetValue() { System.Console.Write($"GetValue:{Program<C1>.F.F1} "); Program<C1>.F = new C1 { F1 = Program<C1>.F.F1 + 1 }; return 1; } } """; var verifier = CompileAndVerify( CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100), expectedOutput: ExpectedOutput("GetRange:3 length:3 Slice:3 GetValue:6 result:11 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); if (refKind == "ref") { verifier.VerifyIL($"Program.Test1<T>({refKind} T)", """ { // Code size 115 (0x73) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, System.Index V_7) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.1 IL_0003: ldloca.s V_6 IL_0005: initobj "T" IL_000b: ldloc.s V_6 IL_000d: box "T" IL_0012: brtrue.s IL_001f IL_0014: ldloc.1 IL_0015: ldobj "T" IL_001a: stloc.0 IL_001b: ldloca.s V_0 IL_001d: br.s IL_0020 IL_001f: ldloc.1 IL_0020: call "System.Range Program.GetRange()" IL_0025: stloc.2 IL_0026: dup IL_0027: ldobj "T" IL_002c: call "int E.get_Length<T>(T)" IL_0031: stloc.3 IL_0032: ldloca.s V_2 IL_0034: call "System.Index System.Range.Start.get" IL_0039: stloc.s V_7 IL_003b: ldloca.s V_7 IL_003d: ldloc.3 IL_003e: call "int System.Index.GetOffset(int)" IL_0043: stloc.s V_4 IL_0045: ldloca.s V_2 IL_0047: call "System.Index System.Range.End.get" IL_004c: stloc.s V_7 IL_004e: ldloca.s V_7 IL_0050: ldloc.3 IL_0051: call "int System.Index.GetOffset(int)" IL_0056: ldloc.s V_4 IL_0058: sub IL_0059: stloc.s V_5 IL_005b: ldobj "T" IL_0060: ldloc.s V_4 IL_0062: ldloc.s V_5 IL_0064: call "ref int E.Slice<T>(T, int, int)" IL_0069: dup IL_006a: ldind.i4 IL_006b: call "int Program.GetValue()" IL_0070: add IL_0071: stind.i4 IL_0072: ret } """); } else { verifier.VerifyIL($"Program.Test1<T>({refKind} T)", """ { // Code size 124 (0x7c) .maxstack 3 .locals init (T V_0, T& V_1, System.Range V_2, int V_3, int V_4, int V_5, T V_6, T V_7, System.Index V_8) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldobj "T" IL_0007: stloc.s V_6 IL_0009: ldloca.s V_6 IL_000b: stloc.1 IL_000c: ldloca.s V_7 IL_000e: initobj "T" IL_0014: ldloc.s V_7 IL_0016: box "T" IL_001b: brtrue.s IL_0028 IL_001d: ldloc.1 IL_001e: ldobj "T" IL_0023: stloc.0 IL_0024: ldloca.s V_0 IL_0026: br.s IL_0029 IL_0028: ldloc.1 IL_0029: call "System.Range Program.GetRange()" IL_002e: stloc.2 IL_002f: dup IL_0030: ldobj "T" IL_0035: call "int E.get_Length<T>(T)" IL_003a: stloc.3 IL_003b: ldloca.s V_2 IL_003d: call "System.Index System.Range.Start.get" IL_0042: stloc.s V_8 IL_0044: ldloca.s V_8 IL_0046: ldloc.3 IL_0047: call "int System.Index.GetOffset(int)" IL_004c: stloc.s V_4 IL_004e: ldloca.s V_2 IL_0050: call "System.Index System.Range.End.get" IL_0055: stloc.s V_8 IL_0057: ldloca.s V_8 IL_0059: ldloc.3 IL_005a: call "int System.Index.GetOffset(int)" IL_005f: ldloc.s V_4 IL_0061: sub IL_0062: stloc.s V_5 IL_0064: ldobj "T" IL_0069: ldloc.s V_4 IL_006b: ldloc.s V_5 IL_006d: call "ref int E.Slice<T>(T, int, int)" IL_0072: dup IL_0073: ldind.i4 IL_0074: call "int Program.GetValue()" IL_0079: add IL_007a: stind.i4 IL_007b: ret } """); } } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_FromEndIndexExpr_01() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_01 // ^GetInt() var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[^Program.GetInt()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[^GetInt()] += GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetInt() { System.Console.Write($"GetInt:{Program.F.F1} "); Program.F.F1++; return 0; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetInt:3 length:4 get:5 GetValue:6 set:7 final:7, GetInt:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 59 (0x3b) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetInt()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: call "int E.get_Length(S1)" IL_0017: ldloc.0 IL_0018: sub IL_0019: stloc.1 IL_001a: dup IL_001b: ldobj "S1" IL_0020: ldloc.1 IL_0021: call "int E.get_Item(S1, int)" IL_0026: call "int Program.GetValue()" IL_002b: add IL_002c: stloc.2 IL_002d: ldobj "S1" IL_0032: ldloc.1 IL_0033: ldloc.2 IL_0034: call "void E.set_Item(S1, int, int)" IL_0039: nop IL_003a: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 55 (0x37) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetInt()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: call "int E.get_Length(S1)" IL_0013: ldloc.0 IL_0014: sub IL_0015: stloc.1 IL_0016: dup IL_0017: ldobj "S1" IL_001c: ldloc.1 IL_001d: call "int E.get_Item(S1, int)" IL_0022: call "int Program.GetValue()" IL_0027: add IL_0028: stloc.2 IL_0029: ldobj "S1" IL_002e: ldloc.1 IL_002f: ldloc.2 IL_0030: call "void E.set_Item(S1, int, int)" IL_0035: nop IL_0036: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_ConversionFromIntIndexExpr_01() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_01 // (Index)GetInt() var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[(System.Index)Program.GetInt()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[(System.Index)Program.GetInt()] += Program.GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetInt() { System.Console.Write($"GetInt:{Program.F.F1} "); Program.F.F1++; return 0; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetInt:3 get:4 GetValue:5 set:6 final:6, GetInt:3 get:4 GetValue:5 set:6 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", """ { // Code size 45 (0x2d) .maxstack 3 .locals init (int V_0, int V_1) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: call "int Program.GetInt()" IL_000b: stloc.0 IL_000c: dup IL_000d: ldobj "S1" IL_0012: ldloc.0 IL_0013: call "int E.get_Item(S1, int)" IL_0018: call "int Program.GetValue()" IL_001d: add IL_001e: stloc.1 IL_001f: ldobj "S1" IL_0024: ldloc.0 IL_0025: ldloc.1 IL_0026: call "void E.set_Item(S1, int, int)" IL_002b: nop IL_002c: ret } """); verifier.VerifyIL("S1.Test2", """ { // Code size 41 (0x29) .maxstack 3 .locals init (int V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "int Program.GetInt()" IL_0007: stloc.0 IL_0008: dup IL_0009: ldobj "S1" IL_000e: ldloc.0 IL_000f: call "int E.get_Item(S1, int)" IL_0014: call "int Program.GetValue()" IL_0019: add IL_001a: stloc.1 IL_001b: ldobj "S1" IL_0020: ldloc.0 IL_0021: ldloc.1 IL_0022: call "void E.set_Item(S1, int, int)" IL_0027: nop IL_0028: ret } """); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_CompoundAssignment_ConversionFromIntIndexExpr_02(string refKind) { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_02 // struct extension parameter passed by ref, (Index)GetInt() var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[(System.Index)Program.GetInt()] += Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[(System.Index)Program.GetInt()] += Program.GetValue(); } public static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return 1; } public static int GetInt() { System.Console.Write($"GetInt:{Program.F.F1} "); Program.F.F1++; return 0; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetInt:3 get:4 GetValue:5 set:6 final:6, GetInt:3 get:4 GetValue:5 set:6 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1", $$""" { // Code size 35 (0x23) .maxstack 4 .locals init (S1& V_0, int V_1) IL_0000: nop IL_0001: ldsflda "S1 Program.F" IL_0006: stloc.0 IL_0007: call "int Program.GetInt()" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: ldloc.1 IL_000f: ldloc.0 IL_0010: ldloc.1 IL_0011: call "int E.get_Item({{refKind}} S1, int)" IL_0016: call "int Program.GetValue()" IL_001b: add IL_001c: call "void E.set_Item({{refKind}} S1, int, int)" IL_0021: nop IL_0022: ret } """); verifier.VerifyIL("S1.Test2", $$""" { // Code size 31 (0x1f) .maxstack 4 .locals init (S1& V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: call "int Program.GetInt()" IL_0008: stloc.1 IL_0009: ldloc.0 IL_000a: ldloc.1 IL_000b: ldloc.0 IL_000c: ldloc.1 IL_000d: call "int E.get_Item({{refKind}} S1, int)" IL_0012: call "int Program.GetValue()" IL_0017: add IL_0018: call "void E.set_Item({{refKind}} S1, int, int)" IL_001d: nop IL_001e: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_CompoundAssignment_ConversionFromIntIndexExpr_03() { // sibling to ExtensionTests.IndexerAccess_CompoundAssignment_03 // class extension parameter, (Index)GetInt() var src = """ static class E { extension(C1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 3; } } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { F[GetIndex()] += GetValue(); } static int GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return 1; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); var verifier = CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); verifier.VerifyIL("Program.Test", """ { // Code size 49 (0x31) .maxstack 4 .locals init (C1 V_0, int V_1, System.Index V_2) IL_0000: nop IL_0001: ldsfld "C1 Program.F" IL_0006: stloc.0 IL_0007: call "System.Index Program.GetIndex()" IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: call "int E.get_Length(C1)" IL_0015: call "int System.Index.GetOffset(int)" IL_001a: stloc.1 IL_001b: ldloc.0 IL_001c: ldloc.1 IL_001d: ldloc.0 IL_001e: ldloc.1 IL_001f: call "int E.get_Item(C1, int)" IL_0024: call "int Program.GetValue()" IL_0029: add IL_002a: call "void E.set_Item(C1, int, int)" IL_002f: nop IL_0030: ret } """); } [Fact] public void ImplicitIndexIndexerAccess_PrefixIncrementAssignment_01() { // sibling to ExtensionTests.PropertyAccess_PrefixIncrementAssignment_01 // struct extension parameter var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { ++this[Program.GetIndex()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { ++F[GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 set:6 final:6, GetIndex:3 length:4 get:5 set:6 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_PrefixIncrementAssignment_02(string refKind) { // refKind on extension parameter var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { ++this[Program.GetIndex()]; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { ++F[GetIndex()]; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 set:6 final:6, GetIndex:3 length:4 get:5 set:6 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_PrefixIncrementAssignment_03() { // class receiver var src = """ static class E { extension(C1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { ++F[GetIndex()]; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 set:3 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_PostfixIncrementAssignment_01() { // sibling to ExtensionTests.PropertyAccess_PostfixIncrementAssignment_01 // struct extension parameter var src = """ static class E { extension(S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[Program.GetIndex()]++; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()]++; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 set:6 final:6, GetIndex:3 length:4 get:5 set:6 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_PostfixIncrementAssignment_02(string refKind) { // refKind on extension parameter var src = $$$""" static class E { extension({{{refKind}}} S1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[Program.GetIndex()]++; } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()]++; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 set:6 final:6, GetIndex:3 length:4 get:5 set:6 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_PostfixIncrementAssignment_03() { // class receiver var src = """ static class E { extension(C1 x) { public int this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return 0; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { F[GetIndex()]++; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 set:3 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_ConditionalAssignment_01() { // sibling to ExtensionTests.PropertyAccess_ConditionalAssignment_01 (??=) // struct extension parameter, nullable result var src = """ #nullable enable static class E { extension(S1 x) { public string? this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return null; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[Program.GetIndex()] ??= Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] ??= GetValue(); } public static string GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return "v"; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_ConditionalAssignment_02(string refKind) { // refKind on extension parameter var src = $$$""" #nullable enable static class E { extension({{{refKind}}} S1 x) { public string? this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return null; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { this[Program.GetIndex()] ??= Program.GetValue(); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { F[GetIndex()] ??= GetValue(); } public static string GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return "v"; } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7, GetIndex:3 length:4 get:5 GetValue:6 set:7 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_ConditionalAssignment_03() { // class receiver var src = """ #nullable enable static class E { extension(C1 x) { public string? this[int i] { get { System.Console.Write($"get:{x.F1} "); Program.F.F1++; return null; } set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } class C1 { public int F1; } class Program { public static C1 F = null!; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { F[GetIndex()] ??= GetValue(); } static string GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return "v"; } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 get:3 GetValue:6 set:3 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_DeconstructAssignment_01() { // sibling to ExtensionTests.PropertyAccess_DeconstructAssignment_01 // struct extension parameter, indexer assigned via tuple deconstruction var src = """ static class E { extension(S1 x) { public int this[int i] { set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; public void Test2() { (this[Program.GetIndex()], int y) = Program.GetTuple(); System.Console.Write($"y:{y} "); } } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"final:{F.F1}"); System.Console.Write(", "); F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"final:{F.F1}"); } static void Test1() { (F[GetIndex()], int y) = GetTuple(); System.Console.Write($"y:{y} "); } public static (int, int) GetTuple() { System.Console.Write($"GetTuple:{Program.F.F1} "); Program.F.F1++; return (1, 2); } public static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetTuple:5 set:6 y:2 final:6, GetIndex:3 length:4 GetTuple:5 set:6 y:2 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitIndexIndexerAccess_DeconstructAssignment_02() { // by-ref struct value (deconstruction needs an assignable receiver, so 'in'/'ref readonly' are not valid) var src = """ static class E { extension(S1 x) { public int this[int i] { set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } struct S1 { public int F1; } class Program { public static S1 F; static void Main() { F = new S1 { F1 = 3 }; Test1(ref F); System.Console.Write($"final:{F.F1}"); } static void Test1(ref S1 f) { (f[GetIndex()], int y) = GetTuple(); System.Console.Write($"y:{y} "); } static (int, int) GetTuple() { System.Console.Write($"GetTuple:{Program.F.F1} "); Program.F.F1++; return (1, 2); } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F.F1++; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:4 GetTuple:5 set:6 y:2 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); // 'in' and 'ref readonly' are rejected: deconstruction requires an assignable receiver var src2 = """ static class E { extension(S1 x) { public int this[int i] { set {} } public int Length => 3; } } struct S1 { } class Program { public static S1 F; static void Test1(in S1 f) { (f[^1], int _) = (1, 2); } static void Test2(ref readonly S1 f) { (f[^1], int _) = (1, 2); } } """; CreateCompilation(src2, targetFramework: TargetFramework.Net100).VerifyEmitDiagnostics( // (16,22): warning CS0649: Field 'Program.F' is never assigned to, and will always have its default value // public static S1 F; Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F").WithArguments("Program.F", "").WithLocation(16, 22), // (20,10): error CS8332: Cannot assign to a member of variable 'f' or use it as the right hand side of a ref assignment because it is a readonly variable // (f[^1], int _) = (1, 2); Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "f[^1]").WithArguments("variable", "f").WithLocation(20, 10), // (25,10): error CS8332: Cannot assign to a member of variable 'f' or use it as the right hand side of a ref assignment because it is a readonly variable // (f[^1], int _) = (1, 2); Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "f[^1]").WithArguments("variable", "f").WithLocation(25, 10)); } [Fact] public void ImplicitIndexIndexerAccess_DeconstructAssignment_03() { // class receiver var src = """ static class E { extension(C1 x) { public int this[int i] { set { System.Console.Write($"set:{x.F1} "); } } public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 3; } } } } class C1 { public int F1; } class Program { public static C1 F; static void Main() { F = new C1 { F1 = 3 }; Test(); System.Console.Write($"final:{F.F1}"); } static void Test() { (F[GetIndex()], int y) = GetTuple(); System.Console.Write($"y:{y} "); } static (int, int) GetTuple() { System.Console.Write($"GetTuple:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return (1, 2); } static System.Index GetIndex() { System.Console.Write($"GetIndex:{Program.F.F1} "); Program.F = new C1 { F1 = Program.F.F1 + 1 }; return ^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 length:3 GetTuple:5 set:3 y:2 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_PrefixIncrementAssignment_01() { // Range pattern with ref-returning extension Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public void Test2() { ++this[Program.GetRange()]; } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { ++F[GetRange()]; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 result:11 final:6, GetRange:3 length:4 Slice:5 result:11 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_PostfixIncrementAssignment_01() { // Range pattern with ref-returning extension Slice var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public void Test2() { this[Program.GetRange()]++; } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()]++; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 result:11 final:6, GetRange:3 length:4 Slice:5 result:11 final:6"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_ConditionalAssignment_01() { // Range pattern with ref-returning extension Slice (string?) var src = """ #nullable enable static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref string? Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public void Test2() { this[Program.GetRange()] ??= Program.GetValue(); } } class Program { public static S1 F; public static string? Result; static void Main() { Result = null; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = null; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { F[GetRange()] ??= GetValue(); } public static string GetValue() { System.Console.Write($"GetValue:{Program.F.F1} "); Program.F.F1++; return "v"; } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetValue:6 result:v final:7, GetRange:3 length:4 Slice:5 GetValue:6 result:v final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Fact] public void ImplicitRangeIndexerAccess_DeconstructAssignment_01() { // Range pattern with ref-returning extension Slice (deconstruction target uses ref slice) var src = """ static class E { extension(S1 x) { public int Length { get { System.Console.Write($"length:{x.F1} "); Program.F.F1++; return 5; } } public ref int Slice(int start, int length) { System.Console.Write($"Slice:{x.F1} "); Program.F.F1++; return ref Program.Result; } } } struct S1 { public int F1; public void Test2() { (this[Program.GetRange()], int y) = Program.GetTuple(); System.Console.Write($"y:{y} "); } } class Program { public static S1 F; public static int Result; static void Main() { Result = 10; F = new S1 { F1 = 3 }; Test1(); System.Console.Write($"result:{Result} final:{F.F1}"); System.Console.Write(", "); Result = 10; F = new S1 { F1 = 3 }; F.Test2(); System.Console.Write($"result:{Result} final:{F.F1}"); } static void Test1() { (F[GetRange()], int y) = GetTuple(); System.Console.Write($"y:{y} "); } public static (int, int) GetTuple() { System.Console.Write($"GetTuple:{Program.F.F1} "); Program.F.F1++; return (1, 2); } public static System.Range GetRange() { System.Console.Write($"GetRange:{Program.F.F1} "); Program.F.F1++; return 1..^1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetRange:3 length:4 Slice:5 GetTuple:6 y:2 result:1 final:7, GetRange:3 length:4 Slice:5 GetTuple:6 y:2 result:1 final:7"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_PrefixIncrementAssignment_ReadonlyReceiver_041(string refKind) { // sibling to ExtensionTests.PropertyAccess_PrefixIncrementAssignment_ReadonlyReceiver_041 // unconstrained extension parameter, ref/ref readonly/in struct value var src = $$$""" static class E { extension<T>(T x) { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>({{{refKind}}} T f) { ++f[GetIndex()]; } static int GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 get:4 set:5 final:5"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } [Theory] [InlineData("ref")] [InlineData("ref readonly")] [InlineData("in")] public void ImplicitIndexIndexerAccess_PostfixIncrementAssignment_ReadonlyReceiver_041(string refKind) { // unconstrained extension parameter, ref/ref readonly/in struct value var src = $$$""" static class E { extension<T>(T x) { public int this[int i] { get { System.Console.Write($"get:{((S1)(object)x).F1} "); Program<S1>.F.F1++; return 0; } set { System.Console.Write($"set:{((S1)(object)x).F1} "); } } } } struct S1 { public int F1; } class Program<T> { public static T F; } class Program { static void Main() { Program<S1>.F = new S1 { F1 = 3 }; Test1({{{(refKind == "ref" ? "ref" : "in")}}} Program<S1>.F); System.Console.Write($"final:{Program<S1>.F.F1}"); } static void Test1<T>({{{refKind}}} T f) { f[GetIndex()]++; } static int GetIndex() { System.Console.Write($"GetIndex:{Program<S1>.F.F1} "); Program<S1>.F.F1++; return 1; } } """; var comp = CreateCompilation(src, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net100); CompileAndVerify(comp, expectedOutput: ExpectedOutput("GetIndex:3 get:4 set:5 final:5"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); } }