/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenInParametersTests.cs
5 094 строки
145 KB
Stephan Bauer
Remove warning from 'in enum' extension methods (#80708)
27 окт 2025, 12:41
Не верифицирован
27 окт 2025, 12:41
459ce89
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { [CompilerTrait(CompilerFeature.ReadOnlyReferences)] public class CodeGenInParametersTests : CompilingTestBase { [Fact] public void ThreeParamReorder() { var comp = CompileAndVerify(@" using System; class C { public struct S { public int X; } private static S _field; public static ref S GetField(int order) { Console.WriteLine(""GetField "" + _field.X++ + "" "" + order); return ref _field; } public static void Main() { M(y: in GetField(0).X, z: GetField(1).X, x: GetField(2).X); } static void M(in int x, in int y, in int z) { Console.WriteLine(x); Console.WriteLine(y); Console.WriteLine(z); } }", expectedOutput: @"GetField 0 0 GetField 1 1 GetField 2 2 3 3 3"); comp.VerifyIL("C.Main", @" { // Code size 43 (0x2b) .maxstack 3 .locals init (int& V_0, int& V_1) IL_0000: ldc.i4.0 IL_0001: call ""ref C.S C.GetField(int)"" IL_0006: ldflda ""int C.S.X"" IL_000b: stloc.0 IL_000c: ldc.i4.1 IL_000d: call ""ref C.S C.GetField(int)"" IL_0012: ldflda ""int C.S.X"" IL_0017: stloc.1 IL_0018: ldc.i4.2 IL_0019: call ""ref C.S C.GetField(int)"" IL_001e: ldflda ""int C.S.X"" IL_0023: ldloc.0 IL_0024: ldloc.1 IL_0025: call ""void C.M(in int, in int, in int)"" IL_002a: ret }"); } [Fact] public void InParamReadonlyFieldReorder() { var comp = CompileAndVerify(@" using System; class C { private static readonly int _f = 0; public C() { M(y: _f, x: _f + 1); M(y: in _f, x: _f + 1); } public static void Main() { M(y: _f, x: _f + 1); M(y: in _f, x: _f + 1); _ = new C(); } static void M(in int x, in int y) { Console.WriteLine(x); Console.WriteLine(y); } }", expectedOutput: @"1 0 1 0 1 0 1 0", verify: Verification.Fails); comp.VerifyIL("C.Main", @" { // Code size 51 (0x33) .maxstack 2 .locals init (int& V_0, int V_1) IL_0000: ldsflda ""int C._f"" IL_0005: stloc.0 IL_0006: ldsfld ""int C._f"" IL_000b: ldc.i4.1 IL_000c: add IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldloc.0 IL_0011: call ""void C.M(in int, in int)"" IL_0016: ldsflda ""int C._f"" IL_001b: stloc.0 IL_001c: ldsfld ""int C._f"" IL_0021: ldc.i4.1 IL_0022: add IL_0023: stloc.1 IL_0024: ldloca.s V_1 IL_0026: ldloc.0 IL_0027: call ""void C.M(in int, in int)"" IL_002c: newobj ""C..ctor()"" IL_0031: pop IL_0032: ret }"); } [Fact] public void InParamCallOptionalArg() { var comp = CompileAndVerify(@" using System; class C { public static void Main() { int x = 1; M(in x); } static void M(in int x, int y = 0) { Console.WriteLine(x); Console.WriteLine(y); } }", expectedOutput: @"1 0"); comp.VerifyIL("C.Main", @" { // Code size 11 (0xb) .maxstack 2 .locals init (int V_0) //x IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.0 IL_0005: call ""void C.M(in int, int)"" IL_000a: ret }"); } [Fact] public void InParamCtorOptionalArg() { var comp = CompileAndVerify(@" using System; class C { public static void Main() { int x = 1; new C(in x); } public C(in int x, int y = 0) { Console.WriteLine(x); Console.WriteLine(y); } }", expectedOutput: @"1 0"); comp.VerifyIL("C.Main", @" { // Code size 12 (0xc) .maxstack 2 .locals init (int V_0) //x IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.0 IL_0005: newobj ""C..ctor(in int, int)"" IL_000a: pop IL_000b: ret }"); } [Fact] public void InParamInitializerOptionalArg() { var comp = CompileAndVerify(@" using System; class C { public static int _x = 1; public int _f = M(in _x); public static void Main() { new C(); } public static int M(in int x, int y = 0) { Console.WriteLine(x); Console.WriteLine(y); return x; } }", expectedOutput: @"1 0"); comp.VerifyIL("C..ctor", @" { // Code size 24 (0x18) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldsflda ""int C._x"" IL_0006: ldc.i4.0 IL_0007: call ""int C.M(in int, int)"" IL_000c: stfld ""int C._f"" IL_0011: ldarg.0 IL_0012: call ""object..ctor()"" IL_0017: ret }"); } [Fact] public void InParamCollectionInitializerOptionalArg() { var comp = CompileAndVerify(@" using System; using System.Collections; class C : IEnumerable { public static void Main() { int x = 1; new C() { x }; } public IEnumerator GetEnumerator() => null; public void Add(in int x, int y = 0) { Console.WriteLine(x); Console.WriteLine(y); } }"); comp.VerifyIL("C.Main", @" { // Code size 16 (0x10) .maxstack 3 .locals init (int V_0) //x IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: newobj ""C..ctor()"" IL_0007: ldloca.s V_0 IL_0009: ldc.i4.0 IL_000a: callvirt ""void C.Add(in int, int)"" IL_000f: ret }"); } [Fact] public void InParamSetter() { var comp = CompileAndVerify(@" using System; using System.Collections; class C { static int _f = 1; public static void Main() { new C()[in _f] = 0; } public IEnumerator GetEnumerator() => null; public int this[in int x, int y = 0] { get => x; set { Console.WriteLine(x); Console.WriteLine(y); _f++; Console.WriteLine(x); } } }", expectedOutput: @"1 0 2"); comp.VerifyIL("C.Main", @" { // Code size 18 (0x12) .maxstack 4 IL_0000: newobj ""C..ctor()"" IL_0005: ldsflda ""int C._f"" IL_000a: ldc.i4.0 IL_000b: ldc.i4.0 IL_000c: call ""void C.this[in int, int].set"" IL_0011: ret }"); } [Fact] public void InParamParamsArg() { var comp = CompileAndVerify(@" using System; class C { public static void Main() { int x = 1; M(in x); } public static void M(in int x, params int[] p) { Console.WriteLine(x); Console.WriteLine(p.Length); } }", expectedOutput: @"1 0"); comp.VerifyIL("C.Main", @" { // Code size 15 (0xf) .maxstack 2 .locals init (int V_0) //x IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: call ""int[] System.Array.Empty<int>()"" IL_0009: call ""void C.M(in int, params int[])"" IL_000e: ret }"); } [Fact] public void EmptyParamsArg() { var comp = CompileAndVerify(@" using System; class C { public static void Main() { M(y: 2, x: 1); } public static void M(int x, int y, params int[] p) { Console.WriteLine(x); Console.WriteLine(p.Length); } }", expectedOutput: @"1 0"); comp.VerifyIL("C.Main", @" { // Code size 13 (0xd) .maxstack 3 IL_0000: ldc.i4.1 IL_0001: ldc.i4.2 IL_0002: call ""int[] System.Array.Empty<int>()"" IL_0007: call ""void C.M(int, int, params int[])"" IL_000c: ret }"); } [Fact] public void InParamReorder() { var comp = CompileAndVerify(@" using System; class C { public struct S { public int X; } private static S _field; public static ref S GetField(int order) { Console.WriteLine(""GetField "" + _field.X++ + "" "" + order); return ref _field; } public static void Main() { M(y: in GetField(0).X, x: GetField(1).X); } static void M(in int x, in int y) { Console.WriteLine(x); Console.WriteLine(y); } }", expectedOutput: @"GetField 0 0 GetField 1 1 2 2"); comp.VerifyIL("C.Main", @" { // Code size 30 (0x1e) .maxstack 2 .locals init (int& V_0) IL_0000: ldc.i4.0 IL_0001: call ""ref C.S C.GetField(int)"" IL_0006: ldflda ""int C.S.X"" IL_000b: stloc.0 IL_000c: ldc.i4.1 IL_000d: call ""ref C.S C.GetField(int)"" IL_0012: ldflda ""int C.S.X"" IL_0017: ldloc.0 IL_0018: call ""void C.M(in int, in int)"" IL_001d: ret }"); } [Fact] public void InParamCtorReorder() { var comp = CompileAndVerify(@" using System; class C { public struct S { public int X; } private static S _field; public static ref S GetField(int order) { Console.WriteLine(""GetField "" + _field.X++ + "" "" + order); return ref _field; } public static void Main() { new C(y: in GetField(0).X, x: GetField(1).X); } public C(in int x, in int y) { Console.WriteLine(x); Console.WriteLine(y); } }", expectedOutput: @"GetField 0 0 GetField 1 1 2 2"); comp.VerifyIL("C.Main", @" { // Code size 31 (0x1f) .maxstack 2 .locals init (int& V_0) IL_0000: ldc.i4.0 IL_0001: call ""ref C.S C.GetField(int)"" IL_0006: ldflda ""int C.S.X"" IL_000b: stloc.0 IL_000c: ldc.i4.1 IL_000d: call ""ref C.S C.GetField(int)"" IL_0012: ldflda ""int C.S.X"" IL_0017: ldloc.0 IL_0018: newobj ""C..ctor(in int, in int)"" IL_001d: pop IL_001e: ret }"); } [Fact] public void InIndexerReorder() { var verifier = CompileAndVerify(@" using System; class C { public struct S { public int X; } private static S _field; public static ref S GetField(int order) { Console.WriteLine(""GetField "" + _field.X++ + "" "" + order); return ref _field; } public static void Main() { var c = new C(); _ = c[y: in GetField(0).X, x: in GetField(1).X]; } int this[in int x, in int y] { get { Console.WriteLine(x); Console.WriteLine(y); return x; } } }", expectedOutput: @"GetField 0 0 GetField 1 1 2 2"); verifier.VerifyIL("C.Main", @" { // Code size 36 (0x24) .maxstack 3 .locals init (int& V_0) IL_0000: newobj ""C..ctor()"" IL_0005: ldc.i4.0 IL_0006: call ""ref C.S C.GetField(int)"" IL_000b: ldflda ""int C.S.X"" IL_0010: stloc.0 IL_0011: ldc.i4.1 IL_0012: call ""ref C.S C.GetField(int)"" IL_0017: ldflda ""int C.S.X"" IL_001c: ldloc.0 IL_001d: callvirt ""int C.this[in int, in int].get"" IL_0022: pop IL_0023: ret }"); } [Fact] public void InIndexerReorderWithCopy() { var verifier = CompileAndVerify(@" using System; class C { public struct S { public int X; } private static S _field; public static ref S GetField(int order) { Console.WriteLine(""GetField "" + _field.X++ + "" "" + order); return ref _field; } public static void Main() { var c = new C(); _ = c[y: GetField(0).X, x: GetField(1).X + 1]; _ = c[y: GetField(0).X + 2, x: GetField(1).X]; } int this[in int x, in int y] { get { Console.WriteLine(x); Console.WriteLine(y); return x; } } }", expectedOutput: @"GetField 0 0 GetField 1 1 3 2 GetField 2 0 GetField 3 1 4 5"); verifier.VerifyIL("C.Main", @" { // Code size 75 (0x4b) .maxstack 4 .locals init (int& V_0, int V_1) IL_0000: newobj ""C..ctor()"" IL_0005: dup IL_0006: ldc.i4.0 IL_0007: call ""ref C.S C.GetField(int)"" IL_000c: ldflda ""int C.S.X"" IL_0011: stloc.0 IL_0012: ldc.i4.1 IL_0013: call ""ref C.S C.GetField(int)"" IL_0018: ldfld ""int C.S.X"" IL_001d: ldc.i4.1 IL_001e: add IL_001f: stloc.1 IL_0020: ldloca.s V_1 IL_0022: ldloc.0 IL_0023: callvirt ""int C.this[in int, in int].get"" IL_0028: pop IL_0029: ldc.i4.0 IL_002a: call ""ref C.S C.GetField(int)"" IL_002f: ldfld ""int C.S.X"" IL_0034: ldc.i4.2 IL_0035: add IL_0036: stloc.1 IL_0037: ldc.i4.1 IL_0038: call ""ref C.S C.GetField(int)"" IL_003d: ldflda ""int C.S.X"" IL_0042: ldloca.s V_1 IL_0044: callvirt ""int C.this[in int, in int].get"" IL_0049: pop IL_004a: ret }"); } [Fact] public void InParamSetterReorder() { var comp = CompileAndVerify(@" using System; using System.Collections; class C { static int _f = 1; public static void Main() { new C()[y: in _f, x: _f] = 0; } public IEnumerator GetEnumerator() => null; public int this[in int x, in int y] { get => x; set { Console.WriteLine(x); Console.WriteLine(y); _f++; Console.WriteLine(x); Console.WriteLine(y); } } }", expectedOutput: @"1 1 2 2"); comp.VerifyIL("C.Main", @" { // Code size 24 (0x18) .maxstack 4 .locals init (int& V_0) IL_0000: newobj ""C..ctor()"" IL_0005: ldsflda ""int C._f"" IL_000a: stloc.0 IL_000b: ldsflda ""int C._f"" IL_0010: ldloc.0 IL_0011: ldc.i4.0 IL_0012: call ""void C.this[in int, in int].set"" IL_0017: ret }"); } [Fact] public void InParamMemberInitializerReorder() { var comp = CompileAndVerify(@" using System; using System.Collections; class C { static int _f = 1; public static void Main() { new C() { [y: in _f, x: _f] = 0 }; } public IEnumerator GetEnumerator() => null; public int this[in int x, in int y] { get => x; set { Console.WriteLine(x); Console.WriteLine(y); _f++; Console.WriteLine(x); Console.WriteLine(y); } } }", expectedOutput: @"1 1 2 2"); comp.VerifyIL("C.Main", @" { // Code size 26 (0x1a) .maxstack 4 .locals init (int& V_0, int& V_1) IL_0000: newobj ""C..ctor()"" IL_0005: ldsflda ""int C._f"" IL_000a: stloc.0 IL_000b: ldsflda ""int C._f"" IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: ldloc.0 IL_0013: ldc.i4.0 IL_0014: callvirt ""void C.this[in int, in int].set"" IL_0019: ret }"); } [Fact] public void RefReturnParamAccess() { var text = @" class Program { static ref readonly int M(in int x) { return ref x; } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails); comp.VerifyIL("Program.M(in int)", @" { // Code size 2 (0x2) .maxstack 1 IL_0000: ldarg.0 IL_0001: ret }"); } [Fact] public void InParamPassLValue() { var text = @" struct Program { public static void Main() { var local = 42; System.Console.WriteLine(M(local)); S1 s1 = default(S1); s1.X = 42; s1 += s1; System.Console.WriteLine(s1.X); } static ref readonly int M(in int x) { return ref x; } struct S1 { public int X; public static S1 operator +(in S1 x, in S1 y) { return new S1(){X = x.X + y.X}; } } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails, expectedOutput: @"42 84"); comp.VerifyIL("Program.Main()", @" { // Code size 55 (0x37) .maxstack 2 .locals init (int V_0, //local Program.S1 V_1) //s1 IL_0000: ldc.i4.s 42 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: call ""ref readonly int Program.M(in int)"" IL_000a: ldind.i4 IL_000b: call ""void System.Console.WriteLine(int)"" IL_0010: ldloca.s V_1 IL_0012: initobj ""Program.S1"" IL_0018: ldloca.s V_1 IL_001a: ldc.i4.s 42 IL_001c: stfld ""int Program.S1.X"" IL_0021: ldloca.s V_1 IL_0023: ldloca.s V_1 IL_0025: call ""Program.S1 Program.S1.op_Addition(in Program.S1, in Program.S1)"" IL_002a: stloc.1 IL_002b: ldloc.1 IL_002c: ldfld ""int Program.S1.X"" IL_0031: call ""void System.Console.WriteLine(int)"" IL_0036: ret }"); } [Fact] public void InParamPassRValue() { var text = @" class Program { public static void Main() { System.Console.WriteLine(M(42)); System.Console.WriteLine(new Program()[5, 6]); System.Console.WriteLine(M(42)); System.Console.WriteLine(M(42)); } static ref readonly int M(in int x) { return ref x; } int this[in int x, in int y] => x + y; } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails, expectedOutput: @"42 11 42 42"); comp.VerifyIL("Program.Main()", @" { // Code size 72 (0x48) .maxstack 3 .locals init (int V_0, int V_1, int V_2) IL_0000: ldc.i4.s 42 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: call ""ref readonly int Program.M(in int)"" IL_000a: ldind.i4 IL_000b: call ""void System.Console.WriteLine(int)"" IL_0010: newobj ""Program..ctor()"" IL_0015: ldc.i4.5 IL_0016: stloc.1 IL_0017: ldloca.s V_1 IL_0019: ldc.i4.6 IL_001a: stloc.2 IL_001b: ldloca.s V_2 IL_001d: call ""int Program.this[in int, in int].get"" IL_0022: call ""void System.Console.WriteLine(int)"" IL_0027: ldc.i4.s 42 IL_0029: stloc.1 IL_002a: ldloca.s V_1 IL_002c: call ""ref readonly int Program.M(in int)"" IL_0031: ldind.i4 IL_0032: call ""void System.Console.WriteLine(int)"" IL_0037: ldc.i4.s 42 IL_0039: stloc.2 IL_003a: ldloca.s V_2 IL_003c: call ""ref readonly int Program.M(in int)"" IL_0041: ldind.i4 IL_0042: call ""void System.Console.WriteLine(int)"" IL_0047: ret }"); } [Fact] [CompilerTrait(CompilerFeature.PEVerifyCompat)] public void InParamPassRoField() { var text = @" class Program { public static readonly int F = 42; public static void Main() { System.Console.WriteLine(M(F)); } static ref readonly int M(in int x) { return ref x; } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails, expectedOutput: "42"); comp.VerifyIL("Program.Main()", @" { // Code size 17 (0x11) .maxstack 1 IL_0000: ldsflda ""int Program.F"" IL_0005: call ""ref readonly int Program.M(in int)"" IL_000a: ldind.i4 IL_000b: call ""void System.Console.WriteLine(int)"" IL_0010: ret }"); comp.VerifyIL("Program.M(in int)", @" { // Code size 2 (0x2) .maxstack 1 IL_0000: ldarg.0 IL_0001: ret }"); comp = CompileAndVerify(text, verify: Verification.Fails, expectedOutput: "42", parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature()); comp.VerifyIL("Program.Main()", @" { // Code size 20 (0x14) .maxstack 1 .locals init (int V_0) IL_0000: ldsfld ""int Program.F"" IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: call ""ref readonly int Program.M(in int)"" IL_000d: ldind.i4 IL_000e: call ""void System.Console.WriteLine(int)"" IL_0013: ret }"); } [Fact] [CompilerTrait(CompilerFeature.PEVerifyCompat)] public void InParamPassRoField1() { var text = @" class Program { public static readonly int F = 42; public static void Main() { System.Console.WriteLine(M(in F)); } static ref readonly int M(in int x) { return ref x; } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails, expectedOutput: "42"); comp.VerifyIL("Program.Main()", @" { // Code size 17 (0x11) .maxstack 1 IL_0000: ldsflda ""int Program.F"" IL_0005: call ""ref readonly int Program.M(in int)"" IL_000a: ldind.i4 IL_000b: call ""void System.Console.WriteLine(int)"" IL_0010: ret }"); comp.VerifyIL("Program.M(in int)", @" { // Code size 2 (0x2) .maxstack 1 IL_0000: ldarg.0 IL_0001: ret }"); comp = CompileAndVerify(text, verify: Verification.Fails, expectedOutput: "42", parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature()); comp.VerifyIL("Program.Main()", @" { // Code size 17 (0x11) .maxstack 1 IL_0000: ldsflda ""int Program.F"" IL_0005: call ""ref readonly int Program.M(in int)"" IL_000a: ldind.i4 IL_000b: call ""void System.Console.WriteLine(int)"" IL_0010: ret }"); } [Fact] public void InParamPassRoParamReturn() { var text = @" class Program { public static readonly int F = 42; public static void Main() { System.Console.WriteLine(M(F)); } static ref readonly int M(in int x) { return ref M1(x); } static ref readonly int M1(in int x) { return ref x; } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails, expectedOutput: "42"); comp.VerifyIL("Program.M(in int)", @" { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call ""ref readonly int Program.M1(in int)"" IL_0006: ret }"); } [Fact] public void InParamBase() { var text = @" class Program { public static readonly string S = ""hi""; public string SI; public static void Main() { var p = new P1(S); System.Console.WriteLine(p.SI); System.Console.WriteLine(p.M(42)); } public Program(in string x) { SI = x; } public virtual ref readonly int M(in int x) { return ref x; } } class P1 : Program { public P1(in string x) : base(x){} public override ref readonly int M(in int x) { return ref base.M(x); } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails, expectedOutput: @"hi 42"); comp.VerifyIL("P1..ctor(in string)", @" { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call ""Program..ctor(in string)"" IL_0007: ret }"); comp.VerifyIL("P1.M(in int)", @" { // Code size 8 (0x8) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: call ""ref readonly int Program.M(in int)"" IL_0007: ret }"); } [Fact] public void RefReturnParamAccess1() { var text = @" class Program { static ref readonly int M(in int x) { return ref x; } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails); comp.VerifyIL("Program.M(in int)", @" { // Code size 2 (0x2) .maxstack 1 IL_0000: ldarg.0 IL_0001: ret }"); } [Fact] public void InParamCannotAssign() { var text = @" class Program { static void M(in int arg1, in (int Alice, int Bob) arg2) { arg1 = 1; arg2.Alice = 2; arg1 ++; arg2.Alice --; arg1 += 1; arg2.Alice -= 2; } } "; var comp = CreateCompilationWithMscorlib461(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( // (6,9): error CS8331: Cannot assign to variable 'arg1' or use it as the right hand side of a ref assignment because it is a readonly variable // arg1 = 1; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(6, 9), // (7,9): error CS8332: Cannot assign to a member of variable 'arg2' or use it as the right hand side of a ref assignment because it is a readonly variable // arg2.Alice = 2; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(7, 9), // (9,9): error CS8331: Cannot assign to variable 'arg1' or use it as the right hand side of a ref assignment because it is a readonly variable // arg1 ++; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(9, 9), // (10,9): error CS8332: Cannot assign to a member of variable 'arg2' or use it as the right hand side of a ref assignment because it is a readonly variable // arg2.Alice --; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(10, 9), // (12,9): error CS8331: Cannot assign to variable 'arg1' or use it as the right hand side of a ref assignment because it is a readonly variable // arg1 += 1; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(12, 9), // (13,9): error CS8332: Cannot assign to a member of variable 'arg2' or use it as the right hand side of a ref assignment because it is a readonly variable // arg2.Alice -= 2; Diagnostic(ErrorCode.ERR_AssignReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(13, 9)); } [Fact] public void InParamCannotRefReturn() { var text = @" class Program { static ref readonly int M1_baseline(in int arg1, in (int Alice, int Bob) arg2) { // valid return ref arg1; } static ref readonly int M2_baseline(in int arg1, in (int Alice, int Bob) arg2) { // valid return ref arg2.Alice; } static ref int M1(in int arg1, in (int Alice, int Bob) arg2) { return ref arg1; } static ref int M2(in int arg1, in (int Alice, int Bob) arg2) { return ref arg2.Alice; } } "; var comp = CreateCompilationWithMscorlib461(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( // (18,20): error CS8333: Cannot return variable 'arg1' by writable reference because it is a readonly variable // return ref arg1; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(18, 20), // (23,20): error CS8334: Members of variable 'arg2' cannot be returned by writable reference because it is a readonly variable // return ref arg2.Alice; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(23, 20) ); } [Fact] public void InParamCannotAssignByref() { var text = @" class Program { static void M(in int arg1, in (int Alice, int Bob) arg2) { ref var y = ref arg1; ref int a = ref arg2.Alice; } } "; var comp = CreateCompilationWithMscorlib461(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( // (6,25): error CS8329: Cannot use variable 'arg1' as a ref or out value because it is a readonly variable // ref var y = ref arg1; Diagnostic(ErrorCode.ERR_RefReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(6, 25), // (7,25): error CS8330: Members of variable 'arg2' cannot be used as a ref or out value because it is a readonly variable // ref int a = ref arg2.Alice; Diagnostic(ErrorCode.ERR_RefReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(7, 25)); } [WorkItem(22306, "https://github.com/dotnet/roslyn/issues/22306")] [Fact] public void InParamCannotTakePtr() { var text = @" class Program { unsafe static void M(in int arg1, in (int Alice, int Bob) arg2) { int* a = & arg1; int* b = & arg2.Alice; fixed(int* c = & arg1) { } fixed(int* d = & arg2.Alice) { } } } "; var comp = CreateCompilationWithMscorlib461(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.UnsafeReleaseDll); comp.VerifyDiagnostics( // (6,18): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer // int* a = & arg1; Diagnostic(ErrorCode.ERR_FixedNeeded, "& arg1").WithLocation(6, 18), // (7,18): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer // int* b = & arg2.Alice; Diagnostic(ErrorCode.ERR_FixedNeeded, "& arg2.Alice").WithLocation(7, 18) ); } [Fact] public void InParamCannotReturnByOrdinaryRef() { var text = @" class Program { static ref int M(in int arg1, in (int Alice, int Bob) arg2) { bool b = true; if (b) { return ref arg1; } else { return ref arg2.Alice; } } } "; var comp = CreateCompilationWithMscorlib461(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( // (10,24): error CS8333: Cannot return variable 'arg1' by writable reference because it is a readonly variable // return ref arg1; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg1").WithArguments("variable", "arg1").WithLocation(10, 24), // (14,24): error CS8334: Members of variable 'arg2' cannot be returned by writable reference because it is a readonly variable // return ref arg2.Alice; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg2.Alice").WithArguments("variable", "arg2").WithLocation(14, 24) ); } [Fact] public void InParamCanReturnByRefReadonly() { var text = @" class Program { static ref readonly int M(in int arg1, in (int Alice, int Bob) arg2) { bool b = true; if (b) { return ref arg1; } else { return ref arg2.Alice; } } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails); comp.VerifyIL("Program.M", @" { // Code size 12 (0xc) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: brfalse.s IL_0005 IL_0003: ldarg.0 IL_0004: ret IL_0005: ldarg.1 IL_0006: ldflda ""int System.ValueTuple<int, int>.Item1"" IL_000b: ret }"); } [Fact, WorkItem(18357, "https://github.com/dotnet/roslyn/issues/18357")] public void InParamCanReturnByRefReadonlyNested() { var text = @" class Program { static ref readonly int M(in int arg1, in (int Alice, int Bob) arg2) { ref readonly int M1(in int arg11, in (int Alice, int Bob) arg21) { bool b = true; if (b) { return ref arg11; } else { return ref arg21.Alice; } } return ref M1(arg1, arg2); } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Fails); comp.VerifyIL("Program.<M>g__M1|0_0(in int, in System.ValueTuple<int, int>)", @" { // Code size 12 (0xc) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: brfalse.s IL_0005 IL_0003: ldarg.0 IL_0004: ret IL_0005: ldarg.1 IL_0006: ldflda ""int System.ValueTuple<int, int>.Item1"" IL_000b: ret }"); } [Fact, WorkItem(18357, "https://github.com/dotnet/roslyn/issues/18357")] public void InParamCannotReturnByRefNested() { var text = @" class Program { static ref readonly int M(in int arg1, in (int Alice, int Bob) arg2) { ref int M1(in int arg11, in (int Alice, int Bob) arg21) { bool b = true; if (b) { return ref arg11; } else { return ref arg21.Alice; } } return ref M1(arg1, arg2); } } "; var comp = CreateCompilationWithMscorlib461(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }); comp.VerifyDiagnostics( // (12,28): error CS8333: Cannot return variable 'arg11' by writable reference because it is a readonly variable // return ref arg11; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField, "arg11").WithArguments("variable", "arg11").WithLocation(12, 28), // (16,28): error CS8334: Members of variable 'arg21' cannot be returned by writable reference because it is a readonly variable // return ref arg21.Alice; Diagnostic(ErrorCode.ERR_RefReturnReadonlyNotField2, "arg21.Alice").WithArguments("variable", "arg21").WithLocation(16, 28) ); } [Fact] public void InParamOptional() { var text = @" class Program { static void Main() { System.Console.WriteLine(M()); } static int M(in int x = 42) => x; } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Passes, expectedOutput: @"42"); comp.VerifyIL("Program.Main", @" { // Code size 16 (0x10) .maxstack 1 .locals init (int V_0) IL_0000: ldc.i4.s 42 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: call ""int Program.M(in int)"" IL_000a: call ""void System.Console.WriteLine(int)"" IL_000f: ret }"); } [Fact] public void InParamConv() { var text = @" class Program { static void Main() { var arg = 42; System.Console.WriteLine(M(arg)); } static double M(in double x) => x; } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Passes, expectedOutput: @"42"); comp.VerifyIL("Program.Main", @" { // Code size 17 (0x11) .maxstack 1 .locals init (double V_0) IL_0000: ldc.i4.s 42 IL_0002: conv.r8 IL_0003: stloc.0 IL_0004: ldloca.s V_0 IL_0006: call ""double Program.M(in double)"" IL_000b: call ""void System.Console.WriteLine(double)"" IL_0010: ret }"); } [Fact] public void InParamAsyncSpill1() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { M1(1, await GetT(2), 3); } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } public static void M1(in int arg1, in int arg2, in int arg3) { System.Console.WriteLine(arg1 + arg2 + arg3); } } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Passes, expectedOutput: @"6"); } [Fact] public void ReadonlyParamAsyncSpillIn() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { int local = 1; M1(in RefReturning(ref local), await GetT(2), 3); } private static ref int RefReturning(ref int arg) { return ref arg; } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } public static void M1(in int arg1, in int arg2, in int arg3) { System.Console.WriteLine(arg1 + arg2 + arg3); } } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (14,19): error CS8178: A reference returned by a call to 'Program.RefReturning(ref int)' cannot be preserved across 'await' or 'yield' boundary. // M1(in RefReturning(ref local), await GetT(2), 3); Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "RefReturning(ref local)").WithArguments("Program.RefReturning(ref int)").WithLocation(14, 19) ); } [Fact] public void ReadonlyParamAsyncSpillIn2() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { int local = 1; M1(arg3: 3, arg1: RefReturning(ref local), arg2: await GetT(2)); } private static ref int RefReturning(ref int arg) { return ref arg; } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } public static void M1(in int arg1, in int arg2, in int arg3) { System.Console.WriteLine(arg1 + arg2 + arg3); } } "; var verifier = CompileAndVerify(text, verify: Verification.Fails, expectedOutput: "6"); verifier.VerifyIL("Program.<Test>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 180 (0xb4) .maxstack 3 .locals init (int V_0, int V_1, //local int V_2, System.Runtime.CompilerServices.TaskAwaiter<int> V_3, int V_4, System.Exception V_5) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Test>d__1.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_004f IL_000a: ldc.i4.1 IL_000b: stloc.1 IL_000c: ldarg.0 IL_000d: ldloca.s V_1 IL_000f: call ""ref int Program.RefReturning(ref int)"" IL_0014: ldind.i4 IL_0015: stfld ""int Program.<Test>d__1.<>7__wrap1"" IL_001a: ldc.i4.2 IL_001b: call ""System.Threading.Tasks.Task<int> Program.GetT<int>(int)"" IL_0020: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()"" IL_0025: stloc.3 IL_0026: ldloca.s V_3 IL_0028: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get"" IL_002d: brtrue.s IL_006b IL_002f: ldarg.0 IL_0030: ldc.i4.0 IL_0031: dup IL_0032: stloc.0 IL_0033: stfld ""int Program.<Test>d__1.<>1__state"" IL_0038: ldarg.0 IL_0039: ldloc.3 IL_003a: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__1.<>u__1"" IL_003f: ldarg.0 IL_0040: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__1.<>t__builder"" IL_0045: ldloca.s V_3 IL_0047: ldarg.0 IL_0048: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<Test>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<Test>d__1)"" IL_004d: leave.s IL_00b3 IL_004f: ldarg.0 IL_0050: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__1.<>u__1"" IL_0055: stloc.3 IL_0056: ldarg.0 IL_0057: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__1.<>u__1"" IL_005c: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>"" IL_0062: ldarg.0 IL_0063: ldc.i4.m1 IL_0064: dup IL_0065: stloc.0 IL_0066: stfld ""int Program.<Test>d__1.<>1__state"" IL_006b: ldloca.s V_3 IL_006d: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()"" IL_0072: stloc.2 IL_0073: ldarg.0 IL_0074: ldflda ""int Program.<Test>d__1.<>7__wrap1"" IL_0079: ldloca.s V_2 IL_007b: ldc.i4.3 IL_007c: stloc.s V_4 IL_007e: ldloca.s V_4 IL_0080: call ""void Program.M1(in int, in int, in int)"" IL_0085: leave.s IL_00a0 } catch System.Exception { IL_0087: stloc.s V_5 IL_0089: ldarg.0 IL_008a: ldc.i4.s -2 IL_008c: stfld ""int Program.<Test>d__1.<>1__state"" IL_0091: ldarg.0 IL_0092: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__1.<>t__builder"" IL_0097: ldloc.s V_5 IL_0099: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_009e: leave.s IL_00b3 } IL_00a0: ldarg.0 IL_00a1: ldc.i4.s -2 IL_00a3: stfld ""int Program.<Test>d__1.<>1__state"" IL_00a8: ldarg.0 IL_00a9: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__1.<>t__builder"" IL_00ae: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00b3: ret }"); } [Fact] public void ReadonlyParamAsyncSpillInRoField() { var text = @" using System.Threading.Tasks; class Program { public static readonly int F = 5; static void Main(string[] args) { Test().Wait(); } public static async Task Test() { int local = 1; M1(in F, await GetT(2), 3); } public static async Task<T> GetT<T>(T val) { await Task.Yield(); MutateReadonlyField(); return val; } private static unsafe void MutateReadonlyField() { fixed(int* ptr = &F) { *ptr = 42; } } public static void M1(in int arg1, in int arg2, in int arg3) { System.Console.WriteLine(arg1 + arg2 + arg3); } } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.UnsafeReleaseExe); var result = CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @"47"); var expectedIL = @" { // Code size 162 (0xa2) .maxstack 3 .locals init (int V_0, int V_1, System.Runtime.CompilerServices.TaskAwaiter<int> V_2, int V_3, System.Exception V_4) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Test>d__2.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_003f IL_000a: ldc.i4.2 IL_000b: call ""System.Threading.Tasks.Task<int> Program.GetT<int>(int)"" IL_0010: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()"" IL_0015: stloc.2 IL_0016: ldloca.s V_2 IL_0018: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get"" IL_001d: brtrue.s IL_005b IL_001f: ldarg.0 IL_0020: ldc.i4.0 IL_0021: dup IL_0022: stloc.0 IL_0023: stfld ""int Program.<Test>d__2.<>1__state"" IL_0028: ldarg.0 IL_0029: ldloc.2 IL_002a: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1"" IL_002f: ldarg.0 IL_0030: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder"" IL_0035: ldloca.s V_2 IL_0037: ldarg.0 IL_0038: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<Test>d__2>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<Test>d__2)"" IL_003d: leave.s IL_00a1 IL_003f: ldarg.0 IL_0040: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1"" IL_0045: stloc.2 IL_0046: ldarg.0 IL_0047: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Program.<Test>d__2.<>u__1"" IL_004c: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>"" IL_0052: ldarg.0 IL_0053: ldc.i4.m1 IL_0054: dup IL_0055: stloc.0 IL_0056: stfld ""int Program.<Test>d__2.<>1__state"" IL_005b: ldloca.s V_2 IL_005d: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()"" IL_0062: stloc.1 IL_0063: ldsflda ""int Program.F"" IL_0068: ldloca.s V_1 IL_006a: ldc.i4.3 IL_006b: stloc.3 IL_006c: ldloca.s V_3 IL_006e: call ""void Program.M1(in int, in int, in int)"" IL_0073: leave.s IL_008e } catch System.Exception { IL_0075: stloc.s V_4 IL_0077: ldarg.0 IL_0078: ldc.i4.s -2 IL_007a: stfld ""int Program.<Test>d__2.<>1__state"" IL_007f: ldarg.0 IL_0080: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder"" IL_0085: ldloc.s V_4 IL_0087: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_008c: leave.s IL_00a1 } IL_008e: ldarg.0 IL_008f: ldc.i4.s -2 IL_0091: stfld ""int Program.<Test>d__2.<>1__state"" IL_0096: ldarg.0 IL_0097: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__2.<>t__builder"" IL_009c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00a1: ret } "; result.VerifyIL("Program.<Test>d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", expectedIL); comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature()); result = CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @"47"); result.VerifyIL("Program.<Test>d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", expectedIL); } [Fact] public void InParamAsyncSpill2() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { M1(await GetT(1), await GetT(2), 3); } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } public static void M1(in int arg1, in int arg2, in int arg3) { System.Console.WriteLine(arg1 + arg2 + arg3); } } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Passes, expectedOutput: @"6"); } [WorkItem(20764, "https://github.com/dotnet/roslyn/issues/20764")] [Fact] public void InParamAsyncSpillMethods() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { var local = new S1(); // BASELINE - without an await // prints 3 42 3 3 note the aliasing, 3 is the last state of the local.f M1(GetLocal(ref local).f, 42, GetLocal(ref local).f, GetLocal(ref local).f); local = new S1(); // prints 1 42 3 3 note no aliasing for the first argument because of spilling of calls M1(GetLocal(ref local).f, await GetT(42), GetLocal(ref local).f, GetLocal(ref local).f); } private static ref readonly S1 GetLocal(ref S1 local) { local.f++; return ref local; } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } public static void M1(in int arg1, in int arg2, in int arg3, in int arg4) { System.Console.WriteLine(arg1); System.Console.WriteLine(arg2); System.Console.WriteLine(arg3); System.Console.WriteLine(arg4); } } public struct S1 { public int f; } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @" 3 42 3 3 1 42 3 3"); } [WorkItem(20764, "https://github.com/dotnet/roslyn/issues/20764")] [Fact] public void InParamAsyncSpillMethodsWriteable() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { var local = new S1(); // BASELINE - without an await // prints 3 42 3 3 note the aliasing, 3 is the last state of the local.f M1(GetLocalWriteable(ref local).f, 42, GetLocalWriteable(ref local).f, GetLocalWriteable(ref local).f); local = new S1(); // prints 1 42 3 3 note no aliasing for the first argument because of spilling of calls M1(GetLocalWriteable(ref local).f, await GetT(42), GetLocalWriteable(ref local).f, GetLocalWriteable(ref local).f); } private static ref S1 GetLocalWriteable(ref S1 local) { local.f++; return ref local; } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } public static void M1(in int arg1, in int arg2, in int arg3, in int arg4) { System.Console.WriteLine(arg1); System.Console.WriteLine(arg2); System.Console.WriteLine(arg3); System.Console.WriteLine(arg4); } } public struct S1 { public int f; } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @" 3 42 3 3 1 42 3 3"); } [WorkItem(20764, "https://github.com/dotnet/roslyn/issues/20764")] [Fact] public void InParamAsyncSpillStructField() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { var local = new S1(); // prints 2 42 2 2 note aliasing for all arguments regardless of spilling M1(local.f, await GetT(42), GetLocal(ref local).f, GetLocal(ref local).f); } private static ref readonly S1 GetLocal(ref S1 local) { local.f++; return ref local; } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } public static void M1(in int arg1, in int arg2, in int arg3, in int arg4) { System.Console.WriteLine(arg1); System.Console.WriteLine(arg2); System.Console.WriteLine(arg3); System.Console.WriteLine(arg4); } } public struct S1 { public int f; } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @" 2 42 2 2"); } [Fact] public void InParamAsyncSpillClassField() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { var local = new S1(); // prints 2 42 2 2 note aliasing for all arguments regardless of spilling M1(local.f, await GetT(42), GetLocal(ref local).f, GetLocal(ref local).f); } private static ref readonly S1 GetLocal(ref S1 local) { local.f++; return ref local; } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } public static void M1(in int arg1, in int arg2, in int arg3, in int arg4) { System.Console.WriteLine(arg1); System.Console.WriteLine(arg2); System.Console.WriteLine(arg3); System.Console.WriteLine(arg4); } } public class S1 { public int f; } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @" 2 42 2 2"); } [Fact] public void InParamAsyncSpillExtension() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { var local = new S1(); // prints 2 42 2 2 note aliasing for all arguments regardless of spilling local.f.M1(await GetT(42), GetLocalWriteable(ref local).f, GetLocalWriteable(ref local).f); } private static ref S1 GetLocalWriteable(ref S1 local) { local.f++; return ref local; } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } } static class Ext { public static void M1(in this int arg1, in int arg2, in int arg3, in int arg4) { System.Console.WriteLine(arg1); System.Console.WriteLine(arg2); System.Console.WriteLine(arg3); System.Console.WriteLine(arg4); } } public struct S1 { public int f; } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @" 2 42 2 2"); } [Fact] [CompilerTrait(CompilerFeature.PEVerifyCompat)] public void InParamAsyncSpillReadOnlyStructThis() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static async Task Test() { var local = new S1(); // BASELINE - without an await // prints 3 42 3 3 note the aliasing, 3 is the last state of the local.f GetLocal(ref local).M1( 42, GetLocal(ref local).f, GetLocal(ref local).f); local = new S1(); // prints 1 42 3 3 note no aliasing for the first argument because of spilling of a call GetLocal(ref local).M1(await GetT(42), GetLocal(ref local).f, GetLocal(ref local).f); local = new S1(); // prints 1 42 3 3 note no aliasing for the first argument because of spilling of a call GetLocalWriteable(ref local).M1(await GetT(42), GetLocal(ref local).f, GetLocal(ref local).f); } private static ref readonly S1 GetLocal(ref S1 local) { local = new S1(local.f + 1); return ref local; } private static ref S1 GetLocalWriteable(ref S1 local) { local = new S1(local.f + 1); return ref local; } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } } public readonly struct S1 { public readonly int f; public S1(int val) { this.f = val; } public void M1(in int arg2, in int arg3, in int arg4) { System.Console.WriteLine(this.f); System.Console.WriteLine(arg2); System.Console.WriteLine(arg3); System.Console.WriteLine(arg4); } } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @" 3 42 3 3 1 42 3 3 1 42 3 3"); comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature()); CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @" 3 42 2 3 1 42 2 3 1 42 2 3"); } [Fact] [CompilerTrait(CompilerFeature.PEVerifyCompat)] public void InParamAsyncSpillReadOnlyStructThis_NoValCapture() { var text = @" using System.Threading.Tasks; class Program { static void Main(string[] args) { Test().Wait(); } public static readonly S1 s1 = new S1(1); public static readonly S1 s2 = new S1(2); public static readonly S1 s3 = new S1(3); public static readonly S1 s4 = new S1(4); public static async Task Test() { s1.M1(s2, await GetT(s3), s4); } public static async Task<T> GetT<T>(T val) { await Task.Yield(); return val; } } public readonly struct S1 { public readonly int f; public S1(int val) { this.f = val; } public void M1(in S1 arg2, in S1 arg3, in S1 arg4) { System.Console.WriteLine(this.f); System.Console.WriteLine(arg2.f); System.Console.WriteLine(arg3.f); System.Console.WriteLine(arg4.f); } } "; var comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe); var v = CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: @" 1 2 3 4"); // NOTE: s1, s3 and s4 are all directly loaded via ldsflda and not spilled. v.VerifyIL("Program.<Test>d__5.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 170 (0xaa) .maxstack 4 .locals init (int V_0, S1 V_1, System.Runtime.CompilerServices.TaskAwaiter<S1> V_2, System.Exception V_3) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Test>d__5.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_0043 IL_000a: ldsfld ""S1 Program.s3"" IL_000f: call ""System.Threading.Tasks.Task<S1> Program.GetT<S1>(S1)"" IL_0014: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<S1> System.Threading.Tasks.Task<S1>.GetAwaiter()"" IL_0019: stloc.2 IL_001a: ldloca.s V_2 IL_001c: call ""bool System.Runtime.CompilerServices.TaskAwaiter<S1>.IsCompleted.get"" IL_0021: brtrue.s IL_005f IL_0023: ldarg.0 IL_0024: ldc.i4.0 IL_0025: dup IL_0026: stloc.0 IL_0027: stfld ""int Program.<Test>d__5.<>1__state"" IL_002c: ldarg.0 IL_002d: ldloc.2 IL_002e: stfld ""System.Runtime.CompilerServices.TaskAwaiter<S1> Program.<Test>d__5.<>u__1"" IL_0033: ldarg.0 IL_0034: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__5.<>t__builder"" IL_0039: ldloca.s V_2 IL_003b: ldarg.0 IL_003c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<S1>, Program.<Test>d__5>(ref System.Runtime.CompilerServices.TaskAwaiter<S1>, ref Program.<Test>d__5)"" IL_0041: leave.s IL_00a9 IL_0043: ldarg.0 IL_0044: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<S1> Program.<Test>d__5.<>u__1"" IL_0049: stloc.2 IL_004a: ldarg.0 IL_004b: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<S1> Program.<Test>d__5.<>u__1"" IL_0050: initobj ""System.Runtime.CompilerServices.TaskAwaiter<S1>"" IL_0056: ldarg.0 IL_0057: ldc.i4.m1 IL_0058: dup IL_0059: stloc.0 IL_005a: stfld ""int Program.<Test>d__5.<>1__state"" IL_005f: ldloca.s V_2 IL_0061: call ""S1 System.Runtime.CompilerServices.TaskAwaiter<S1>.GetResult()"" IL_0066: stloc.1 IL_0067: ldsflda ""S1 Program.s1"" IL_006c: ldsflda ""S1 Program.s2"" IL_0071: ldloca.s V_1 IL_0073: ldsflda ""S1 Program.s4"" IL_0078: call ""void S1.M1(in S1, in S1, in S1)"" IL_007d: leave.s IL_0096 } catch System.Exception { IL_007f: stloc.3 IL_0080: ldarg.0 IL_0081: ldc.i4.s -2 IL_0083: stfld ""int Program.<Test>d__5.<>1__state"" IL_0088: ldarg.0 IL_0089: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__5.<>t__builder"" IL_008e: ldloc.3 IL_008f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_0094: leave.s IL_00a9 } IL_0096: ldarg.0 IL_0097: ldc.i4.s -2 IL_0099: stfld ""int Program.<Test>d__5.<>1__state"" IL_009e: ldarg.0 IL_009f: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__5.<>t__builder"" IL_00a4: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00a9: ret } "); comp = CreateCompilationWithMscorlib46(text, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature()); v = CompileAndVerify(comp, verify: Verification.Passes, expectedOutput: @" 1 2 3 4"); // NOTE: s1, s3 and s4 are all directly loaded via ldsflda and not spilled. v.VerifyIL("Program.<Test>d__5.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" { // Code size 183 (0xb7) .maxstack 4 .locals init (int V_0, S1 V_1, System.Runtime.CompilerServices.TaskAwaiter<S1> V_2, S1 V_3, S1 V_4, S1 V_5, System.Exception V_6) IL_0000: ldarg.0 IL_0001: ldfld ""int Program.<Test>d__5.<>1__state"" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_0043 IL_000a: ldsfld ""S1 Program.s3"" IL_000f: call ""System.Threading.Tasks.Task<S1> Program.GetT<S1>(S1)"" IL_0014: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<S1> System.Threading.Tasks.Task<S1>.GetAwaiter()"" IL_0019: stloc.2 IL_001a: ldloca.s V_2 IL_001c: call ""bool System.Runtime.CompilerServices.TaskAwaiter<S1>.IsCompleted.get"" IL_0021: brtrue.s IL_005f IL_0023: ldarg.0 IL_0024: ldc.i4.0 IL_0025: dup IL_0026: stloc.0 IL_0027: stfld ""int Program.<Test>d__5.<>1__state"" IL_002c: ldarg.0 IL_002d: ldloc.2 IL_002e: stfld ""System.Runtime.CompilerServices.TaskAwaiter<S1> Program.<Test>d__5.<>u__1"" IL_0033: ldarg.0 IL_0034: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__5.<>t__builder"" IL_0039: ldloca.s V_2 IL_003b: ldarg.0 IL_003c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<S1>, Program.<Test>d__5>(ref System.Runtime.CompilerServices.TaskAwaiter<S1>, ref Program.<Test>d__5)"" IL_0041: leave.s IL_00b6 IL_0043: ldarg.0 IL_0044: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<S1> Program.<Test>d__5.<>u__1"" IL_0049: stloc.2 IL_004a: ldarg.0 IL_004b: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<S1> Program.<Test>d__5.<>u__1"" IL_0050: initobj ""System.Runtime.CompilerServices.TaskAwaiter<S1>"" IL_0056: ldarg.0 IL_0057: ldc.i4.m1 IL_0058: dup IL_0059: stloc.0 IL_005a: stfld ""int Program.<Test>d__5.<>1__state"" IL_005f: ldloca.s V_2 IL_0061: call ""S1 System.Runtime.CompilerServices.TaskAwaiter<S1>.GetResult()"" IL_0066: stloc.1 IL_0067: ldsfld ""S1 Program.s1"" IL_006c: stloc.3 IL_006d: ldloca.s V_3 IL_006f: ldsfld ""S1 Program.s2"" IL_0074: stloc.s V_4 IL_0076: ldloca.s V_4 IL_0078: ldloca.s V_1 IL_007a: ldsfld ""S1 Program.s4"" IL_007f: stloc.s V_5 IL_0081: ldloca.s V_5 IL_0083: call ""void S1.M1(in S1, in S1, in S1)"" IL_0088: leave.s IL_00a3 } catch System.Exception { IL_008a: stloc.s V_6 IL_008c: ldarg.0 IL_008d: ldc.i4.s -2 IL_008f: stfld ""int Program.<Test>d__5.<>1__state"" IL_0094: ldarg.0 IL_0095: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__5.<>t__builder"" IL_009a: ldloc.s V_6 IL_009c: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" IL_00a1: leave.s IL_00b6 } IL_00a3: ldarg.0 IL_00a4: ldc.i4.s -2 IL_00a6: stfld ""int Program.<Test>d__5.<>1__state"" IL_00ab: ldarg.0 IL_00ac: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<Test>d__5.<>t__builder"" IL_00b1: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00b6: ret } "); } [ConditionalTheory(typeof(ClrOnly), Reason = "https://github.com/mono/mono/issues/10834"), CombinatorialData] public void InParamGenericReadonly([CombinatorialValues("in", "ref readonly")] string modifier) { var text = @" class Program { static void Main(string[] args) { var o = new D(); var s = new S1(); o.M1(s); // should not be mutated. System.Console.WriteLine(s.field); } } abstract class C<U> { public abstract void M1<T>(" + modifier + @" T arg) where T : U, I1; } class D: C<S1> { public override void M1<T>(" + modifier + @" T arg) { arg.M3(); } } public struct S1: I1 { public int field; public void M3() { field = 42; } } interface I1 { void M3(); } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Passes, expectedOutput: @"0"); comp.VerifyIL($"D.M1<T>({modifier} T)", @" { // Code size 21 (0x15) .maxstack 1 .locals init (T V_0) IL_0000: ldarg.1 IL_0001: ldobj ""T"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: constrained. ""T"" IL_000f: callvirt ""void I1.M3()"" IL_0014: ret }"); } [ConditionalTheory(typeof(ClrOnly), Reason = "https://github.com/mono/mono/issues/10834"), CombinatorialData] public void InParamGenericReadonlyROstruct([CombinatorialValues("in", "ref readonly")] string modifier) { var text = @" class Program { static void Main(string[] args) { var o = new D(); var s = new S1(); o.M1(s); } } abstract class C<U> { public abstract void M1<T>(" + modifier + @" T arg) where T : U, I1; } class D: C<S1> { public override void M1<T>(" + modifier + @" T arg) { arg.M3(); } } public readonly struct S1: I1 { public void M3() { } } interface I1 { void M3(); } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Passes, expectedOutput: @""); comp.VerifyIL($"D.M1<T>({modifier} T)", @" { // Code size 21 (0x15) .maxstack 1 .locals init (T V_0) IL_0000: ldarg.1 IL_0001: ldobj ""T"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: constrained. ""T"" IL_000f: callvirt ""void I1.M3()"" IL_0014: ret }"); } [Fact] public void RefReadOnlyOptionalParameters() { CompileAndVerify(@" using System; class Program { static void Print(in int p = 5) { Console.Write(p); } static void Main() { Print(); Console.Write(""-""); Print(9); } }", expectedOutput: "5-9"); } [WorkItem(23338, "https://github.com/dotnet/roslyn/issues/23338")] [Theory, CombinatorialData] public void InParamsNullable([CombinatorialValues("in", "ref readonly")] string modifier) { var text = @" class Program { static void Main(string[] args) { S1 s = new S1(); s.val = 42; S1? ns = s; Test1(in ns); Test2(ref ns); } static void Test1(" + modifier + @" S1? arg) { // cannot not mutate System.Console.Write(arg.GetValueOrDefault()); // should not mutate arg.ToString(); // cannot not mutate System.Console.Write(arg.GetValueOrDefault()); } static void Test2(ref S1? arg) { // cannot not mutate System.Console.Write(arg.GetValueOrDefault()); // can mutate arg.ToString(); // cannot not mutate System.Console.Write(arg.GetValueOrDefault()); } } struct S1 { public int val; public override string ToString() { var result = val.ToString(); val = 0; return result; } } "; var comp = CompileAndVerify(text, parseOptions: TestOptions.Regular, verify: Verification.Passes, expectedOutput: @"4242420"); comp.VerifyIL($"Program.Test1({modifier} S1?)", @" { // Code size 54 (0x36) .maxstack 1 .locals init (S1? V_0) IL_0000: ldarg.0 IL_0001: call ""S1 S1?.GetValueOrDefault()"" IL_0006: box ""S1"" IL_000b: call ""void System.Console.Write(object)"" IL_0010: ldarg.0 IL_0011: ldobj ""S1?"" IL_0016: stloc.0 IL_0017: ldloca.s V_0 IL_0019: constrained. ""S1?"" IL_001f: callvirt ""string object.ToString()"" IL_0024: pop IL_0025: ldarg.0 IL_0026: call ""S1 S1?.GetValueOrDefault()"" IL_002b: box ""S1"" IL_0030: call ""void System.Console.Write(object)"" IL_0035: ret }"); comp.VerifyIL("Program.Test2(ref S1?)", @" { // Code size 46 (0x2e) .maxstack 1 IL_0000: ldarg.0 IL_0001: call ""S1 S1?.GetValueOrDefault()"" IL_0006: box ""S1"" IL_000b: call ""void System.Console.Write(object)"" IL_0010: ldarg.0 IL_0011: constrained. ""S1?"" IL_0017: callvirt ""string object.ToString()"" IL_001c: pop IL_001d: ldarg.0 IL_001e: call ""S1 S1?.GetValueOrDefault()"" IL_0023: box ""S1"" IL_0028: call ""void System.Console.Write(object)"" IL_002d: ret }"); } [Fact] [WorkItem(530136, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=530136")] public void OperatorsWithInParametersFromMetadata_Binary() { var reference = CreateCompilation(@" public class Test { public int Value { get; set; } public static int operator +(in Test a, in Test b) { return a.Value + b.Value; } }"); var code = @" class Program { static void Main(string[] args) { var a = new Test { Value = 3 }; var b = new Test { Value = 6 }; System.Console.WriteLine(a + b); } }"; CompileAndVerify(code, references: new[] { reference.ToMetadataReference() }, expectedOutput: "9"); CompileAndVerify(code, references: new[] { reference.EmitToImageReference() }, expectedOutput: "9"); } [Fact] [WorkItem(530136, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=530136")] public void OperatorsWithInParametersFromMetadata_Binary_Right() { var reference = CreateCompilation(@" public class Test { public int Value { get; set; } public static int operator +(Test a, in Test b) { return a.Value + b.Value; } }"); var code = @" class Program { static void Main(string[] args) { var a = new Test { Value = 3 }; var b = new Test { Value = 6 }; System.Console.WriteLine(a + b); } }"; CompileAndVerify(code, references: new[] { reference.ToMetadataReference() }, expectedOutput: "9"); CompileAndVerify(code, references: new[] { reference.EmitToImageReference() }, expectedOutput: "9"); } [Fact] [WorkItem(530136, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=530136")] public void OperatorsWithInParametersFromMetadata_Binary_Left() { var reference = CreateCompilation(@" public class Test { public int Value { get; set; } public static int operator +(in Test a, Test b) { return a.Value + b.Value; } }"); var code = @" class Program { static void Main(string[] args) { var a = new Test { Value = 3 }; var b = new Test { Value = 6 }; System.Console.WriteLine(a + b); } }"; CompileAndVerify(code, references: new[] { reference.ToMetadataReference() }, expectedOutput: "9"); CompileAndVerify(code, references: new[] { reference.EmitToImageReference() }, expectedOutput: "9"); } [Fact] [WorkItem(530136, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=530136")] public void OperatorsWithInParametersFromMetadata_Unary() { var reference = CreateCompilation(@" public class Test { public bool Value { get; set; } public static bool operator !(in Test a) { return !a.Value; } }"); var code = @" class Program { static void Main(string[] args) { var a = new Test { Value = true }; System.Console.WriteLine(!a); } }"; CompileAndVerify(code, references: new[] { reference.ToMetadataReference() }, expectedOutput: "False"); CompileAndVerify(code, references: new[] { reference.EmitToImageReference() }, expectedOutput: "False"); } [Fact] [WorkItem(530136, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=530136")] public void OperatorsWithInParametersFromMetadata_Conversion() { var reference = CreateCompilation(@" public class Test { public bool Value { get; set; } public static explicit operator int(in Test a) { return a.Value ? 3 : 5; } }"); var code = @" class Program { static void Main(string[] args) { var a = new Test { Value = true }; System.Console.WriteLine((int)a); } }"; CompileAndVerify(code, references: new[] { reference.ToMetadataReference() }, expectedOutput: "3"); CompileAndVerify(code, references: new[] { reference.EmitToImageReference() }, expectedOutput: "3"); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_Method_Optional_NoArgs() { var code = @" class Program { static void Test(in int value = 5) { System.Console.WriteLine(value); } static void Main(string[] args) { /*<bind>*/Test()/*<bind>*/; } }"; CompileAndVerify(code, expectedOutput: "5").VerifyIL("Program.Main", @" { // Code size 10 (0xa) .maxstack 1 .locals init (int V_0) IL_0000: ldc.i4.5 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: call ""void Program.Test(in int)"" IL_0009: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.Test([in System.Int32 value = 5])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test()') Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: value) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Test()') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsImplicit) (Syntax: 'Test()') 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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_Method_Optional_OneArg() { var code = @" class Program { static void Test(in int value = 5) { System.Console.WriteLine(value); } static void Main(string[] args) { /*<bind>*/Test(10)/*<bind>*/; } }"; CompileAndVerify(code, expectedOutput: "10").VerifyIL("Program.Main", @" { // Code size 11 (0xb) .maxstack 1 .locals init (int V_0) IL_0000: ldc.i4.s 10 IL_0002: stloc.0 IL_0003: ldloca.s V_0 IL_0005: call ""void Program.Test(in int)"" IL_000a: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.Test([in System.Int32 value = 5])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test(10)') Instance Receiver: null Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '10') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') 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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_Method_Optional_Optional_NoArgs() { var code = @" class Program { static void Test(in int value1 = 1, in int value2 = 5) { System.Console.WriteLine($""({value1}, {value2})""); } static void Main(string[] args) { /*<bind>*/Test()/*<bind>*/; } }"; CompileAndVerify(code, expectedOutput: "(1, 5)").VerifyIL("Program.Main", @" { // Code size 14 (0xe) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.5 IL_0005: stloc.1 IL_0006: ldloca.s V_1 IL_0008: call ""void Program.Test(in int, in int)"" IL_000d: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.Test([in System.Int32 value1 = 1], [in System.Int32 value2 = 5])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test()') Instance Receiver: null Arguments(2): IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: value1) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Test()') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'Test()') 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.DefaultValue, Matching Parameter: value2) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Test()') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsImplicit) (Syntax: 'Test()') 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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_Method_Optional_Optional_OneArg() { var code = @" class Program { static void Test(in int value1 = 1, in int value2 = 5) { System.Console.WriteLine($""({value1}, {value2})""); } static void Main(string[] args) { /*<bind>*/Test(2)/*<bind>*/; } }"; CompileAndVerify(code, expectedOutput: "(2, 5)").VerifyIL("Program.Main", @" { // Code size 14 (0xe) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: ldc.i4.2 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.5 IL_0005: stloc.1 IL_0006: ldloca.s V_1 IL_0008: call ""void Program.Test(in int, in int)"" IL_000d: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.Test([in System.Int32 value1 = 1], [in System.Int32 value2 = 5])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test(2)') Instance Receiver: null Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value1) (OperationKind.Argument, Type: null) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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.DefaultValue, Matching Parameter: value2) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Test(2)') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsImplicit) (Syntax: 'Test(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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_Method_Optional_Optional_TwoArgs() { var code = @" class Program { static void Test(in int value1 = 1, in int value2 = 5) { System.Console.WriteLine($""({value1}, {value2})""); } static void Main(string[] args) { /*<bind>*/Test(3, 10)/*<bind>*/; } }"; CompileAndVerify(code, expectedOutput: "(3, 10)").VerifyIL("Program.Main", @" { // Code size 15 (0xf) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.s 10 IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: call ""void Program.Test(in int, in int)"" IL_000e: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.Test([in System.Int32 value1 = 1], [in System.Int32 value2 = 5])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test(3, 10)') Instance Receiver: null Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value1) (OperationKind.Argument, Type: null) (Syntax: '3') ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value2) (OperationKind.Argument, Type: null) (Syntax: '10') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') 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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_Method_Required_Optional_OneArg() { var code = @" class Program { static void Test(in int value1, in int value2 = 5) { System.Console.WriteLine($""({value1}, {value2})""); } static void Main(string[] args) { /*<bind>*/Test(1)/*<bind>*/; } }"; CompileAndVerify(code, expectedOutput: "(1, 5)").VerifyIL("Program.Main", @" { // Code size 14 (0xe) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.5 IL_0005: stloc.1 IL_0006: ldloca.s V_1 IL_0008: call ""void Program.Test(in int, in int)"" IL_000d: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.Test(in System.Int32 value1, [in System.Int32 value2 = 5])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test(1)') Instance Receiver: null Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value1) (OperationKind.Argument, Type: null) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: value2) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Test(1)') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsImplicit) (Syntax: 'Test(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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_Method_Required_Optional_TwoArgs() { var code = @" class Program { static void Test(in int value1, in int value2 = 5) { System.Console.WriteLine($""({value1}, {value2})""); } static void Main(string[] args) { /*<bind>*/Test(2, 10)/*<bind>*/; } }"; CompileAndVerify(code, expectedOutput: "(2, 10)").VerifyIL("Program.Main", @" { // Code size 15 (0xf) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: ldc.i4.2 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: ldc.i4.s 10 IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: call ""void Program.Test(in int, in int)"" IL_000e: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.Test(in System.Int32 value1, [in System.Int32 value2 = 5])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Test(2, 10)') Instance Receiver: null Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value1) (OperationKind.Argument, Type: null) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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: value2) (OperationKind.Argument, Type: null) (Syntax: '10') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 10) (Syntax: '10') 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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_CompoundAssignment_Optional_Optional_OneArg() { var code = @" class Program { public int this[in int p1 = 1, in int p2 = 2] { get { System.Console.WriteLine($""get p1={p1} p2={p2}""); return 0; } set { System.Console.WriteLine($""set p1={p1} p2={p2} to {value}""); } } static void Main(string[] args) { var obj = new Program(); /*<bind>*/obj[3]/*<bind>*/ += 10; } }"; CompileAndVerify(code, expectedOutput: @" get p1=3 p2=2 set p1=3 p2=2 to 10 ").VerifyIL("Program.Main", @" { // Code size 39 (0x27) .maxstack 6 .locals init (Program V_0, int V_1, int V_2, int V_3, int V_4) IL_0000: newobj ""Program..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.3 IL_0008: stloc.1 IL_0009: ldloca.s V_1 IL_000b: ldc.i4.2 IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: ldc.i4.3 IL_0011: stloc.3 IL_0012: ldloca.s V_3 IL_0014: ldc.i4.2 IL_0015: stloc.s V_4 IL_0017: ldloca.s V_4 IL_0019: callvirt ""int Program.this[in int, in int].get"" IL_001e: ldc.i4.s 10 IL_0020: add IL_0021: callvirt ""void Program.this[in int, in int].set"" IL_0026: ret }"); VerifyOperationTreeAndDiagnosticsForTest<ElementAccessExpressionSyntax>(code, @" IPropertyReferenceOperation: System.Int32 Program.this[[in System.Int32 p1 = 1], [in System.Int32 p2 = 2]] { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'obj[3]') Instance Receiver: ILocalReferenceOperation: obj (OperationKind.LocalReference, Type: Program) (Syntax: 'obj') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: p1) (OperationKind.Argument, Type: null) (Syntax: '3') ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: p2) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'obj[3]') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: 'obj[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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_CompoundAssignment_Optional_Optional_TwoArgs() { var code = @" class Program { public int this[in int p1 = 1, in int p2 = 2] { get { System.Console.WriteLine($""get p1={p1} p2={p2}""); return 0; } set { System.Console.WriteLine($""set p1={p1} p2={p2} to {value}""); } } static void Main(string[] args) { var obj = new Program(); /*<bind>*/obj[4, 5]/*<bind>*/ += 11; } }"; CompileAndVerify(code, expectedOutput: @" get p1=4 p2=5 set p1=4 p2=5 to 11 ").VerifyIL("Program.Main", @" { // Code size 39 (0x27) .maxstack 6 .locals init (Program V_0, int V_1, int V_2, int V_3, int V_4) IL_0000: newobj ""Program..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.4 IL_0008: stloc.1 IL_0009: ldloca.s V_1 IL_000b: ldc.i4.5 IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: ldc.i4.4 IL_0011: stloc.3 IL_0012: ldloca.s V_3 IL_0014: ldc.i4.5 IL_0015: stloc.s V_4 IL_0017: ldloca.s V_4 IL_0019: callvirt ""int Program.this[in int, in int].get"" IL_001e: ldc.i4.s 11 IL_0020: add IL_0021: callvirt ""void Program.this[in int, in int].set"" IL_0026: ret }"); VerifyOperationTreeAndDiagnosticsForTest<ElementAccessExpressionSyntax>(code, @" IPropertyReferenceOperation: System.Int32 Program.this[[in System.Int32 p1 = 1], [in System.Int32 p2 = 2]] { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'obj[4, 5]') Instance Receiver: ILocalReferenceOperation: obj (OperationKind.LocalReference, Type: Program) (Syntax: 'obj') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: p1) (OperationKind.Argument, Type: null) (Syntax: '4') ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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: p2) (OperationKind.Argument, Type: null) (Syntax: '5') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') 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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_CompoundAssignment_Required_Optional_OneArg() { var code = @" class Program { public int this[in int p1, in int p2 = 2] { get { System.Console.WriteLine($""get p1={p1} p2={p2}""); return 0; } set { System.Console.WriteLine($""set p1={p1} p2={p2} to {value}""); } } static void Main(string[] args) { var obj = new Program(); /*<bind>*/obj[3]/*<bind>*/ += 10; } }"; CompileAndVerify(code, expectedOutput: @" get p1=3 p2=2 set p1=3 p2=2 to 10 ").VerifyIL("Program.Main", @" { // Code size 39 (0x27) .maxstack 6 .locals init (Program V_0, int V_1, int V_2, int V_3, int V_4) IL_0000: newobj ""Program..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.3 IL_0008: stloc.1 IL_0009: ldloca.s V_1 IL_000b: ldc.i4.2 IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: ldc.i4.3 IL_0011: stloc.3 IL_0012: ldloca.s V_3 IL_0014: ldc.i4.2 IL_0015: stloc.s V_4 IL_0017: ldloca.s V_4 IL_0019: callvirt ""int Program.this[in int, in int].get"" IL_001e: ldc.i4.s 10 IL_0020: add IL_0021: callvirt ""void Program.this[in int, in int].set"" IL_0026: ret }"); VerifyOperationTreeAndDiagnosticsForTest<ElementAccessExpressionSyntax>(code, @" IPropertyReferenceOperation: System.Int32 Program.this[in System.Int32 p1, [in System.Int32 p2 = 2]] { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'obj[3]') Instance Receiver: ILocalReferenceOperation: obj (OperationKind.LocalReference, Type: Program) (Syntax: 'obj') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: p1) (OperationKind.Argument, Type: null) (Syntax: '3') ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: p2) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'obj[3]') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: 'obj[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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void OptionalInParameters_CompoundAssignment_Required_Optional_TwoArgs() { var code = @" class Program { public int this[in int p1, in int p2 = 2] { get { System.Console.WriteLine($""get p1={p1} p2={p2}""); return 0; } set { System.Console.WriteLine($""set p1={p1} p2={p2} to {value}""); } } static void Main(string[] args) { var obj = new Program(); /*<bind>*/obj[4, 5]/*<bind>*/ += 11; } }"; CompileAndVerify(code, expectedOutput: @" get p1=4 p2=5 set p1=4 p2=5 to 11 ").VerifyIL("Program.Main", @" { // Code size 39 (0x27) .maxstack 6 .locals init (Program V_0, int V_1, int V_2, int V_3, int V_4) IL_0000: newobj ""Program..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.4 IL_0008: stloc.1 IL_0009: ldloca.s V_1 IL_000b: ldc.i4.5 IL_000c: stloc.2 IL_000d: ldloca.s V_2 IL_000f: ldloc.0 IL_0010: ldc.i4.4 IL_0011: stloc.3 IL_0012: ldloca.s V_3 IL_0014: ldc.i4.5 IL_0015: stloc.s V_4 IL_0017: ldloca.s V_4 IL_0019: callvirt ""int Program.this[in int, in int].get"" IL_001e: ldc.i4.s 11 IL_0020: add IL_0021: callvirt ""void Program.this[in int, in int].set"" IL_0026: ret }"); VerifyOperationTreeAndDiagnosticsForTest<ElementAccessExpressionSyntax>(code, @" IPropertyReferenceOperation: System.Int32 Program.this[in System.Int32 p1, [in System.Int32 p2 = 2]] { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'obj[4, 5]') Instance Receiver: ILocalReferenceOperation: obj (OperationKind.LocalReference, Type: Program) (Syntax: 'obj') Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: p1) (OperationKind.Argument, Type: null) (Syntax: '4') ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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: p2) (OperationKind.Argument, Type: null) (Syntax: '5') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') 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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void Issue23691_PassingInOptionalArgumentsByRef_OneArg() { var code = @" class Program { static void Main() { /*<bind>*/A(1)/*<bind>*/; } static void A(in double x = 1, in string y = ""test"") => System.Console.WriteLine(y); static void B(in float x, in float y, in float z = 3.0f) => System.Console.WriteLine(x * y * z); }"; CompileAndVerify(code, expectedOutput: "test").VerifyIL("Program.Main", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (double V_0, string V_1) IL_0000: ldc.r8 1 IL_0009: stloc.0 IL_000a: ldloca.s V_0 IL_000c: ldstr ""test"" IL_0011: stloc.1 IL_0012: ldloca.s V_1 IL_0014: call ""void Program.A(in double, in string)"" IL_0019: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.A([in System.Double x = 1], [in System.String y = ""test""])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'A(1)') Instance Receiver: null Arguments(2): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '1') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Double, Constant: 1, IsImplicit) (Syntax: '1') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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) IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: y) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'A(1)') ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""test"", IsImplicit) (Syntax: 'A(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)", DiagnosticDescription.None); } [Fact] [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.ReadOnlyReferences)] [WorkItem(23691, "https://github.com/dotnet/roslyn/issues/23691")] public void Issue23691_PassingInOptionalArgumentsByRef_TwoArgs() { var code = @" class Program { static void Main() { /*<bind>*/B(1, 2)/*<bind>*/; } static void A(in double x = 1, in string y = ""test"") => System.Console.WriteLine(y); static void B(in float x, in float y, in float z = 3.0f) => System.Console.WriteLine(x * y * z); }"; CompileAndVerify(code, expectedOutput: "6").VerifyIL("Program.Main", @" { // Code size 30 (0x1e) .maxstack 3 .locals init (float V_0, float V_1, float V_2) IL_0000: ldc.r4 1 IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: ldc.r4 2 IL_000d: stloc.1 IL_000e: ldloca.s V_1 IL_0010: ldc.r4 3 IL_0015: stloc.2 IL_0016: ldloca.s V_2 IL_0018: call ""void Program.B(in float, in float, in float)"" IL_001d: ret }"); VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(code, @" IInvocationOperation (void Program.B(in System.Single x, in System.Single y, [in System.Single z = 3])) (OperationKind.Invocation, Type: System.Void) (Syntax: 'B(1, 2)') Instance Receiver: null Arguments(3): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '1') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Single, Constant: 1, IsImplicit) (Syntax: '1') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: y) (OperationKind.Argument, Type: null) (Syntax: '2') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Single, Constant: 2, IsImplicit) (Syntax: '2') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: True, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, 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.DefaultValue, Matching Parameter: z) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'B(1, 2)') ILiteralOperation (OperationKind.Literal, Type: System.Single, Constant: 3, IsImplicit) (Syntax: 'B(1, 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)", DiagnosticDescription.None); } [WorkItem(23692, "https://github.com/dotnet/roslyn/issues/23692")] [Fact] public void ThisToInParam() { var code = @" using System; static class Ex { public static void InMethod(in X arg) => Console.Write(arg); } class X { public void M() { // pass `this` by in-parameter. Ex.InMethod(this); } } class Program { static void Main() { var x = new X(); // baseline Ex.InMethod(x); x.M(); } } "; var compilation = CreateCompilationWithMscorlib40AndSystemCore(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "XX"); verifier.VerifyIL("X.M()", @" { // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 IL_0002: call ""void Ex.InMethod(in X)"" IL_0007: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_Local() { var code = @" using System; public class Test { static void Main(string[] args) { int x = 50; Moo(x + 0, () => x = 60); } static void Moo(in int y, Action change) { Console.Write(y); change(); Console.Write(y); } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "5050"); verifier.VerifyIL("Test.Main(string[])", @" { // Code size 41 (0x29) .maxstack 3 .locals init (Test.<>c__DisplayClass0_0 V_0, //CS$<>8__locals0 int V_1) IL_0000: newobj ""Test.<>c__DisplayClass0_0..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.s 50 IL_0009: stfld ""int Test.<>c__DisplayClass0_0.x"" IL_000e: ldloc.0 IL_000f: ldfld ""int Test.<>c__DisplayClass0_0.x"" IL_0014: stloc.1 IL_0015: ldloca.s V_1 IL_0017: ldloc.0 IL_0018: ldftn ""void Test.<>c__DisplayClass0_0.<Main>b__0()"" IL_001e: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_0023: call ""void Test.Moo(in int, System.Action)"" IL_0028: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_ArrayAccess() { var code = @" using System; public class Test { static void Main(string[] args) { int[] x = new int[] { 50 }; Moo(x[0] + 0, () => x[0] = 60); } static void Moo(in int y, Action change) { Console.Write(y); change(); Console.Write(y); } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "5050"); verifier.VerifyIL("Test.Main(string[])", @" { // Code size 52 (0x34) .maxstack 5 .locals init (Test.<>c__DisplayClass0_0 V_0, //CS$<>8__locals0 int V_1) IL_0000: newobj ""Test.<>c__DisplayClass0_0..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.1 IL_0008: newarr ""int"" IL_000d: dup IL_000e: ldc.i4.0 IL_000f: ldc.i4.s 50 IL_0011: stelem.i4 IL_0012: stfld ""int[] Test.<>c__DisplayClass0_0.x"" IL_0017: ldloc.0 IL_0018: ldfld ""int[] Test.<>c__DisplayClass0_0.x"" IL_001d: ldc.i4.0 IL_001e: ldelem.i4 IL_001f: stloc.1 IL_0020: ldloca.s V_1 IL_0022: ldloc.0 IL_0023: ldftn ""void Test.<>c__DisplayClass0_0.<Main>b__0()"" IL_0029: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_002e: call ""void Test.Moo(in int, System.Action)"" IL_0033: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_ArrayAccessReordered() { var code = @" using System; public class Test { static void Main(string[] args) { int[] x = new int[] { 50 }; Moo(change: () => x[0] = 60, y: x[0] + 0); } static void Moo(in int y, Action change) { Console.Write(y); change(); Console.Write(y); } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "5050"); verifier.VerifyIL("Test.Main(string[])", @" { // Code size 52 (0x34) .maxstack 5 .locals init (Test.<>c__DisplayClass0_0 V_0, //CS$<>8__locals0 int V_1) IL_0000: newobj ""Test.<>c__DisplayClass0_0..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.1 IL_0008: newarr ""int"" IL_000d: dup IL_000e: ldc.i4.0 IL_000f: ldc.i4.s 50 IL_0011: stelem.i4 IL_0012: stfld ""int[] Test.<>c__DisplayClass0_0.x"" IL_0017: ldloc.0 IL_0018: ldfld ""int[] Test.<>c__DisplayClass0_0.x"" IL_001d: ldc.i4.0 IL_001e: ldelem.i4 IL_001f: stloc.1 IL_0020: ldloca.s V_1 IL_0022: ldloc.0 IL_0023: ldftn ""void Test.<>c__DisplayClass0_0.<Main>b__0()"" IL_0029: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_002e: call ""void Test.Moo(in int, System.Action)"" IL_0033: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_FieldAcces() { var code = @" using System; public class Test { struct S1 { public int x; } static S1 s = new S1 { x = 555 }; static void Main(string[] args) { Moo(s.x + 0); } static void Moo(in int y) { Console.Write(y); s.x = 123; Console.Write(y); } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "555555"); verifier.VerifyIL("Test.Main(string[])", @" { // Code size 19 (0x13) .maxstack 1 .locals init (int V_0) IL_0000: ldsflda ""Test.S1 Test.s"" IL_0005: ldfld ""int Test.S1.x"" IL_000a: stloc.0 IL_000b: ldloca.s V_0 IL_000d: call ""void Test.Moo(in int)"" IL_0012: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_RoFieldAcces() { var code = @" using System; public class Test { struct S1 { public int x; } readonly S1 s; static void Main(string[] args) { var x = new Test(); } public Test() { Test1(s.x + 0, ref s.x); } private void Test1(in int y, ref int f) { Console.Write(y); f = 1; Console.Write(y); Test2(s.x + 0, ref f); } private void Test2(in int y, ref int f) { Console.Write(y); f = 2; Console.Write(y); } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); // PEVerify: Cannot change initonly field outside its .ctor. var verifier = CompileAndVerify(compilation, expectedOutput: "0011", verify: Verification.FailsPEVerify); verifier.VerifyIL("Test..ctor()", @" { // Code size 38 (0x26) .maxstack 3 .locals init (int V_0) IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: ldarg.0 IL_0007: ldarg.0 IL_0008: ldflda ""Test.S1 Test.s"" IL_000d: ldfld ""int Test.S1.x"" IL_0012: stloc.0 IL_0013: ldloca.s V_0 IL_0015: ldarg.0 IL_0016: ldflda ""Test.S1 Test.s"" IL_001b: ldflda ""int Test.S1.x"" IL_0020: call ""void Test.Test1(in int, ref int)"" IL_0025: ret } "); verifier.VerifyIL("Test.Test1(in int, ref int)", @" { // Code size 39 (0x27) .maxstack 3 .locals init (int V_0) IL_0000: ldarg.1 IL_0001: ldind.i4 IL_0002: call ""void System.Console.Write(int)"" IL_0007: ldarg.2 IL_0008: ldc.i4.1 IL_0009: stind.i4 IL_000a: ldarg.1 IL_000b: ldind.i4 IL_000c: call ""void System.Console.Write(int)"" IL_0011: ldarg.0 IL_0012: ldarg.0 IL_0013: ldflda ""Test.S1 Test.s"" IL_0018: ldfld ""int Test.S1.x"" IL_001d: stloc.0 IL_001e: ldloca.s V_0 IL_0020: ldarg.2 IL_0021: call ""void Test.Test2(in int, ref int)"" IL_0026: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_ThisAcces() { var code = @" using System; public class Test { static void Main(string[] args) { var x = new Test(); } public Test() { Test3(null ?? this); } private void Test3(in Test y) { } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: ""); verifier.VerifyIL("Test..ctor()", @" { // Code size 17 (0x11) .maxstack 2 .locals init (Test V_0) IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: ldarg.0 IL_0007: ldarg.0 IL_0008: stloc.0 IL_0009: ldloca.s V_0 IL_000b: call ""void Test.Test3(in Test)"" IL_0010: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_RefMethod() { var code = @" using System; public class Test { static void Main(string[] args) { var x = new Test(); } private string s = ""hi""; private ref string M1() { return ref s; } public Test() { Test3(null ?? M1()); } private void Test3(in string y) { Console.Write(y); s = ""bye""; Console.Write(y); } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "hihi"); verifier.VerifyIL("Test..ctor()", @" { // Code size 34 (0x22) .maxstack 2 .locals init (string V_0) IL_0000: ldarg.0 IL_0001: ldstr ""hi"" IL_0006: stfld ""string Test.s"" IL_000b: ldarg.0 IL_000c: call ""object..ctor()"" IL_0011: ldarg.0 IL_0012: ldarg.0 IL_0013: call ""ref string Test.M1()"" IL_0018: ldind.ref IL_0019: stloc.0 IL_001a: ldloca.s V_0 IL_001c: call ""void Test.Test3(in string)"" IL_0021: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_InOperator() { var code = @" using System; public class Test { static void Main(string[] args) { var x = new Test(); } private static string s = ""hi""; public Test() { var dummy = (null ?? s) + this; } public static string operator +(in string y, in Test t) { Console.Write(y); s = ""bye""; Console.Write(y); return y; } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "hihi"); verifier.VerifyIL("Test..ctor()", @" { // Code size 23 (0x17) .maxstack 2 .locals init (string V_0) IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: ldsfld ""string Test.s"" IL_000b: stloc.0 IL_000c: ldloca.s V_0 IL_000e: ldarga.s V_0 IL_0010: call ""string Test.op_Addition(in string, in Test)"" IL_0015: pop IL_0016: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_InOperatorLifted() { var code = @" using System; public struct Test { static void Main(string[] args) { var x = new Test(); x.Test1(); } private Action change; public Test(Action change) { this.change = change; } public void Test1() { int s = 1; Test? t = new Test(() => s = 42); var dummy = s + t; } public static int operator +(in int y, in Test t) { Console.Write(y); t.change(); Console.Write(y); return 88; } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "11"); verifier.VerifyIL("Test.Test1()", @" { // Code size 71 (0x47) .maxstack 2 .locals init (Test.<>c__DisplayClass3_0 V_0, //CS$<>8__locals0 int V_1, Test? V_2, Test V_3) IL_0000: newobj ""Test.<>c__DisplayClass3_0..ctor()"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.1 IL_0008: stfld ""int Test.<>c__DisplayClass3_0.s"" IL_000d: ldloc.0 IL_000e: ldftn ""void Test.<>c__DisplayClass3_0.<Test1>b__0()"" IL_0014: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_0019: newobj ""Test..ctor(System.Action)"" IL_001e: newobj ""Test?..ctor(Test)"" IL_0023: ldloc.0 IL_0024: ldfld ""int Test.<>c__DisplayClass3_0.s"" IL_0029: stloc.1 IL_002a: stloc.2 IL_002b: ldloca.s V_2 IL_002d: call ""bool Test?.HasValue.get"" IL_0032: brfalse.s IL_0046 IL_0034: ldloca.s V_1 IL_0036: ldloca.s V_2 IL_0038: call ""Test Test?.GetValueOrDefault()"" IL_003d: stloc.3 IL_003e: ldloca.s V_3 IL_0040: call ""int Test.op_Addition(in int, in Test)"" IL_0045: pop IL_0046: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_InOperatorUnary() { var code = @" using System; public class Test { static void Main(string[] args) { Test1(); } private static Test s = new Test(); public static void Test1() { var dummy = +(null ?? s); } public static Test operator +(in Test y) { Console.Write(y); s = default; Console.Write(y); return y; } } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "TestTest"); verifier.VerifyIL("Test.Test1()", @" { // Code size 15 (0xf) .maxstack 1 .locals init (Test V_0) IL_0000: ldsfld ""Test Test.s"" IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: call ""Test Test.op_UnaryPlus(in Test)"" IL_000d: pop IL_000e: ret } "); } [WorkItem(24806, "https://github.com/dotnet/roslyn/issues/24806")] [Fact] public void OptimizedRValueToIn_InConversion() { var code = @" using System; public class Test { static void Main(string[] args) { Test1(); } private static Test s = new Test(); public static void Test1() { int dummyI = (null ?? s); s = new Derived(); long dummyL = (null ?? s); } public static implicit operator int(in Test y) { Console.Write(y.ToString()); s = default; Console.Write(y.ToString()); return 1; } } class Derived : Test { } "; var compilation = CreateCompilation(code, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(compilation, expectedOutput: "TestTestDerivedDerived"); verifier.VerifyIL("Test.Test1()", @" { // Code size 39 (0x27) .maxstack 1 .locals init (Test V_0) IL_0000: ldsfld ""Test Test.s"" IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: call ""int Test.op_Implicit(in Test)"" IL_000d: pop IL_000e: newobj ""Derived..ctor()"" IL_0013: stsfld ""Test Test.s"" IL_0018: ldsfld ""Test Test.s"" IL_001d: stloc.0 IL_001e: ldloca.s V_0 IL_0020: call ""int Test.op_Implicit(in Test)"" IL_0025: pop IL_0026: ret } "); } [Theory, CombinatorialData, WorkItem(66135, "https://github.com/dotnet/roslyn/issues/66135")] public void ConstrainedCallOnInParameter([CombinatorialValues("in", "ref readonly")] string modifier) { var source = @" using System; using System.Collections; using System.Collections.Generic; using System.Linq; public class C { public static void Main() { S value = new(); ref readonly S valueRef = ref value; Console.Write(valueRef); M(in valueRef); Console.Write(valueRef); } public static void M(" + modifier + @" S value) { foreach (var x in value) { } } } public struct S : IEnumerable<int> { int a; public readonly override string ToString() => a.ToString(); private IEnumerator<int> GetEnumerator() => Enumerable.Range(0, ++a).GetEnumerator(); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }"; var verifier = CompileAndVerify(source, expectedOutput: "00"); // Note: we use a temp instead of directly doing a constrained call on `in` parameter verifier.VerifyIL("C.M", """ { // Code size 51 (0x33) .maxstack 1 .locals init (System.Collections.Generic.IEnumerator<int> V_0, S V_1) IL_0000: ldarg.0 IL_0001: ldobj "S" IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: constrained. "S" IL_000f: callvirt "System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()" IL_0014: stloc.0 .try { IL_0015: br.s IL_001e IL_0017: ldloc.0 IL_0018: callvirt "int System.Collections.Generic.IEnumerator<int>.Current.get" IL_001d: pop IL_001e: ldloc.0 IL_001f: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0024: brtrue.s IL_0017 IL_0026: leave.s IL_0032 } finally { IL_0028: ldloc.0 IL_0029: brfalse.s IL_0031 IL_002b: ldloc.0 IL_002c: callvirt "void System.IDisposable.Dispose()" IL_0031: endfinally } IL_0032: ret } """); } [Theory, CombinatorialData, WorkItem(66135, "https://github.com/dotnet/roslyn/issues/66135")] public void ConstrainedCallOnInParameter_ConstrainedGenericReceiver([CombinatorialValues("in", "ref readonly")] string modifier) { var source = @" using System; using System.Collections; using System.Collections.Generic; using System.Linq; public class C { public static void Main() { S value = new(); ref readonly S valueRef = ref value; Console.Write(valueRef); M(in valueRef); Console.Write(valueRef); } public static void M<T>(" + modifier + @" T value) where T : struct, IEnumerable<int> { foreach (var x in value) { } } } public struct S : IEnumerable<int> { int a; public readonly override string ToString() => a.ToString(); private IEnumerator<int> GetEnumerator() => Enumerable.Range(0, ++a).GetEnumerator(); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }"; var verifier = CompileAndVerify(source, expectedOutput: "00"); verifier.VerifyIL($"C.M<T>({modifier} T)", """ { // Code size 51 (0x33) .maxstack 1 .locals init (System.Collections.Generic.IEnumerator<int> V_0, T V_1) IL_0000: ldarg.0 IL_0001: ldobj "T" IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: constrained. "T" IL_000f: callvirt "System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()" IL_0014: stloc.0 .try { IL_0015: br.s IL_001e IL_0017: ldloc.0 IL_0018: callvirt "int System.Collections.Generic.IEnumerator<int>.Current.get" IL_001d: pop IL_001e: ldloc.0 IL_001f: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0024: brtrue.s IL_0017 IL_0026: leave.s IL_0032 } finally { IL_0028: ldloc.0 IL_0029: brfalse.s IL_0031 IL_002b: ldloc.0 IL_002c: callvirt "void System.IDisposable.Dispose()" IL_0031: endfinally } IL_0032: ret } """); } [Fact, WorkItem(66135, "https://github.com/dotnet/roslyn/issues/66135")] public void ConstrainedCallOnReadonlyField() { var source = @" using System; using System.Collections; using System.Collections.Generic; using System.Linq; public class C { public static void Main() { S s = new(); var d = new D(s); d.M(); d.M(); } } public class D { readonly S field; public D(S s) { field = s; } public void M() { foreach (var x in field) { } System.Console.Write(field.ToString()); } } public struct S : IEnumerable<int> { int a; public readonly override string ToString() => a.ToString(); private IEnumerator<int> GetEnumerator() => Enumerable.Range(0, ++a).GetEnumerator(); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }"; var verifier = CompileAndVerify(source, expectedOutput: "00", verify: Verification.FailsPEVerify); // Note: we use a temp instead of directly doing a constrained call on readonly field verifier.VerifyIL("D.M", """ { // Code size 73 (0x49) .maxstack 1 .locals init (System.Collections.Generic.IEnumerator<int> V_0, S V_1) IL_0000: ldarg.0 IL_0001: ldfld "S D.field" IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: constrained. "S" IL_000f: callvirt "System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()" IL_0014: stloc.0 .try { IL_0015: br.s IL_001e IL_0017: ldloc.0 IL_0018: callvirt "int System.Collections.Generic.IEnumerator<int>.Current.get" IL_001d: pop IL_001e: ldloc.0 IL_001f: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0024: brtrue.s IL_0017 IL_0026: leave.s IL_0032 } finally { IL_0028: ldloc.0 IL_0029: brfalse.s IL_0031 IL_002b: ldloc.0 IL_002c: callvirt "void System.IDisposable.Dispose()" IL_0031: endfinally } IL_0032: ldarg.0 IL_0033: ldflda "S D.field" IL_0038: constrained. "S" IL_003e: callvirt "string object.ToString()" IL_0043: call "void System.Console.Write(string)" IL_0048: ret } """); } [Fact, WorkItem(66135, "https://github.com/dotnet/roslyn/issues/66135")] public void ConstrainedCallOnField() { var source = @" using System; using System.Collections; using System.Collections.Generic; using System.Linq; public class C { public static void Main() { S s = new(); var d = new D(s); d.M(); d.M(); } } public class D { S field; public D(S s) { field = s; } public void M() { foreach (var x in field) { } System.Console.Write(field.ToString()); } } public struct S : IEnumerable<int> { int a; public readonly override string ToString() => a.ToString(); private IEnumerator<int> GetEnumerator() => Enumerable.Range(0, ++a).GetEnumerator(); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }"; var verifier = CompileAndVerify(source, expectedOutput: "12"); // Note: we do a constrained call directly on the field verifier.VerifyIL("D.M", """ { // Code size 70 (0x46) .maxstack 1 .locals init (System.Collections.Generic.IEnumerator<int> V_0) IL_0000: ldarg.0 IL_0001: ldflda "S D.field" IL_0006: constrained. "S" IL_000c: callvirt "System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()" IL_0011: stloc.0 .try { IL_0012: br.s IL_001b IL_0014: ldloc.0 IL_0015: callvirt "int System.Collections.Generic.IEnumerator<int>.Current.get" IL_001a: pop IL_001b: ldloc.0 IL_001c: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0021: brtrue.s IL_0014 IL_0023: leave.s IL_002f } finally { IL_0025: ldloc.0 IL_0026: brfalse.s IL_002e IL_0028: ldloc.0 IL_0029: callvirt "void System.IDisposable.Dispose()" IL_002e: endfinally } IL_002f: ldarg.0 IL_0030: ldflda "S D.field" IL_0035: constrained. "S" IL_003b: callvirt "string object.ToString()" IL_0040: call "void System.Console.Write(string)" IL_0045: ret } """); } [Theory, CombinatorialData, WorkItem(66135, "https://github.com/dotnet/roslyn/issues/66135")] public void InvokeStructToStringOverrideOnInParameter([CombinatorialValues("in", "ref readonly")] string modifier) { var text = @" using System; class C { public static void Main() { S1 s = new S1(); Console.Write(M(in s)); Console.Write(M(in s)); } static string M(" + modifier + @" S1 s) { return s.ToString(); } } struct S1 { int i; public override string ToString() => (i++).ToString(); } "; var comp = CompileAndVerify(text, expectedOutput: "00"); comp.VerifyIL("C.M", """ { // Code size 21 (0x15) .maxstack 1 .locals init (S1 V_0) IL_0000: ldarg.0 IL_0001: ldobj "S1" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: constrained. "S1" IL_000f: callvirt "string object.ToString()" IL_0014: ret } """); } [Theory, CombinatorialData, WorkItem(66135, "https://github.com/dotnet/roslyn/issues/66135")] public void InvokeAddedStructToStringOverrideOnInParameter([CombinatorialValues("in", "ref readonly")] string modifier) { var libOrig_cs = """ public struct S { int i; public void Report() { throw null; } } """; var libOrig = CreateCompilation(libOrig_cs, assemblyName: "lib"); var libChanged_cs = """ public struct S { int i; public override string ToString() => (i++).ToString(); public void Report() { System.Console.Write("RAN "); } } """; var libChanged = CreateCompilation(libChanged_cs, assemblyName: "lib"); var libUser_cs = $$""" public class C { public static string M({{modifier}} S s) { return s.ToString(); } } """; var libUser = CreateCompilation(libUser_cs, references: new[] { libOrig.EmitToImageReference() }); CompileAndVerify(libUser).VerifyIL("C.M", """ { // Code size 21 (0x15) .maxstack 1 .locals init (S V_0) IL_0000: ldarg.0 IL_0001: ldobj "S" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: constrained. "S" IL_000f: callvirt "string object.ToString()" IL_0014: ret } """); var src = """ using System; S s = new S(); s.Report(); Console.Write(C.M(in s)); Console.Write(C.M(in s)); """; var comp = CreateCompilation(src, references: new[] { libChanged.EmitToImageReference(), libUser.EmitToImageReference() }); CompileAndVerify(comp, expectedOutput: "RAN 00"); } [Fact, WorkItem(66135, "https://github.com/dotnet/roslyn/issues/66135")] public void InvokeAddedStructToStringOverrideOnReadonlyField() { var libOrig_cs = """ public struct S { int i; public void Report() { throw null; } } """; var libOrig = CreateCompilation(libOrig_cs, assemblyName: "lib"); var libChanged_cs = """ public struct S { int i; public override string ToString() => (i++).ToString(); public void Report() { System.Console.Write($"Report{i} "); } } """; var libChanged = CreateCompilation(libChanged_cs, assemblyName: "lib"); var libUser_cs = """ public class C { readonly S field; public C(int i) { field.ToString(); } public string M() { return field.ToString(); } public void Report() { field.Report(); } } """; var libUser = CreateCompilation(libUser_cs, references: new[] { libOrig.EmitToImageReference() }); var verifier = CompileAndVerify(libUser); verifier.VerifyIL("C.M", """ { // Code size 21 (0x15) .maxstack 1 .locals init (S V_0) IL_0000: ldarg.0 IL_0001: ldfld "S C.field" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: constrained. "S" IL_000f: callvirt "string object.ToString()" IL_0014: ret } """); verifier.VerifyIL("C..ctor(int)", """ { // Code size 25 (0x19) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "object..ctor()" IL_0006: ldarg.0 IL_0007: ldflda "S C.field" IL_000c: constrained. "S" IL_0012: callvirt "string object.ToString()" IL_0017: pop IL_0018: ret } """); var src = """ C c = new C(42); c.Report(); System.Console.Write(c.M()); System.Console.Write(c.M()); """; var comp = CreateCompilation(src, references: new[] { libChanged.EmitToImageReference(), libUser.EmitToImageReference() }); CompileAndVerify(comp, expectedOutput: "Report1 11"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73746")] public void TestExtensionMethodInModifierWithEnumType() { var source = """ int i = 0; i.M1(); E e = (E)0; e.M2(); enum E; static class Extensions { public static void M1(this in int e) { System.Console.WriteLine("int"); } public static void M2(this in E e) { System.Console.WriteLine("enum"); } } """; var comp = CompileAndVerify(source, symbolValidator: validate, expectedOutput: """ int enum """); static void validate(ModuleSymbol m) { AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("Extensions.M1").Parameters.Single().RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("Extensions.M2").Parameters.Single().RefKind); } } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73746")] public void TestExtensionBlockInModifierWithEnumType() { var source = """ int i = 0; i.M1(); _ = i.P1; i.P1 = 0; _ = i + new object(); E e = (E)0; e.M1(); _ = e.P1; e.P1 = 0; _ = e + new object(); enum E; static class IntExtensions { extension(in int i) { public void M1() { System.Console.WriteLine("method_int"); } public int P1 { get { System.Console.WriteLine("property_get_int"); return 0; } set { System.Console.WriteLine("property_set_int"); } } public static int operator +(in int a, object b) { System.Console.WriteLine("operator_int"); return 0; } } } static class EnumExtensions { extension(in E e) { public void M1() { System.Console.WriteLine("method_enum"); } public int P1 { get { System.Console.WriteLine("property_get_enum"); return 0; } set { System.Console.WriteLine("property_set_enum"); } } public static int operator +(in E a, object b) { System.Console.WriteLine("operator_enum"); return 0; } } } """; var comp = CompileAndVerify(source, symbolValidator: validate, expectedOutput: """ method_int property_get_int property_set_int operator_int method_enum property_get_enum property_set_enum operator_enum """); static void validate(ModuleSymbol m) { AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetTypeMember("IntExtensions").GetTypeMembers().Single().ExtensionParameter.RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("IntExtensions.M1").Parameters.Single().RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("IntExtensions.get_P1").Parameters.Single().RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("IntExtensions.set_P1").Parameters.First().RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("IntExtensions.op_Addition").Parameters.First().RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetTypeMember("EnumExtensions").GetTypeMembers().Single().ExtensionParameter.RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("EnumExtensions.M1").Parameters.Single().RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("EnumExtensions.get_P1").Parameters.Single().RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("EnumExtensions.set_P1").Parameters.First().RefKind); AssertEx.Equal( RefKind.In, m.GlobalNamespace.GetMember<MethodSymbol>("EnumExtensions.op_Addition").Parameters.First().RefKind); } } } }