/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs
3 928 строк
107 KB
Jan Jones
Generate branchless IL for `(b ? 1 : 0)` (#67191)
17 апр 2023, 17:54
Не верифицирован
17 апр 2023, 17:54
009407d
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class CodeGenConditionalOperatorTests : CSharpTestBase { [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] public void Branchless_Compare() { var source = """ using static System.Console; WriteLine(C.Compare(1, 2)); WriteLine(C.Compare(3, 3)); WriteLine(C.Compare(5, 4)); class C { public static int Compare(int x, int y) { int tmp1 = (x > y) ? 1 : 0; int tmp2 = (x < y) ? 1 : 0; return tmp1 - tmp2; } } """; var expectedOutput = """ -1 0 1 """; var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput); verifier.VerifyDiagnostics(); verifier.VerifyMethodBody("C.Compare", """ { // Code size 12 (0xc) .maxstack 3 .locals init (int V_0) //tmp2 // sequence point: int tmp1 = (x > y) ? 1 : 0; IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: cgt // sequence point: int tmp2 = (x < y) ? 1 : 0; IL_0004: ldarg.0 IL_0005: ldarg.1 IL_0006: clt IL_0008: stloc.0 // sequence point: return tmp1 - tmp2; IL_0009: ldloc.0 IL_000a: sub IL_000b: ret } """); verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe.WithDebugPlusMode(true), expectedOutput: expectedOutput); verifier.VerifyDiagnostics(); verifier.VerifyMethodBody("C.Compare", """ { // Code size 14 (0xe) .maxstack 2 .locals init (int V_0, //tmp1 int V_1) //tmp2 // sequence point: int tmp1 = (x > y) ? 1 : 0; IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: cgt IL_0004: stloc.0 // sequence point: int tmp2 = (x < y) ? 1 : 0; IL_0005: ldarg.0 IL_0006: ldarg.1 IL_0007: clt IL_0009: stloc.1 // sequence point: return tmp1 - tmp2; IL_000a: ldloc.0 IL_000b: ldloc.1 IL_000c: sub IL_000d: ret } """); verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput); verifier.VerifyDiagnostics(); verifier.VerifyMethodBody("C.Compare", """ { // Code size 27 (0x1b) .maxstack 2 .locals init (int V_0, //tmp1 int V_1, //tmp2 int V_2) // sequence point: { IL_0000: nop // sequence point: int tmp1 = (x > y) ? 1 : 0; IL_0001: ldarg.0 IL_0002: ldarg.1 IL_0003: bgt.s IL_0008 IL_0005: ldc.i4.0 IL_0006: br.s IL_0009 IL_0008: ldc.i4.1 IL_0009: stloc.0 // sequence point: int tmp2 = (x < y) ? 1 : 0; IL_000a: ldarg.0 IL_000b: ldarg.1 IL_000c: blt.s IL_0011 IL_000e: ldc.i4.0 IL_000f: br.s IL_0012 IL_0011: ldc.i4.1 IL_0012: stloc.1 // sequence point: return tmp1 - tmp2; IL_0013: ldloc.0 IL_0014: ldloc.1 IL_0015: sub IL_0016: stloc.2 IL_0017: br.s IL_0019 // sequence point: } IL_0019: ldloc.2 IL_001a: ret } """); } [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] public void Branchless_Operations() { var source = """ using static System.Console; class C { static void Main() => M(1, 0, null!, true); static void M(int x, int y, object a, bool b) { Write(x == y ? 1 : 0); Write(x == y ? 0 : 1); Write(x < y ? 1 : 0); Write(x < y ? 0 : 1); Write(x > y ? 1 : 0); Write(x > y ? 0 : 1); Write(a is string ? 0 : 1); Write(a is not string ? 0 : 1); Write(b ? 0 : 1); Write(!b ? 0 : 1); Write(x <= y ? true : false); Write(x <= y ? false : true); Write(x != y ? (byte)1 : (byte)0); Write(x != y ? 1 : (sbyte)0); Write(x != y ? (short)1 : (short)0); Write(x != y ? (ushort)1 : 0); Write(x != y ? 1U : 0); Write(x != y ? 1L : 0); Write(x != y ? 1UL : 0); Write(x != y ? (nint)1 : 0); Write(x != y ? 1 : (nuint)0); Write(x < y ? (char)0 : (char)1); Write(x < y ? '\x1' : '\x0'); Write(true ? 1 : 0); Write(false ? 0 : 1); const bool B = true; Write(B ? 1 : 0); } } """; var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "0101101001FalseTrue111111111" + (char)1 + (char)0 + "111"); verifier.VerifyDiagnostics(); verifier.VerifyMethodBody("C.M", """ { // Code size 288 (0x120) .maxstack 2 // sequence point: Write(x == y ? 1 : 0); IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: ceq IL_0004: call "void System.Console.Write(int)" // sequence point: Write(x == y ? 0 : 1); IL_0009: ldarg.0 IL_000a: ldarg.1 IL_000b: ceq IL_000d: ldc.i4.0 IL_000e: ceq IL_0010: call "void System.Console.Write(int)" // sequence point: Write(x < y ? 1 : 0); IL_0015: ldarg.0 IL_0016: ldarg.1 IL_0017: clt IL_0019: call "void System.Console.Write(int)" // sequence point: Write(x < y ? 0 : 1); IL_001e: ldarg.0 IL_001f: ldarg.1 IL_0020: clt IL_0022: ldc.i4.0 IL_0023: ceq IL_0025: call "void System.Console.Write(int)" // sequence point: Write(x > y ? 1 : 0); IL_002a: ldarg.0 IL_002b: ldarg.1 IL_002c: cgt IL_002e: call "void System.Console.Write(int)" // sequence point: Write(x > y ? 0 : 1); IL_0033: ldarg.0 IL_0034: ldarg.1 IL_0035: cgt IL_0037: ldc.i4.0 IL_0038: ceq IL_003a: call "void System.Console.Write(int)" // sequence point: Write(a is string ? 0 : 1); IL_003f: ldarg.2 IL_0040: isinst "string" IL_0045: ldnull IL_0046: ceq IL_0048: call "void System.Console.Write(int)" // sequence point: Write(a is not string ? 0 : 1); IL_004d: ldarg.2 IL_004e: isinst "string" IL_0053: ldnull IL_0054: cgt.un IL_0056: call "void System.Console.Write(int)" // sequence point: Write(b ? 0 : 1); IL_005b: ldarg.3 IL_005c: ldc.i4.0 IL_005d: ceq IL_005f: call "void System.Console.Write(int)" // sequence point: Write(!b ? 0 : 1); IL_0064: ldarg.3 IL_0065: ldc.i4.0 IL_0066: cgt.un IL_0068: call "void System.Console.Write(int)" // sequence point: Write(x <= y ? true : false); IL_006d: ldarg.0 IL_006e: ldarg.1 IL_006f: cgt IL_0071: ldc.i4.0 IL_0072: ceq IL_0074: call "void System.Console.Write(bool)" // sequence point: Write(x <= y ? false : true); IL_0079: ldarg.0 IL_007a: ldarg.1 IL_007b: cgt IL_007d: call "void System.Console.Write(bool)" // sequence point: Write(x != y ? (byte)1 : (byte)0); IL_0082: ldarg.0 IL_0083: ldarg.1 IL_0084: ceq IL_0086: ldc.i4.0 IL_0087: ceq IL_0089: conv.u1 IL_008a: call "void System.Console.Write(int)" // sequence point: Write(x != y ? 1 : (sbyte)0); IL_008f: ldarg.0 IL_0090: ldarg.1 IL_0091: ceq IL_0093: ldc.i4.0 IL_0094: ceq IL_0096: call "void System.Console.Write(int)" // sequence point: Write(x != y ? (short)1 : (short)0); IL_009b: ldarg.0 IL_009c: ldarg.1 IL_009d: ceq IL_009f: ldc.i4.0 IL_00a0: ceq IL_00a2: conv.i2 IL_00a3: call "void System.Console.Write(int)" // sequence point: Write(x != y ? (ushort)1 : 0); IL_00a8: ldarg.0 IL_00a9: ldarg.1 IL_00aa: ceq IL_00ac: ldc.i4.0 IL_00ad: ceq IL_00af: call "void System.Console.Write(int)" // sequence point: Write(x != y ? 1U : 0); IL_00b4: ldarg.0 IL_00b5: ldarg.1 IL_00b6: ceq IL_00b8: ldc.i4.0 IL_00b9: ceq IL_00bb: call "void System.Console.Write(uint)" // sequence point: Write(x != y ? 1L : 0); IL_00c0: ldarg.0 IL_00c1: ldarg.1 IL_00c2: ceq IL_00c4: ldc.i4.0 IL_00c5: ceq IL_00c7: conv.i8 IL_00c8: call "void System.Console.Write(long)" // sequence point: Write(x != y ? 1UL : 0); IL_00cd: ldarg.0 IL_00ce: ldarg.1 IL_00cf: ceq IL_00d1: ldc.i4.0 IL_00d2: ceq IL_00d4: conv.i8 IL_00d5: call "void System.Console.Write(ulong)" // sequence point: Write(x != y ? (nint)1 : 0); IL_00da: ldarg.0 IL_00db: ldarg.1 IL_00dc: ceq IL_00de: ldc.i4.0 IL_00df: ceq IL_00e1: conv.i IL_00e2: conv.i8 IL_00e3: call "void System.Console.Write(long)" // sequence point: Write(x != y ? 1 : (nuint)0); IL_00e8: ldarg.0 IL_00e9: ldarg.1 IL_00ea: ceq IL_00ec: ldc.i4.0 IL_00ed: ceq IL_00ef: conv.i IL_00f0: conv.u8 IL_00f1: call "void System.Console.Write(ulong)" // sequence point: Write(x < y ? (char)0 : (char)1); IL_00f6: ldarg.0 IL_00f7: ldarg.1 IL_00f8: clt IL_00fa: ldc.i4.0 IL_00fb: ceq IL_00fd: conv.u2 IL_00fe: call "void System.Console.Write(char)" // sequence point: Write(x < y ? '\x1' : '\x0'); IL_0103: ldarg.0 IL_0104: ldarg.1 IL_0105: clt IL_0107: conv.u2 IL_0108: call "void System.Console.Write(char)" // sequence point: Write(true ? 1 : 0); IL_010d: ldc.i4.1 IL_010e: call "void System.Console.Write(int)" // sequence point: Write(false ? 0 : 1); IL_0113: ldc.i4.1 IL_0114: call "void System.Console.Write(int)" // sequence point: Write(B ? 1 : 0); IL_0119: ldc.i4.1 IL_011a: call "void System.Console.Write(int)" // sequence point: } IL_011f: ret } """); } [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] public void Branchless_Negations() { var source = """ using static System.Console; class C { static void Main() => M(1, 0, true); static void M(int x, int y, bool b) { Write(!(x < y) ? 0 : 1); Write(!!(x < y) ? 0 : 1); Write(!!!(x < y) ? 0 : 1); Write(!(x == y) ? 0 : 1); Write(!b ? 0 : 1); Write(!!b ? 0 : 1); Write(!!!b ? 0 : 1); Write(!false ? 0 : 1); Write(!!false ? 0 : 1); Write(!!!false ? 0 : 1); } } """; var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "0100101010"); verifier.VerifyDiagnostics(); verifier.VerifyMethodBody("C.M", """ { // Code size 85 (0x55) .maxstack 2 // sequence point: Write(!(x < y) ? 0 : 1); IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: clt IL_0004: call "void System.Console.Write(int)" // sequence point: Write(!!(x < y) ? 0 : 1); IL_0009: ldarg.0 IL_000a: ldarg.1 IL_000b: clt IL_000d: ldc.i4.0 IL_000e: ceq IL_0010: call "void System.Console.Write(int)" // sequence point: Write(!!!(x < y) ? 0 : 1); IL_0015: ldarg.0 IL_0016: ldarg.1 IL_0017: clt IL_0019: call "void System.Console.Write(int)" // sequence point: Write(!(x == y) ? 0 : 1); IL_001e: ldarg.0 IL_001f: ldarg.1 IL_0020: ceq IL_0022: call "void System.Console.Write(int)" // sequence point: Write(!b ? 0 : 1); IL_0027: ldarg.2 IL_0028: ldc.i4.0 IL_0029: cgt.un IL_002b: call "void System.Console.Write(int)" // sequence point: Write(!!b ? 0 : 1); IL_0030: ldarg.2 IL_0031: ldc.i4.0 IL_0032: ceq IL_0034: call "void System.Console.Write(int)" // sequence point: Write(!!!b ? 0 : 1); IL_0039: ldarg.2 IL_003a: ldc.i4.0 IL_003b: cgt.un IL_003d: call "void System.Console.Write(int)" // sequence point: Write(!false ? 0 : 1); IL_0042: ldc.i4.0 IL_0043: call "void System.Console.Write(int)" // sequence point: Write(!!false ? 0 : 1); IL_0048: ldc.i4.1 IL_0049: call "void System.Console.Write(int)" // sequence point: Write(!!!false ? 0 : 1); IL_004e: ldc.i4.0 IL_004f: call "void System.Console.Write(int)" // sequence point: } IL_0054: ret } """); } [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] public void Branchless_NonBinaryArms() { var source = """ using static System.Console; class C { static void Main() => M(1, 0); static void M(int x, int y) { Write(x == y ? 1 : 1); Write(x != y ? 0 : 0); Write(x <= y ? 0 : 2); Write(x >= y ? 2 : 1); Write(x < y ? 0 : -1); Write(x < y ? 0d : 1d); Write(x < y ? 0f : 1f); Write(x < y ? 0m : 1m); Write(x < y ? '\x0' : 'a'); } } """; var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: "1022-1111a"); verifier.VerifyDiagnostics(); verifier.VerifyMethodBody("C.M", """ { // Code size 151 (0x97) .maxstack 2 // sequence point: Write(x == y ? 1 : 1); IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: beq.s IL_0007 IL_0004: ldc.i4.1 IL_0005: br.s IL_0008 IL_0007: ldc.i4.1 IL_0008: call "void System.Console.Write(int)" // sequence point: Write(x != y ? 0 : 0); IL_000d: ldarg.0 IL_000e: ldarg.1 IL_000f: bne.un.s IL_0014 IL_0011: ldc.i4.0 IL_0012: br.s IL_0015 IL_0014: ldc.i4.0 IL_0015: call "void System.Console.Write(int)" // sequence point: Write(x <= y ? 0 : 2); IL_001a: ldarg.0 IL_001b: ldarg.1 IL_001c: ble.s IL_0021 IL_001e: ldc.i4.2 IL_001f: br.s IL_0022 IL_0021: ldc.i4.0 IL_0022: call "void System.Console.Write(int)" // sequence point: Write(x >= y ? 2 : 1); IL_0027: ldarg.0 IL_0028: ldarg.1 IL_0029: bge.s IL_002e IL_002b: ldc.i4.1 IL_002c: br.s IL_002f IL_002e: ldc.i4.2 IL_002f: call "void System.Console.Write(int)" // sequence point: Write(x < y ? 0 : -1); IL_0034: ldarg.0 IL_0035: ldarg.1 IL_0036: blt.s IL_003b IL_0038: ldc.i4.m1 IL_0039: br.s IL_003c IL_003b: ldc.i4.0 IL_003c: call "void System.Console.Write(int)" // sequence point: Write(x < y ? 0d : 1d); IL_0041: ldarg.0 IL_0042: ldarg.1 IL_0043: blt.s IL_0050 IL_0045: ldc.r8 1 IL_004e: br.s IL_0059 IL_0050: ldc.r8 0 IL_0059: call "void System.Console.Write(double)" // sequence point: Write(x < y ? 0f : 1f); IL_005e: ldarg.0 IL_005f: ldarg.1 IL_0060: blt.s IL_0069 IL_0062: ldc.r4 1 IL_0067: br.s IL_006e IL_0069: ldc.r4 0 IL_006e: call "void System.Console.Write(float)" // sequence point: Write(x < y ? 0m : 1m); IL_0073: ldarg.0 IL_0074: ldarg.1 IL_0075: blt.s IL_007e IL_0077: ldsfld "decimal decimal.One" IL_007c: br.s IL_0083 IL_007e: ldsfld "decimal decimal.Zero" IL_0083: call "void System.Console.Write(decimal)" // sequence point: Write(x < y ? '\x0' : 'a'); IL_0088: ldarg.0 IL_0089: ldarg.1 IL_008a: blt.s IL_0090 IL_008c: ldc.i4.s 97 IL_008e: br.s IL_0091 IL_0090: ldc.i4.0 IL_0091: call "void System.Console.Write(char)" // sequence point: } IL_0096: ret } """); } [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] public void Branchless_NonBinaryCondition() { // public static class C { public static bool M() => -1; } var source1 = """ .class public auto ansi abstract sealed beforefieldinit C extends System.Object { .method public hidebysig static bool M () cil managed { .maxstack 8 ldc.i4.m1 ret } } """; var source2 = """ System.Console.WriteLine(D.M1()); System.Console.WriteLine(D.M2()); class D { public static int M1() => C.M() ? 1 : 0; public static int M2() => C.M() ? 0 : 1; } """; var comp = CreateCompilationWithIL(source2, source1, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: """ 1 0 """); verifier.VerifyDiagnostics(); verifier.VerifyMethodBody("D.M1", """ { // Code size 9 (0x9) .maxstack 2 // sequence point: C.M() ? 1 : 0 IL_0000: call "bool C.M()" IL_0005: ldc.i4.0 IL_0006: cgt.un IL_0008: ret } """); verifier.VerifyMethodBody("D.M2", """ { // Code size 9 (0x9) .maxstack 2 // sequence point: C.M() ? 0 : 1 IL_0000: call "bool C.M()" IL_0005: ldc.i4.0 IL_0006: ceq IL_0008: ret } """); } [Fact, WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void ConditionalDelegateInterfaceUnification1() { var src = @"using System; using System.Security; [assembly: SecurityTransparent()] interface I {} class A : I {} class C : I { static Func<I> Tester(bool a) { return a ? (Func<I>)(() => new A()) : () => new C(); } static void Main() { System.Console.Write(Tester(false)().GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "C"); verify.VerifyIL("C.Tester", @" { // Code size 73 (0x49) .maxstack 2 .locals init (System.Func<I> V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: brtrue.s IL_0025 IL_0004: ldsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0009: dup IL_000a: brtrue.s IL_0023 IL_000c: pop IL_000d: ldsfld ""C.<>c C.<>c.<>9"" IL_0012: ldftn ""I C.<>c.<Tester>b__0_1()"" IL_0018: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_001d: dup IL_001e: stsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0023: br.s IL_0044 IL_0025: ldsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_002a: dup IL_002b: brtrue.s IL_0044 IL_002d: pop IL_002e: ldsfld ""C.<>c C.<>c.<>9"" IL_0033: ldftn ""I C.<>c.<Tester>b__0_0()"" IL_0039: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_003e: dup IL_003f: stsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0044: stloc.0 IL_0045: br.s IL_0047 IL_0047: ldloc.0 IL_0048: ret } "); verify = CompileAndVerify(src, expectedOutput: "C"); verify.VerifyIL("C.Tester", @" { // Code size 67 (0x43) .maxstack 2 IL_0000: ldarg.0 IL_0001: brtrue.s IL_0023 IL_0003: ldsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0008: dup IL_0009: brtrue.s IL_0042 IL_000b: pop IL_000c: ldsfld ""C.<>c C.<>c.<>9"" IL_0011: ldftn ""I C.<>c.<Tester>b__0_1()"" IL_0017: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_001c: dup IL_001d: stsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0022: ret IL_0023: ldsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0028: dup IL_0029: brtrue.s IL_0042 IL_002b: pop IL_002c: ldsfld ""C.<>c C.<>c.<>9"" IL_0031: ldftn ""I C.<>c.<Tester>b__0_0()"" IL_0037: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_003c: dup IL_003d: stsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0042: ret } "); } [Fact, WorkItem(20266, "https://github.com/dotnet/roslyn/issues/20266")] public void ConditionalAccessInMethodGroupConversion() { var source = @" class C { void M(object o) { System.Func<int, string> filter = new C(o?.ToString()).Method; filter(0); } string Method(int x) { throw null; } C(string x) { } } "; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); CompileAndVerify(comp); } [Fact, WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void ConditionalDelegateInterfaceUnification2() { var src = @"using System; using System.Security; [assembly: SecurityTransparent()] interface I {} class A : I {} class B : I {} class C : I { static Func<I> Tester(int a) { return a > 0 ? a == 1 ? (Func<I>)(() => new A()) : () => new B() : (() => new C()); } static void Main() { System.Console.Write(Tester(1)().GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "A"); verify.VerifyIL("C.Tester", @" { // Code size 111 (0x6f) .maxstack 2 .locals init (System.Func<I> V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: bgt.s IL_0026 IL_0005: ldsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_000a: dup IL_000b: brtrue.s IL_0024 IL_000d: pop IL_000e: ldsfld ""C.<>c C.<>c.<>9"" IL_0013: ldftn ""I C.<>c.<Tester>b__0_2()"" IL_0019: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_001e: dup IL_001f: stsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_0024: br.s IL_006a IL_0026: ldarg.0 IL_0027: ldc.i4.1 IL_0028: beq.s IL_004b IL_002a: ldsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_002f: dup IL_0030: brtrue.s IL_0049 IL_0032: pop IL_0033: ldsfld ""C.<>c C.<>c.<>9"" IL_0038: ldftn ""I C.<>c.<Tester>b__0_1()"" IL_003e: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0043: dup IL_0044: stsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0049: br.s IL_006a IL_004b: ldsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0050: dup IL_0051: brtrue.s IL_006a IL_0053: pop IL_0054: ldsfld ""C.<>c C.<>c.<>9"" IL_0059: ldftn ""I C.<>c.<Tester>b__0_0()"" IL_005f: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0064: dup IL_0065: stsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_006a: stloc.0 IL_006b: br.s IL_006d IL_006d: ldloc.0 IL_006e: ret } "); verify = CompileAndVerify(src, expectedOutput: "A"); verify.VerifyIL("C.Tester", @" { // Code size 104 (0x68) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: bgt.s IL_0024 IL_0004: ldsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_0009: dup IL_000a: brtrue.s IL_0067 IL_000c: pop IL_000d: ldsfld ""C.<>c C.<>c.<>9"" IL_0012: ldftn ""I C.<>c.<Tester>b__0_2()"" IL_0018: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_001d: dup IL_001e: stsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_0023: ret IL_0024: ldarg.0 IL_0025: ldc.i4.1 IL_0026: beq.s IL_0048 IL_0028: ldsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_002d: dup IL_002e: brtrue.s IL_0067 IL_0030: pop IL_0031: ldsfld ""C.<>c C.<>c.<>9"" IL_0036: ldftn ""I C.<>c.<Tester>b__0_1()"" IL_003c: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0041: dup IL_0042: stsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0047: ret IL_0048: ldsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_004d: dup IL_004e: brtrue.s IL_0067 IL_0050: pop IL_0051: ldsfld ""C.<>c C.<>c.<>9"" IL_0056: ldftn ""I C.<>c.<Tester>b__0_0()"" IL_005c: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0061: dup IL_0062: stsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0067: ret } "); } [Fact, WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void ConditionalDelegateInterfaceUnification3() { var src = @"using System; using System.Security; [assembly: SecurityTransparent()] interface I {} class A : I {} class B : I {} class C : I { static Func<I> Tester(int a) { return a > 0 ? (() => new A()) : a == -1 ? (Func<I>)(() => new B()) : (() => new C()); } static void Main() { System.Console.Write(Tester(-1)().GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "B"); verify.VerifyIL("C.Tester", @" { // Code size 111 (0x6f) .maxstack 2 .locals init (System.Func<I> V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: bgt.s IL_004b IL_0005: ldarg.0 IL_0006: ldc.i4.m1 IL_0007: beq.s IL_002a IL_0009: ldsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_000e: dup IL_000f: brtrue.s IL_0028 IL_0011: pop IL_0012: ldsfld ""C.<>c C.<>c.<>9"" IL_0017: ldftn ""I C.<>c.<Tester>b__0_2()"" IL_001d: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0022: dup IL_0023: stsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_0028: br.s IL_0049 IL_002a: ldsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_002f: dup IL_0030: brtrue.s IL_0049 IL_0032: pop IL_0033: ldsfld ""C.<>c C.<>c.<>9"" IL_0038: ldftn ""I C.<>c.<Tester>b__0_1()"" IL_003e: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0043: dup IL_0044: stsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0049: br.s IL_006a IL_004b: ldsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0050: dup IL_0051: brtrue.s IL_006a IL_0053: pop IL_0054: ldsfld ""C.<>c C.<>c.<>9"" IL_0059: ldftn ""I C.<>c.<Tester>b__0_0()"" IL_005f: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0064: dup IL_0065: stsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_006a: stloc.0 IL_006b: br.s IL_006d IL_006d: ldloc.0 IL_006e: ret } "); verify = CompileAndVerify(src, expectedOutput: "B"); verify.VerifyIL("C.Tester", @" { // Code size 104 (0x68) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: bgt.s IL_0048 IL_0004: ldarg.0 IL_0005: ldc.i4.m1 IL_0006: beq.s IL_0028 IL_0008: ldsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_000d: dup IL_000e: brtrue.s IL_0067 IL_0010: pop IL_0011: ldsfld ""C.<>c C.<>c.<>9"" IL_0016: ldftn ""I C.<>c.<Tester>b__0_2()"" IL_001c: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0021: dup IL_0022: stsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_0027: ret IL_0028: ldsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_002d: dup IL_002e: brtrue.s IL_0067 IL_0030: pop IL_0031: ldsfld ""C.<>c C.<>c.<>9"" IL_0036: ldftn ""I C.<>c.<Tester>b__0_1()"" IL_003c: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0041: dup IL_0042: stsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0047: ret IL_0048: ldsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_004d: dup IL_004e: brtrue.s IL_0067 IL_0050: pop IL_0051: ldsfld ""C.<>c C.<>c.<>9"" IL_0056: ldftn ""I C.<>c.<Tester>b__0_0()"" IL_005c: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0061: dup IL_0062: stsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0067: ret }"); } [Fact, WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void ConditionalDelegateInterfaceUnification4() { var src = @"using System; using System.Security; [assembly: SecurityTransparent()] interface I {} class A : I {} class B : I {} class D : I {} class C : I { static Func<I> Tester(int a) { return a > 0 ? a == 1 ? (Func<I>)(() => new A()) : () => new B() : a == -1 ? () => new C() : (Func<I>)(() => new D()); } static void Main() { System.Console.Write(Tester(-2)().GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "D"); verify.VerifyIL("C.Tester", @" { // Code size 148 (0x94) .maxstack 2 .locals init (System.Func<I> V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: bgt.s IL_004b IL_0005: ldarg.0 IL_0006: ldc.i4.m1 IL_0007: beq.s IL_002a IL_0009: ldsfld ""System.Func<I> C.<>c.<>9__0_3"" IL_000e: dup IL_000f: brtrue.s IL_0028 IL_0011: pop IL_0012: ldsfld ""C.<>c C.<>c.<>9"" IL_0017: ldftn ""I C.<>c.<Tester>b__0_3()"" IL_001d: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0022: dup IL_0023: stsfld ""System.Func<I> C.<>c.<>9__0_3"" IL_0028: br.s IL_0049 IL_002a: ldsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_002f: dup IL_0030: brtrue.s IL_0049 IL_0032: pop IL_0033: ldsfld ""C.<>c C.<>c.<>9"" IL_0038: ldftn ""I C.<>c.<Tester>b__0_2()"" IL_003e: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0043: dup IL_0044: stsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_0049: br.s IL_008f IL_004b: ldarg.0 IL_004c: ldc.i4.1 IL_004d: beq.s IL_0070 IL_004f: ldsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0054: dup IL_0055: brtrue.s IL_006e IL_0057: pop IL_0058: ldsfld ""C.<>c C.<>c.<>9"" IL_005d: ldftn ""I C.<>c.<Tester>b__0_1()"" IL_0063: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0068: dup IL_0069: stsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_006e: br.s IL_008f IL_0070: ldsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0075: dup IL_0076: brtrue.s IL_008f IL_0078: pop IL_0079: ldsfld ""C.<>c C.<>c.<>9"" IL_007e: ldftn ""I C.<>c.<Tester>b__0_0()"" IL_0084: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0089: dup IL_008a: stsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_008f: stloc.0 IL_0090: br.s IL_0092 IL_0092: ldloc.0 IL_0093: ret } "); verify = CompileAndVerify(src, expectedOutput: "D"); verify.VerifyIL("C.Tester", @" { // Code size 140 (0x8c) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: bgt.s IL_0048 IL_0004: ldarg.0 IL_0005: ldc.i4.m1 IL_0006: beq.s IL_0028 IL_0008: ldsfld ""System.Func<I> C.<>c.<>9__0_3"" IL_000d: dup IL_000e: brtrue.s IL_008b IL_0010: pop IL_0011: ldsfld ""C.<>c C.<>c.<>9"" IL_0016: ldftn ""I C.<>c.<Tester>b__0_3()"" IL_001c: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0021: dup IL_0022: stsfld ""System.Func<I> C.<>c.<>9__0_3"" IL_0027: ret IL_0028: ldsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_002d: dup IL_002e: brtrue.s IL_008b IL_0030: pop IL_0031: ldsfld ""C.<>c C.<>c.<>9"" IL_0036: ldftn ""I C.<>c.<Tester>b__0_2()"" IL_003c: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0041: dup IL_0042: stsfld ""System.Func<I> C.<>c.<>9__0_2"" IL_0047: ret IL_0048: ldarg.0 IL_0049: ldc.i4.1 IL_004a: beq.s IL_006c IL_004c: ldsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_0051: dup IL_0052: brtrue.s IL_008b IL_0054: pop IL_0055: ldsfld ""C.<>c C.<>c.<>9"" IL_005a: ldftn ""I C.<>c.<Tester>b__0_1()"" IL_0060: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0065: dup IL_0066: stsfld ""System.Func<I> C.<>c.<>9__0_1"" IL_006b: ret IL_006c: ldsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_0071: dup IL_0072: brtrue.s IL_008b IL_0074: pop IL_0075: ldsfld ""C.<>c C.<>c.<>9"" IL_007a: ldftn ""I C.<>c.<Tester>b__0_0()"" IL_0080: newobj ""System.Func<I>..ctor(object, System.IntPtr)"" IL_0085: dup IL_0086: stsfld ""System.Func<I> C.<>c.<>9__0_0"" IL_008b: ret } "); } [Fact(), WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void NestedConditional1() { var src = @"using System.Security; [assembly: SecurityTransparent()] public interface I {} class A : I {} class B : I {} public class C : I { static I Tester(int x, int y) { return x == 0 ? (y == 0 ? (I)new A() : new B()) : new C(); } static void Main() { System.Console.Write(Tester(1, 0).GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "C"); verify.VerifyIL("C.Tester", @" { // Code size 37 (0x25) .maxstack 1 .locals init (I V_0, I V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: brfalse.s IL_000d IL_0004: newobj ""C..ctor()"" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: br.s IL_0020 IL_000d: ldarg.1 IL_000e: brfalse.s IL_0019 IL_0010: newobj ""B..ctor()"" IL_0015: stloc.0 IL_0016: ldloc.0 IL_0017: br.s IL_0020 IL_0019: newobj ""A..ctor()"" IL_001e: stloc.0 IL_001f: ldloc.0 IL_0020: stloc.1 IL_0021: br.s IL_0023 IL_0023: ldloc.1 IL_0024: ret }"); // Optimized verify = CompileAndVerify(src, expectedOutput: "C"); verify.VerifyIL("C.Tester", @" { // Code size 30 (0x1e) .maxstack 1 .locals init (I V_0) IL_0000: ldarg.0 IL_0001: brfalse.s IL_000b IL_0003: newobj ""C..ctor()"" IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: ret IL_000b: ldarg.1 IL_000c: brfalse.s IL_0016 IL_000e: newobj ""B..ctor()"" IL_0013: stloc.0 IL_0014: ldloc.0 IL_0015: ret IL_0016: newobj ""A..ctor()"" IL_001b: stloc.0 IL_001c: ldloc.0 IL_001d: ret }"); } [Fact(), WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void NestedConditional2() { var src = @"using System.Security; [assembly: SecurityTransparent()] public interface I {} class A : I {} class B : I {} public class C : I { static I Tester(int x) { return x > 0 ? new C() : (x < 0 ? (I)new A() : new B()); } static void Main() { System.Console.Write(Tester(0).GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "B"); verify.VerifyIL("C.Tester", @" { // Code size 39 (0x27) .maxstack 2 .locals init (I V_0, I V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: bgt.s IL_001b IL_0005: ldarg.0 IL_0006: ldc.i4.0 IL_0007: blt.s IL_0012 IL_0009: newobj ""B..ctor()"" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: br.s IL_0019 IL_0012: newobj ""A..ctor()"" IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: br.s IL_0022 IL_001b: newobj ""C..ctor()"" IL_0020: stloc.0 IL_0021: ldloc.0 IL_0022: stloc.1 IL_0023: br.s IL_0025 IL_0025: ldloc.1 IL_0026: ret }"); // Optimized verify = CompileAndVerify(src, expectedOutput: "B"); verify.VerifyIL("C.Tester", @" { // Code size 32 (0x20) .maxstack 2 .locals init (I V_0) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: bgt.s IL_0018 IL_0004: ldarg.0 IL_0005: ldc.i4.0 IL_0006: blt.s IL_0010 IL_0008: newobj ""B..ctor()"" IL_000d: stloc.0 IL_000e: ldloc.0 IL_000f: ret IL_0010: newobj ""A..ctor()"" IL_0015: stloc.0 IL_0016: ldloc.0 IL_0017: ret IL_0018: newobj ""C..ctor()"" IL_001d: stloc.0 IL_001e: ldloc.0 IL_001f: ret }"); } [Fact(), WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void NestedConditional3() { var src = @"using System.Security; [assembly: SecurityTransparent()] public interface I {} class A : I {} class B : I {} class D : I {} public class C : I { static I Tester(int x) { return x > 0 ? x == 1 ? (I)new A() : new B() : x == 0 ? (I)new C() : new D(); } static void Main() { System.Console.Write(Tester(1).GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "A"); verify.VerifyIL("C.Tester", @" { // Code size 51 (0x33) .maxstack 2 .locals init (I V_0, I V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: bgt.s IL_001a IL_0005: ldarg.0 IL_0006: brfalse.s IL_0011 IL_0008: newobj ""D..ctor()"" IL_000d: stloc.0 IL_000e: ldloc.0 IL_000f: br.s IL_0018 IL_0011: newobj ""C..ctor()"" IL_0016: stloc.0 IL_0017: ldloc.0 IL_0018: br.s IL_002e IL_001a: ldarg.0 IL_001b: ldc.i4.1 IL_001c: beq.s IL_0027 IL_001e: newobj ""B..ctor()"" IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: br.s IL_002e IL_0027: newobj ""A..ctor()"" IL_002c: stloc.0 IL_002d: ldloc.0 IL_002e: stloc.1 IL_002f: br.s IL_0031 IL_0031: ldloc.1 IL_0032: ret }"); // Optimized verify = CompileAndVerify(src, expectedOutput: "A"); verify.VerifyIL("C.Tester", @" { // Code size 43 (0x2b) .maxstack 2 .locals init (I V_0) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: bgt.s IL_0017 IL_0004: ldarg.0 IL_0005: brfalse.s IL_000f IL_0007: newobj ""D..ctor()"" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: ret IL_000f: newobj ""C..ctor()"" IL_0014: stloc.0 IL_0015: ldloc.0 IL_0016: ret IL_0017: ldarg.0 IL_0018: ldc.i4.1 IL_0019: beq.s IL_0023 IL_001b: newobj ""B..ctor()"" IL_0020: stloc.0 IL_0021: ldloc.0 IL_0022: ret IL_0023: newobj ""A..ctor()"" IL_0028: stloc.0 IL_0029: ldloc.0 IL_002a: ret }"); } [Fact(), WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void NestedConditional4() { var src = @"using System.Security; [assembly: SecurityTransparent()] public interface I {} class A : I {} class B : I {} class D : I {} public class C : I { static I Tester(int x) { return x > 0 ? x > 1 ? x > 2 ? (I)new A() : new B() : new C() : new D(); } static void Main() { System.Console.Write(Tester(0).GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "D"); verify.VerifyIL("C.Tester", @" { // Code size 52 (0x34) .maxstack 2 .locals init (I V_0, I V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: bgt.s IL_000e IL_0005: newobj ""D..ctor()"" IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: br.s IL_002f IL_000e: ldarg.0 IL_000f: ldc.i4.1 IL_0010: bgt.s IL_001b IL_0012: newobj ""C..ctor()"" IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: br.s IL_002f IL_001b: ldarg.0 IL_001c: ldc.i4.2 IL_001d: bgt.s IL_0028 IL_001f: newobj ""B..ctor()"" IL_0024: stloc.0 IL_0025: ldloc.0 IL_0026: br.s IL_002f IL_0028: newobj ""A..ctor()"" IL_002d: stloc.0 IL_002e: ldloc.0 IL_002f: stloc.1 IL_0030: br.s IL_0032 IL_0032: ldloc.1 IL_0033: ret }"); // Optimized verify = CompileAndVerify(src, expectedOutput: "D"); verify.VerifyIL("C.Tester", @" { // Code size 44 (0x2c) .maxstack 2 .locals init (I V_0) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: bgt.s IL_000c IL_0004: newobj ""D..ctor()"" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: ret IL_000c: ldarg.0 IL_000d: ldc.i4.1 IL_000e: bgt.s IL_0018 IL_0010: newobj ""C..ctor()"" IL_0015: stloc.0 IL_0016: ldloc.0 IL_0017: ret IL_0018: ldarg.0 IL_0019: ldc.i4.2 IL_001a: bgt.s IL_0024 IL_001c: newobj ""B..ctor()"" IL_0021: stloc.0 IL_0022: ldloc.0 IL_0023: ret IL_0024: newobj ""A..ctor()"" IL_0029: stloc.0 IL_002a: ldloc.0 IL_002b: ret }"); } [Fact] public void TestConditionalOperatorSimple() { var source = @" class C { static void Main() { bool b = true; System.Console.WriteLine(b ? 1 : 2); } }"; var comp = CompileAndVerify(source, expectedOutput: "1"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 13 (0xd) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: brtrue.s IL_0006 IL_0003: ldc.i4.2 IL_0004: br.s IL_0007 IL_0006: ldc.i4.1 IL_0007: call ""void System.Console.WriteLine(int)"" IL_000c: ret }"); } [Fact] public void TestConditionalOperatorConstantCondition() { var source = @" class C { static void Main() { int x = 1; int y = 2; System.Console.WriteLine(true ? x : y); System.Console.WriteLine(false ? x : y); } }"; var comp = CompileAndVerify(source, expectedOutput: @" 1 2"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @"{ // Code size 15 (0xf) .maxstack 2 .locals init (int V_0) //y IL_0000: ldc.i4.1 IL_0001: ldc.i4.2 IL_0002: stloc.0 IL_0003: call ""void System.Console.WriteLine(int)"" IL_0008: ldloc.0 IL_0009: call ""void System.Console.WriteLine(int)"" IL_000e: ret }"); } [Fact] public void TestConditionalOperatorNullLiteral() { var source = @" class C { static void Main() { bool b = true; string s1 = b ? ""hello"" : null; System.Console.WriteLine(s1); string s2 = b ? null : ""goodbye""; System.Console.WriteLine(s2); } }"; var comp = CompileAndVerify(source); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 33 (0x21) .maxstack 2 IL_0000: ldc.i4.1 IL_0001: dup IL_0002: brtrue.s IL_0007 IL_0004: ldnull IL_0005: br.s IL_000c IL_0007: ldstr ""hello"" IL_000c: call ""void System.Console.WriteLine(string)"" IL_0011: brtrue.s IL_001a IL_0013: ldstr ""goodbye"" IL_0018: br.s IL_001b IL_001a: ldnull IL_001b: call ""void System.Console.WriteLine(string)"" IL_0020: ret }"); } [Fact] public void TestConditionalOperatorLambda() { var source = @" class C { static void Main() { bool b = true; System.Func<int, int> f = null; System.Console.WriteLine(f); System.Func<int, int> g1 = b ? f : x => x; System.Console.WriteLine(g1); System.Func<int, int> g2 = b ? x => x : f; System.Console.WriteLine(g2); } }"; // NOTE: this is slightly different from the Dev10 IL, which caches the lambdas in static fields var comp = CompileAndVerify(source); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 93 (0x5d) .maxstack 3 .locals init (System.Func<int, int> V_0) //f IL_0000: ldc.i4.1 IL_0001: ldnull IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call ""void System.Console.WriteLine(object)"" IL_0009: dup IL_000a: brtrue.s IL_002d IL_000c: ldsfld ""System.Func<int, int> C.<>c.<>9__0_0"" IL_0011: dup IL_0012: brtrue.s IL_002e IL_0014: pop IL_0015: ldsfld ""C.<>c C.<>c.<>9"" IL_001a: ldftn ""int C.<>c.<Main>b__0_0(int)"" IL_0020: newobj ""System.Func<int, int>..ctor(object, System.IntPtr)"" IL_0025: dup IL_0026: stsfld ""System.Func<int, int> C.<>c.<>9__0_0"" IL_002b: br.s IL_002e IL_002d: ldloc.0 IL_002e: call ""void System.Console.WriteLine(object)"" IL_0033: brtrue.s IL_0038 IL_0035: ldloc.0 IL_0036: br.s IL_0057 IL_0038: ldsfld ""System.Func<int, int> C.<>c.<>9__0_1"" IL_003d: dup IL_003e: brtrue.s IL_0057 IL_0040: pop IL_0041: ldsfld ""C.<>c C.<>c.<>9"" IL_0046: ldftn ""int C.<>c.<Main>b__0_1(int)"" IL_004c: newobj ""System.Func<int, int>..ctor(object, System.IntPtr)"" IL_0051: dup IL_0052: stsfld ""System.Func<int, int> C.<>c.<>9__0_1"" IL_0057: call ""void System.Console.WriteLine(object)"" IL_005c: ret } "); } [Fact] public void TestConditionalOperatorMethodGroup() { var source = @" class C { static void Main() { bool b = true; System.Func<int> f = null; System.Console.WriteLine(f); System.Func<int> g1 = b ? f : M; System.Console.WriteLine(g1); System.Func<int> g2 = b ? M : f; System.Console.WriteLine(g2); } static int M() { return 0; } }"; var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 55 (0x37) .maxstack 3 .locals init (System.Func<int> V_0) //f IL_0000: ldc.i4.1 IL_0001: ldnull IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: call ""void System.Console.WriteLine(object)"" IL_0009: dup IL_000a: brtrue.s IL_001a IL_000c: ldnull IL_000d: ldftn ""int C.M()"" IL_0013: newobj ""System.Func<int>..ctor(object, System.IntPtr)"" IL_0018: br.s IL_001b IL_001a: ldloc.0 IL_001b: call ""void System.Console.WriteLine(object)"" IL_0020: brtrue.s IL_0025 IL_0022: ldloc.0 IL_0023: br.s IL_0031 IL_0025: ldnull IL_0026: ldftn ""int C.M()"" IL_002c: newobj ""System.Func<int>..ctor(object, System.IntPtr)"" IL_0031: call ""void System.Console.WriteLine(object)"" IL_0036: ret } "); } [Fact] public void TestConditionalOperatorPreferWider() { var source = @" class C { static void Main() { bool b = true; System.Console.Write(b ? 0 : (short)1); System.Console.Write(b ? 0 : 1u); } }"; // NOTE: second call is to Write(uint) var comp = CompileAndVerify(source, expectedOutput: "00"); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { // Code size 19 (0x13) .maxstack 3 IL_0000: ldc.i4.1 IL_0001: dup IL_0002: ldc.i4.0 IL_0003: ceq IL_0005: call ""void System.Console.Write(int)"" IL_000a: ldc.i4.0 IL_000b: ceq IL_000d: call ""void System.Console.Write(uint)"" IL_0012: ret }"); } /// <summary> /// This specific code has caused problems in the past. /// System.Security.VerificationException on the second attempt. /// </summary> /// <remarks> /// No special handling seems to have been required to make this work /// in Roslyn. /// </remarks> [Fact] public void TestConditionalOperatorInterfaceRegression1() { var source = @" using System; using System.Collections.Generic; public class Test { static void Main() { IList<Type> knownTypeList = null; Console.Write(""first attempt: ""); object o1 = (IList<Type>)(knownTypeList == null ? new Type[0] : knownTypeList); Console.WriteLine(o1); Console.Write(""second attempt: ""); IList<Type> o2 = (IList<Type>)(knownTypeList == null ? new Type[0] : knownTypeList); Console.WriteLine(o2); } }"; // NOTE: second call is to Write(uint) var comp = CompileAndVerify(source, expectedOutput: @" first attempt: System.Type[] second attempt: System.Type[]"); comp.VerifyDiagnostics(); comp.VerifyIL("Test.Main", @" { // Code size 61 (0x3d) .maxstack 1 .locals init (System.Collections.Generic.IList<System.Type> V_0, //knownTypeList System.Collections.Generic.IList<System.Type> V_1) IL_0000: ldnull IL_0001: stloc.0 IL_0002: ldstr ""first attempt: "" IL_0007: call ""void System.Console.Write(string)"" IL_000c: ldloc.0 IL_000d: brfalse.s IL_0012 IL_000f: ldloc.0 IL_0010: br.s IL_001a IL_0012: ldc.i4.0 IL_0013: newarr ""System.Type"" IL_0018: stloc.1 IL_0019: ldloc.1 IL_001a: call ""void System.Console.WriteLine(object)"" IL_001f: ldstr ""second attempt: "" IL_0024: call ""void System.Console.Write(string)"" IL_0029: ldloc.0 IL_002a: brfalse.s IL_002f IL_002c: ldloc.0 IL_002d: br.s IL_0037 IL_002f: ldc.i4.0 IL_0030: newarr ""System.Type"" IL_0035: stloc.1 IL_0036: ldloc.1 IL_0037: call ""void System.Console.WriteLine(object)"" IL_003c: ret }"); } /// <summary> /// From orcas bug #42645. PEVerify fails. /// </summary> [Fact] public void TestConditionalOperatorInterfaceRegression2() { var source = @" using System.Collections.Generic; public class Test { static void Main() { int[] a = new int[] { }; List<int> b = new List<int>(); IEnumerable<int> c = a != null ? a : (IEnumerable<int>)b; System.Console.WriteLine(c); } }"; // Note the explicit casts, even though the conversions are // implicit reference conversions. var comp = CompileAndVerify(source); comp.VerifyDiagnostics(); comp.VerifyIL("Test.Main", @" { // Code size 30 (0x1e) .maxstack 1 .locals init (int[] V_0, //a System.Collections.Generic.List<int> V_1, //b System.Collections.Generic.IEnumerable<int> V_2) IL_0000: ldc.i4.0 IL_0001: newarr ""int"" IL_0006: stloc.0 IL_0007: newobj ""System.Collections.Generic.List<int>..ctor()"" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: brtrue.s IL_0015 IL_0010: ldloc.1 IL_0011: stloc.2 IL_0012: ldloc.2 IL_0013: br.s IL_0018 IL_0015: ldloc.0 IL_0016: stloc.2 IL_0017: ldloc.2 IL_0018: call ""void System.Console.WriteLine(object)"" IL_001d: ret }"); } [Fact] public void TestConditionalOperatorInterfaceRegression2a() { var source = @" using System.Collections.Generic; public class Test { static void Main() { int[] a = new int[] { }; List<int> b = new List<int>(); IEnumerable<int> c = a != null ? a : (IEnumerable<int>)b; } }"; // Note the explicit casts, even though the conversions are // implicit reference conversions. var comp = CompileAndVerify(source); comp.VerifyDiagnostics(); comp.VerifyIL("Test.Main", @" { // Code size 16 (0x10) .maxstack 1 .locals init (int[] V_0, //a System.Collections.Generic.List<int> V_1) //b IL_0000: ldc.i4.0 IL_0001: newarr ""int"" IL_0006: stloc.0 IL_0007: newobj ""System.Collections.Generic.List<int>..ctor()"" IL_000c: stloc.1 IL_000d: ldloc.0 IL_000e: pop IL_000f: ret } "); } /// <summary> /// From whidbey bug #108643. Extraneous class casts in Test1.m2 (vs Test1.m1). /// </summary> [Fact] public void TestConditionalOperatorInterfaceRegression3() { var source = @" interface Base { Base m1();} class Driver { public static int mask = 1;} class Test1 : Base { Base next; Base same; int cnt = 0; public Test1(Base link) { same = this; next = link; if (next == null) next = this; } public Base m1() { return ((++cnt) & Driver.mask) != 0 ? same : next; } //version1 (explicit impl in original repro) public Base m2() { return ((++cnt) & Driver.mask) != 0 ? this : next; } //version2 }"; var comp = CompileAndVerify(source); comp.VerifyDiagnostics(); comp.VerifyIL("Test1.m1", @" { // Code size 39 (0x27) .maxstack 3 .locals init (int V_0) IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: ldfld ""int Test1.cnt"" IL_0007: ldc.i4.1 IL_0008: add IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: stfld ""int Test1.cnt"" IL_0010: ldloc.0 IL_0011: ldsfld ""int Driver.mask"" IL_0016: and IL_0017: brtrue.s IL_0020 IL_0019: ldarg.0 IL_001a: ldfld ""Base Test1.next"" IL_001f: ret IL_0020: ldarg.0 IL_0021: ldfld ""Base Test1.same"" IL_0026: ret }"); // NOTE: no castclass comp.VerifyIL("Test1.m2", @" { // Code size 36 (0x24) .maxstack 3 .locals init (int V_0, Base V_1) IL_0000: ldarg.0 IL_0001: ldarg.0 IL_0002: ldfld ""int Test1.cnt"" IL_0007: ldc.i4.1 IL_0008: add IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: stfld ""int Test1.cnt"" IL_0010: ldloc.0 IL_0011: ldsfld ""int Driver.mask"" IL_0016: and IL_0017: brtrue.s IL_0020 IL_0019: ldarg.0 IL_001a: ldfld ""Base Test1.next"" IL_001f: ret IL_0020: ldarg.0 IL_0021: stloc.1 IL_0022: ldloc.1 IL_0023: ret }"); } /// <summary> /// From whidbey bug #49619. PEVerify fails. /// </summary> [Fact] public void TestConditionalOperatorInterfaceRegression4() { var source = @" public interface IA { } public interface IB { int f(); } public class AB1 : IA, IB { public int f() { return 42; } } public class AB2 : IA, IB { public int f() { return 1; } } class MainClass { public static void g(bool p) { (p ? (IB)new AB1() : (IB)new AB2()).f(); } }"; // Note the explicit casts, even though the conversions are // implicit reference conversions. // CONSIDER: dev10 writes to/reads from a temp to simulate // a static cast (instead of using castclass). var comp = CompileAndVerify(source); comp.VerifyDiagnostics(); comp.VerifyIL("MainClass.g", @" { // Code size 26 (0x1a) .maxstack 1 .locals init (IB V_0) IL_0000: ldarg.0 IL_0001: brtrue.s IL_000c IL_0003: newobj ""AB2..ctor()"" IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: br.s IL_0013 IL_000c: newobj ""AB1..ctor()"" IL_0011: stloc.0 IL_0012: ldloc.0 IL_0013: callvirt ""int IB.f()"" IL_0018: pop IL_0019: ret }"); } [WorkItem(9229, "DevDiv_Projects/Roslyn")] [Fact] public void TestConditionalOperatorForExpression() { var source = @" using System.Linq; using System.Collections.Generic; class Program { static void Main(string[] args) { string[] arr = new string[] { ""aaa"", ""bbb"", ""ccc"" }; int[] arr_int = new int[] { 111, 222, 333 }; IEnumerable<string> s = true ? from x in arr select x : from x in arr_int select x.ToString(); foreach (var item in s) { System.Console.WriteLine(item); } } }"; string expectedOutput = @"aaa bbb ccc"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestConditionalOperatorForObjInit() { var source = @" using System; class Program { static void Main(string[] args) { Goo f1 = new Goo(), f2 = new Goo(), f3 = new Goo(); bool b = true; f3 = b ? f1 = new Goo { i = 1 } : f2 = new Goo { i = 2 }; Console.WriteLine(f1.i); Console.WriteLine(f2.i); Console.WriteLine(f3.i); b = false; f3 = b ? f1 = new Goo { i = 3 } : f2 = new Goo { i = 4 }; Console.WriteLine(f1.i); Console.WriteLine(f2.i); Console.WriteLine(f3.i); } } class Goo { public int i; } "; string expectedOutput = @"1 0 1 1 4 4"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestConditionalOperatorForObjInit_2() { var source = @" using System; class Program { static void Main(string[] args) { bool b1 = true; bool b2 = false; Goo f = new Goo { i = b1 ? 10 : -10 }; Console.WriteLine(f.i); f = new Goo { i = b2 ? 10 : -10 }; Console.WriteLine(f.i); } } class Goo { public int i; } "; string expectedOutput = @"10 -10"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestConditionalOperatorForExpressionTree() { var source = @" using System; using System.Linq.Expressions; class Program { static void Main(string[] args) { Expression<Func<bool, long, int, long>> testExpr = (x, y, z) => x ? y : z; var testFunc = testExpr.Compile(); Console.WriteLine(testFunc(false, (long)3, 100)); //100 } } "; string expectedOutput = @"100"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestConditionalOperatorForCustomOperator() { var source = @" using System; using System.Linq.Expressions; class Program { static void Main(string[] args) { Expression<Func<TestStruct, int?, int, int?>> testExpr = (x, y, z) => x ? y : z; var testFunc = testExpr.Compile(); Console.WriteLine(testFunc(new TestStruct(), (int?)null, 10)); //10 } } public struct TestStruct { public static bool operator true(TestStruct ts) { return false; } public static bool operator false(TestStruct ts) { return true; } } "; string expectedOutput = @"10"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestConditionalOperatorForMultiCondition() { var source = @" class Program { static void Main(string[] args) { System.Console.WriteLine(false ? 1 : true ? 2 : 3); } } "; string expectedOutput = @"2"; string expectedIL = @"{ // Code size 7 (0x7) .maxstack 1 IL_0000: ldc.i4.2 IL_0001: call ""void System.Console.WriteLine(int)"" IL_0006: ret }"; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.Main", expectedIL); } [WorkItem(528275, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/528275")] [Fact] public void TestConditionalOperatorForImplicitConv() { var source = @" using System.Linq; class Program { static void Main(string[] args) { System.Console.WriteLine(false ? Enumerable.Empty<int>() : new int[] { 1 }); } } "; string expectedOutput = @"System.Int32[]"; string expectedIL = @"{ // Code size 16 (0x10) .maxstack 4 IL_0000: ldc.i4.1 IL_0001: newarr ""int"" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: stelem.i4 IL_000a: call ""void System.Console.WriteLine(object)"" IL_000f: ret }"; // Dev11 compiler reports WRN_UnreachableExpr, but reachability is defined for statements not for expressions. // We don't report the warning. CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.Main", expectedIL).VerifyDiagnostics(); } [Fact] public void TestConditionalOperatorForConversion() { var source = @" using System; class Program { static void Main(string[] args) { object valueFromDatabase; decimal result; valueFromDatabase = DBNull.Value; result = (valueFromDatabase != DBNull.Value ? (decimal)valueFromDatabase : (decimal)0); System.Console.WriteLine(result); result = (decimal)(valueFromDatabase != DBNull.Value ? valueFromDatabase : 0); //Runtime exception System.Console.WriteLine(result); } } "; string expectedIL = @" { // Code size 60 (0x3c) .maxstack 2 .locals init (object V_0) //valueFromDatabase IL_0000: ldsfld ""System.DBNull System.DBNull.Value"" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldsfld ""System.DBNull System.DBNull.Value"" IL_000c: bne.un.s IL_0015 IL_000e: ldsfld ""decimal decimal.Zero"" IL_0013: br.s IL_001b IL_0015: ldloc.0 IL_0016: unbox.any ""decimal"" IL_001b: call ""void System.Console.WriteLine(decimal)"" IL_0020: ldloc.0 IL_0021: ldsfld ""System.DBNull System.DBNull.Value"" IL_0026: bne.un.s IL_0030 IL_0028: ldc.i4.0 IL_0029: box ""int"" IL_002e: br.s IL_0031 IL_0030: ldloc.0 IL_0031: unbox.any ""decimal"" IL_0036: call ""void System.Console.WriteLine(decimal)"" IL_003b: ret }"; CompileAndVerify(source).VerifyIL("Program.Main", expectedIL); } [Fact, WorkItem(530071, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530071")] public void TestConditionalOperatorForImplicitlyTypedArrays() { var source = @" using System; class Program { static void Main(string[] args) { int[] a, a1, a2; a = a1 = a2 = null; a = true ? a1 = new[] { 1, 2, 3 } : a2 = new[] { 4, 5, 6 }; foreach (int item in a1) { Console.WriteLine(item); } } } "; string expectedOutput = @"1 2 3"; // Dev11 compiler reports WRN_UnreachableExpr, but reachability is defined for statements not for expressions. // We don't report the warning. CompileAndVerify(source, expectedOutput: expectedOutput). VerifyDiagnostics(); } [Fact] public void TestConditionalOperatorForConst() { var source = @" class Program { const bool con = true; static int Main(string[] args) { int s1 = con != true ? 1 : 2; return s1; } } "; string expectedIL = @"{ // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.2 IL_0001: ret }"; CompileAndVerify(source).VerifyIL("Program.Main", expectedIL); } [Fact] public void TestConditionalOperatorForStatic() { var source = @" class Program { static bool con = true; static int Main(string[] args) { int s1 = con != true ? 1 : 2; return s1; } } "; string expectedIL = @"{ // Code size 11 (0xb) .maxstack 1 IL_0000: ldsfld ""bool Program.con"" IL_0005: brfalse.s IL_0009 IL_0007: ldc.i4.2 IL_0008: ret IL_0009: ldc.i4.1 IL_000a: ret }"; CompileAndVerify(source).VerifyIL("Program.Main", expectedIL); } [Fact] public void TestConditionalOperatorForGenericMethod() { var source = @" class Program { static void Main(string[] args) { int x = 1; object y = 0; System.Console.WriteLine(true ? fun(x) : fun(y)); } private static T fun<T>(T t) { return t; } } "; string expectedOutput = @"1"; string expectedIL = @"{ // Code size 17 (0x11) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: call ""int Program.fun<int>(int)"" IL_0006: box ""int"" IL_000b: call ""void System.Console.WriteLine(object)"" IL_0010: ret }"; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.Main", expectedIL); } [Fact] public void TestConditionalOperatorForGenericMethod2() { var source = @" class Program { static void Main(string[] args) { int x = 1; object y = 0; System.Console.WriteLine(y != null ? fun(x) : fun(y)); } private static T fun<T>(T t) { return t; } } "; string expectedOutput = @"1"; string expectedIL = @" { // Code size 37 (0x25) .maxstack 1 .locals init (int V_0, //x object V_1) //y IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: box ""int"" IL_0008: stloc.1 IL_0009: ldloc.1 IL_000a: brtrue.s IL_0014 IL_000c: ldloc.1 IL_000d: call ""object Program.fun<object>(object)"" IL_0012: br.s IL_001f IL_0014: ldloc.0 IL_0015: call ""int Program.fun<int>(int)"" IL_001a: box ""int"" IL_001f: call ""void System.Console.WriteLine(object)"" IL_0024: ret } "; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.Main", expectedIL); } [Fact] public void TestConditionalOperatorForParenthesizedExpression() { var source = @" class Program { static void Main(string[] args) { int x = 1; int y = 1; System.Console.WriteLine(((x == y)) ? (++x) : ((((++y))))); // OK } } "; string expectedOutput = @"2"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void TestConditionalOperatorForParameter() { var source = @" class Program { static bool b = true; static void Main(string[] args) { fun(b); } static void fun(bool b) { System.Console.WriteLine(b ? ""true"" : ""false""); } } "; string expectedOutput = @"true"; string expectedIL = @"{ // Code size 21 (0x15) .maxstack 1 IL_0000: ldarg.0 IL_0001: brtrue.s IL_000a IL_0003: ldstr ""false"" IL_0008: br.s IL_000f IL_000a: ldstr ""true"" IL_000f: call ""void System.Console.WriteLine(string)"" IL_0014: ret }"; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.fun", expectedIL); } [Fact] public void TestConditionalOperatorForParameterRef() { var source = @" class Program { static bool b = true; static void Main(string[] args) { fun(ref b); } static void fun(ref bool b) { System.Console.WriteLine(b != true ? b = true : b = false); } } "; string expectedOutput = @"False"; string expectedIL = @" { // Code size 24 (0x18) .maxstack 3 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: ldind.u1 IL_0002: brfalse.s IL_000c IL_0004: ldarg.0 IL_0005: ldc.i4.0 IL_0006: dup IL_0007: stloc.0 IL_0008: stind.i1 IL_0009: ldloc.0 IL_000a: br.s IL_0012 IL_000c: ldarg.0 IL_000d: ldc.i4.1 IL_000e: dup IL_000f: stloc.0 IL_0010: stind.i1 IL_0011: ldloc.0 IL_0012: call ""void System.Console.WriteLine(bool)"" IL_0017: ret }"; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.fun", expectedIL); } [Fact] public void TestConditionalOperatorForParameterOut() { var source = @" class Program { static bool b = true; static void Main(string[] args) { fun(out b); } static void fun(out bool b) { System.Console.WriteLine(string.IsNullOrEmpty(""s"") ? b = true : b = false); } } "; string expectedOutput = @"False"; string expectedIL = @"{ // Code size 32 (0x20) .maxstack 3 .locals init (bool V_0) IL_0000: ldstr ""s"" IL_0005: call ""bool string.IsNullOrEmpty(string)"" IL_000a: brtrue.s IL_0014 IL_000c: ldarg.0 IL_000d: ldc.i4.0 IL_000e: dup IL_000f: stloc.0 IL_0010: stind.i1 IL_0011: ldloc.0 IL_0012: br.s IL_001a IL_0014: ldarg.0 IL_0015: ldc.i4.1 IL_0016: dup IL_0017: stloc.0 IL_0018: stind.i1 IL_0019: ldloc.0 IL_001a: call ""void System.Console.WriteLine(bool)"" IL_001f: ret }"; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.fun", expectedIL); } [Fact] public void TestConditionalRequiringBox() { var source = @" public static class Program { public static void Main() { int a = 1; IGoo<string> i = null; GooVal e = new GooVal(); i = a > 1 ? i : e; System.Console.Write(i.Goo()); } interface IGoo<T> { T Goo(); } struct GooVal : IGoo<string> { public string Goo() { return ""Val ""; } } }"; string expectedOutput = @"Val "; string expectedIL = @" { // Code size 38 (0x26) .maxstack 2 .locals init (Program.IGoo<string> V_0, //i Program.GooVal V_1, //e Program.IGoo<string> V_2) IL_0000: ldc.i4.1 IL_0001: ldnull IL_0002: stloc.0 IL_0003: ldloca.s V_1 IL_0005: initobj ""Program.GooVal"" IL_000b: ldc.i4.1 IL_000c: bgt.s IL_0018 IL_000e: ldloc.1 IL_000f: box ""Program.GooVal"" IL_0014: stloc.2 IL_0015: ldloc.2 IL_0016: br.s IL_0019 IL_0018: ldloc.0 IL_0019: stloc.0 IL_001a: ldloc.0 IL_001b: callvirt ""string Program.IGoo<string>.Goo()"" IL_0020: call ""void System.Console.Write(string)"" IL_0025: ret }"; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.Main", expectedIL); } [Fact] public void TestCoalesceRequiringBox() { var source = @" public static class Program { public static void Main() { IGoo<string> i = null; GooVal e = new GooVal(); GooVal? n = e; i = i ?? e; System.Console.Write(i.Goo()); i = null; i = n ?? i; System.Console.Write(i.Goo()); } interface IGoo<T> { T Goo(); } struct GooVal : IGoo<string> { public string Goo() { return ""Val ""; } } }"; string expectedOutput = @"Val Val "; string expectedIL = @" { // Code size 81 (0x51) .maxstack 3 .locals init (Program.IGoo<string> V_0, //i Program.GooVal V_1, //e Program.GooVal? V_2, Program.IGoo<string> V_3) IL_0000: ldnull IL_0001: stloc.0 IL_0002: ldloca.s V_1 IL_0004: initobj ""Program.GooVal"" IL_000a: ldloc.1 IL_000b: newobj ""Program.GooVal?..ctor(Program.GooVal)"" IL_0010: ldloc.0 IL_0011: dup IL_0012: brtrue.s IL_001b IL_0014: pop IL_0015: ldloc.1 IL_0016: box ""Program.GooVal"" IL_001b: stloc.0 IL_001c: ldloc.0 IL_001d: callvirt ""string Program.IGoo<string>.Goo()"" IL_0022: call ""void System.Console.Write(string)"" IL_0027: ldnull IL_0028: stloc.0 IL_0029: stloc.2 IL_002a: ldloca.s V_2 IL_002c: call ""bool Program.GooVal?.HasValue.get"" IL_0031: brtrue.s IL_0036 IL_0033: ldloc.0 IL_0034: br.s IL_0044 IL_0036: ldloca.s V_2 IL_0038: call ""Program.GooVal Program.GooVal?.GetValueOrDefault()"" IL_003d: box ""Program.GooVal"" IL_0042: stloc.3 IL_0043: ldloc.3 IL_0044: stloc.0 IL_0045: ldloc.0 IL_0046: callvirt ""string Program.IGoo<string>.Goo()"" IL_004b: call ""void System.Console.Write(string)"" IL_0050: ret }"; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Program.Main", expectedIL); } [Fact(), WorkItem(543609, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543609")] public void SeveralAdjacentIfsWithConditionalExpressions() { var source = @" class Class1 { static void Main() { int x=0; int y=0; bool local_bool=true; if(true?true:local_bool) x++; else y++; if(true?false:local_bool) x++; else y++; if(true?local_bool:true) x++; else y++; } } "; string expectedOutput = @""; string expectedIL = @" { // Code size 25 (0x19) .maxstack 3 .locals init (int V_0, //x int V_1) //y IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldc.i4.1 IL_0005: ldloc.0 IL_0006: ldc.i4.1 IL_0007: add IL_0008: stloc.0 IL_0009: ldloc.1 IL_000a: ldc.i4.1 IL_000b: add IL_000c: stloc.1 IL_000d: brfalse.s IL_0014 IL_000f: ldloc.0 IL_0010: ldc.i4.1 IL_0011: add IL_0012: stloc.0 IL_0013: ret IL_0014: ldloc.1 IL_0015: ldc.i4.1 IL_0016: add IL_0017: stloc.1 IL_0018: ret }"; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Class1.Main", expectedIL); } [Fact(), WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void TestNestedConditionalAndNullOperators() { var src = @"using System.Security; [assembly: SecurityTransparent()] public interface I {} class A : I {} class B : I {} class D : I {} public class C : I { static I Tester(A a) { return a ?? (a != null ? (I)new B() : new C()); } static void Main() { System.Console.Write(Tester(null).GetType()); } }"; var verify = CompileAndVerify(src, options: TestOptions.DebugExe, expectedOutput: "C"); verify.VerifyIL("C.Tester", @" { // Code size 32 (0x20) .maxstack 2 .locals init (I V_0, I V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: dup IL_0005: brtrue.s IL_001b IL_0007: pop IL_0008: ldarg.0 IL_0009: brtrue.s IL_0014 IL_000b: newobj ""C..ctor()"" IL_0010: stloc.0 IL_0011: ldloc.0 IL_0012: br.s IL_001b IL_0014: newobj ""B..ctor()"" IL_0019: stloc.0 IL_001a: ldloc.0 IL_001b: stloc.1 IL_001c: br.s IL_001e IL_001e: ldloc.1 IL_001f: ret }"); // Optimized verify = CompileAndVerify(src, expectedOutput: "C"); verify.VerifyIL("C.Tester", @" { // Code size 26 (0x1a) .maxstack 2 .locals init (I V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: dup IL_0004: brtrue.s IL_0019 IL_0006: pop IL_0007: ldarg.0 IL_0008: brtrue.s IL_0012 IL_000a: newobj ""C..ctor()"" IL_000f: stloc.0 IL_0010: ldloc.0 IL_0011: ret IL_0012: newobj ""B..ctor()"" IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: ret }"); } [Fact(), WorkItem(543609, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543609")] public void UnreachableLabelInUnreachableCode() { var source = @" class Class1 { static void Main() { var local = ""hi""; if (true) { } else { goto unreachable; // this is not emitted } goto reachable; // stack for this block == 1 because of stacklocal System.Console.WriteLine(""unreachable""); // label to which we have not seen branches yet unreachable: System.Console.WriteLine(""hello""); reachable: System.Console.WriteLine(local); } } "; string expectedOutput = @"hi"; string expectedIL = @" { // Code size 11 (0xb) .maxstack 2 IL_0000: ldstr ""hi"" IL_0005: call ""void System.Console.WriteLine(string)"" IL_000a: ret } "; CompileAndVerify(source, expectedOutput: expectedOutput).VerifyIL("Class1.Main", expectedIL); } [Fact, WorkItem(12439, "https://github.com/dotnet/roslyn/issues/12439")] public void GenericStructInLambda() { var source = @" using System; static class LiveList { struct WhereInfo<TSource> { public int Key { get; set; } } static void Where<TSource>() { Action subscribe = () => { WhereInfo<TSource>? previous = null; var previousKey = previous?.Key; }; } }"; CompileAndVerify(source); } [Fact, WorkItem(12439, "https://github.com/dotnet/roslyn/issues/12439")] public void GenericStructInIterator() { var source = @" using System; using System.Collections.Generic; static class LiveList { struct WhereInfo<TSource> { public int Key { get; set; } } static IEnumerable<int> Where<TSource>() { WhereInfo<TSource>? previous = null; var previousKey = previous?.Key; yield break; } }"; CompileAndVerify(source); } [Fact, WorkItem(17756, "https://github.com/dotnet/roslyn/issues/17756")] public void TestConditionalOperatorNotLvalue() { var source = @" class Program { interface IIncrementable { int Increment(); } struct S1: IIncrementable { public int field; public int Increment() => field++; } static void Main() { S1 v = default(S1); v.Increment(); (true ? v : default(S1)).Increment(); System.Console.WriteLine(v.field); System.Console.WriteLine((false ? default(S1): v).Increment()); System.Console.WriteLine(v.field); Test(ref v); System.Console.WriteLine(v.field); } static void Test<T>(ref T arg) where T:IIncrementable { (true ? arg : default(T)).Increment(); } } "; string expectedOutput = @"1 1 1 1"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact, WorkItem(17756, "https://github.com/dotnet/roslyn/issues/17756")] public void TestConditionalOperatorNotLvaluePointerElementAccess() { var source = @" class Program { struct S1 { public int field; public int Increment() => field++; } unsafe static void Main() { S1 v = default(S1); v.Increment(); System.Console.WriteLine(v.field); S1* pv = &v; (true ? pv[0] : default(S1)).Increment(); System.Console.WriteLine(v.field); } } "; string expectedOutput = @"1 1"; CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.UnsafeReleaseExe, verify: Verification.Fails); } [Fact, WorkItem(17756, "https://github.com/dotnet/roslyn/issues/17756")] public void TestConditionalOperatorNotLvalueRefCall() { var source = @" class Program { struct S1 { public int field; public int Increment() => field++; } private static S1 sv; private static ref S1 M1() { return ref sv; } static void Main() { sv.Increment(); System.Console.WriteLine(sv.field); (true ? M1() : default(S1)).Increment(); System.Console.WriteLine(sv.field); } } "; string expectedOutput = @"1 1"; CompileAndVerify(source, expectedOutput: expectedOutput); } [Fact] public void RefReadonlyConditional() { var comp = CompileAndVerify(@" using System; struct S { public int X; public S(int x) => X = x; public void Mutate() => X++; } class C { static void Main() { S local1 = new S(0); S local2 = new S(0); Console.WriteLine(local1.X); Console.WriteLine(local2.X); bool condition = false; (condition ? ref local1 : ref local2).Mutate(); Console.WriteLine(local1.X); Console.WriteLine(local2.X); (condition ? ref local1 : ref local2).X = 0; Console.WriteLine(local1.X); Console.WriteLine(local2.X); ref readonly S ro1 = ref local1; ref readonly S ro2 = ref local2; (condition ? ref local1 : ref ro2).Mutate(); Console.WriteLine(local1.X); Console.WriteLine(local2.X); (condition ? ref ro1 : ref local2).Mutate(); Console.WriteLine(local1.X); Console.WriteLine(local2.X); (!condition ? ref local1 : ref ro2).Mutate(); Console.WriteLine(local1.X); Console.WriteLine(local2.X); (!condition ? ref ro1 : ref local2).Mutate(); Console.WriteLine(local1.X); Console.WriteLine(local2.X); } }", expectedOutput: @"0 0 0 1 0 0 0 0 0 0 0 0 0 0"); comp.VerifyIL("C.Main", @" { // Code size 290 (0x122) .maxstack 3 .locals init (S V_0, //local1 S V_1, //local2 S& V_2, //ro1 S& V_3, //ro2 S V_4) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.0 IL_0003: call ""S..ctor(int)"" IL_0008: ldloca.s V_1 IL_000a: ldc.i4.0 IL_000b: call ""S..ctor(int)"" IL_0010: ldloc.0 IL_0011: ldfld ""int S.X"" IL_0016: call ""void System.Console.WriteLine(int)"" IL_001b: ldloc.1 IL_001c: ldfld ""int S.X"" IL_0021: call ""void System.Console.WriteLine(int)"" IL_0026: ldc.i4.0 IL_0027: dup IL_0028: brtrue.s IL_002e IL_002a: ldloca.s V_1 IL_002c: br.s IL_0030 IL_002e: ldloca.s V_0 IL_0030: call ""void S.Mutate()"" IL_0035: ldloc.0 IL_0036: ldfld ""int S.X"" IL_003b: call ""void System.Console.WriteLine(int)"" IL_0040: ldloc.1 IL_0041: ldfld ""int S.X"" IL_0046: call ""void System.Console.WriteLine(int)"" IL_004b: dup IL_004c: brtrue.s IL_0052 IL_004e: ldloca.s V_1 IL_0050: br.s IL_0054 IL_0052: ldloca.s V_0 IL_0054: ldc.i4.0 IL_0055: stfld ""int S.X"" IL_005a: ldloc.0 IL_005b: ldfld ""int S.X"" IL_0060: call ""void System.Console.WriteLine(int)"" IL_0065: ldloc.1 IL_0066: ldfld ""int S.X"" IL_006b: call ""void System.Console.WriteLine(int)"" IL_0070: ldloca.s V_0 IL_0072: stloc.2 IL_0073: ldloca.s V_1 IL_0075: stloc.3 IL_0076: dup IL_0077: brtrue.s IL_0081 IL_0079: ldloc.3 IL_007a: ldobj ""S"" IL_007f: br.s IL_0082 IL_0081: ldloc.0 IL_0082: stloc.s V_4 IL_0084: ldloca.s V_4 IL_0086: call ""void S.Mutate()"" IL_008b: ldloc.0 IL_008c: ldfld ""int S.X"" IL_0091: call ""void System.Console.WriteLine(int)"" IL_0096: ldloc.1 IL_0097: ldfld ""int S.X"" IL_009c: call ""void System.Console.WriteLine(int)"" IL_00a1: dup IL_00a2: brtrue.s IL_00a7 IL_00a4: ldloc.1 IL_00a5: br.s IL_00ad IL_00a7: ldloc.2 IL_00a8: ldobj ""S"" IL_00ad: stloc.s V_4 IL_00af: ldloca.s V_4 IL_00b1: call ""void S.Mutate()"" IL_00b6: ldloc.0 IL_00b7: ldfld ""int S.X"" IL_00bc: call ""void System.Console.WriteLine(int)"" IL_00c1: ldloc.1 IL_00c2: ldfld ""int S.X"" IL_00c7: call ""void System.Console.WriteLine(int)"" IL_00cc: dup IL_00cd: brfalse.s IL_00d7 IL_00cf: ldloc.3 IL_00d0: ldobj ""S"" IL_00d5: br.s IL_00d8 IL_00d7: ldloc.0 IL_00d8: stloc.s V_4 IL_00da: ldloca.s V_4 IL_00dc: call ""void S.Mutate()"" IL_00e1: ldloc.0 IL_00e2: ldfld ""int S.X"" IL_00e7: call ""void System.Console.WriteLine(int)"" IL_00ec: ldloc.1 IL_00ed: ldfld ""int S.X"" IL_00f2: call ""void System.Console.WriteLine(int)"" IL_00f7: brfalse.s IL_00fc IL_00f9: ldloc.1 IL_00fa: br.s IL_0102 IL_00fc: ldloc.2 IL_00fd: ldobj ""S"" IL_0102: stloc.s V_4 IL_0104: ldloca.s V_4 IL_0106: call ""void S.Mutate()"" IL_010b: ldloc.0 IL_010c: ldfld ""int S.X"" IL_0111: call ""void System.Console.WriteLine(int)"" IL_0116: ldloc.1 IL_0117: ldfld ""int S.X"" IL_011c: call ""void System.Console.WriteLine(int)"" IL_0121: ret }"); } [Fact] public void RefConditionalOperatorInValConditional() { var source = @" using System; class Program { static void Main() { S1 local1 = 1; S1 local2 = 2; bool condition = false; (condition ? condition ? ref local1 : ref local2 : condition ? ref local1 : ref local2).Mutate(); // must print '2', the above mutation is applied to an rvalue. System.Console.WriteLine(local2.field); (false ? condition ? ref local1 : ref local2 : condition ? ref local1 : ref local2).Mutate(); // must print '2', the above mutation is applied to an rvalue. System.Console.WriteLine(local2.field); (false ? false ? ref local1 : ref local2 : false ? ref local1 : ref local2).Mutate(); // must print '2', the above mutation is applied to an rvalue. System.Console.WriteLine(local2.field); } struct S1 { public int field; public static implicit operator S1(int arg) => new S1 {field = arg }; public void Mutate() { field = 42; } } } "; string expectedOutput = @"2 2 2"; var verify = CompileAndVerify(source, expectedOutput: expectedOutput); verify.VerifyIL("Program.Main", @" { // Code size 101 (0x65) .maxstack 1 .locals init (Program.S1 V_0, //local1 Program.S1 V_1, //local2 bool V_2, //condition Program.S1 V_3) IL_0000: ldc.i4.1 IL_0001: call ""Program.S1 Program.S1.op_Implicit(int)"" IL_0006: stloc.0 IL_0007: ldc.i4.2 IL_0008: call ""Program.S1 Program.S1.op_Implicit(int)"" IL_000d: stloc.1 IL_000e: ldc.i4.0 IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: brtrue.s IL_001c IL_0013: ldloc.2 IL_0014: brtrue.s IL_0019 IL_0016: ldloc.1 IL_0017: br.s IL_0023 IL_0019: ldloc.0 IL_001a: br.s IL_0023 IL_001c: ldloc.2 IL_001d: brtrue.s IL_0022 IL_001f: ldloc.1 IL_0020: br.s IL_0023 IL_0022: ldloc.0 IL_0023: stloc.3 IL_0024: ldloca.s V_3 IL_0026: call ""void Program.S1.Mutate()"" IL_002b: ldloc.1 IL_002c: ldfld ""int Program.S1.field"" IL_0031: call ""void System.Console.WriteLine(int)"" IL_0036: ldloc.2 IL_0037: brtrue.s IL_003c IL_0039: ldloc.1 IL_003a: br.s IL_003d IL_003c: ldloc.0 IL_003d: stloc.3 IL_003e: ldloca.s V_3 IL_0040: call ""void Program.S1.Mutate()"" IL_0045: ldloc.1 IL_0046: ldfld ""int Program.S1.field"" IL_004b: call ""void System.Console.WriteLine(int)"" IL_0050: ldloc.1 IL_0051: stloc.3 IL_0052: ldloca.s V_3 IL_0054: call ""void Program.S1.Mutate()"" IL_0059: ldloc.1 IL_005a: ldfld ""int Program.S1.field"" IL_005f: call ""void System.Console.WriteLine(int)"" IL_0064: ret }"); } [Fact] [WorkItem(3519, "https://github.com/dotnet/roslyn/issues/35319")] public void ConditionalAccessUnconstrainedTField() { var source = @"using System; public class C<T> { public C(T t) => this.t = t; public C(){} private T t; public void Print() { Console.WriteLine(t?.ToString()); Console.WriteLine(t); } } public struct S { int a; public override string ToString() => a++.ToString(); } public static class Program { public static void Main() { new C<S>().Print(); new C<S?>().Print(); new C<S?>(new S()).Print(); new C<string>(""hello"").Print(); new C<string>().Print(); } }"; var verify = CompileAndVerify(source, expectedOutput: @"0 1 0 0 hello hello"); verify.VerifyIL("C<T>.Print()", @" { // Code size 75 (0x4b) .maxstack 2 .locals init (T V_0) IL_0000: ldarg.0 IL_0001: ldflda ""T C<T>.t"" IL_0006: ldloca.s V_0 IL_0008: initobj ""T"" IL_000e: ldloc.0 IL_000f: box ""T"" IL_0014: brtrue.s IL_002a IL_0016: ldobj ""T"" IL_001b: stloc.0 IL_001c: ldloca.s V_0 IL_001e: ldloc.0 IL_001f: box ""T"" IL_0024: brtrue.s IL_002a IL_0026: pop IL_0027: ldnull IL_0028: br.s IL_0035 IL_002a: constrained. ""T"" IL_0030: callvirt ""string object.ToString()"" IL_0035: call ""void System.Console.WriteLine(string)"" IL_003a: ldarg.0 IL_003b: ldfld ""T C<T>.t"" IL_0040: box ""T"" IL_0045: call ""void System.Console.WriteLine(object)"" IL_004a: ret }"); } [Fact] [WorkItem(3519, "https://github.com/dotnet/roslyn/issues/35319")] public void ConditionalAccessReadonlyUnconstrainedTField() { var source = @"using System; public class C<T> { public C(T t) => this.t = t; public C(){} readonly T t; public void Print() { Console.WriteLine(t?.ToString()); Console.WriteLine(t); } } public struct S { int a; public override string ToString() => a++.ToString(); } public static class Program { public static void Main() { new C<S>().Print(); new C<S?>().Print(); new C<S?>(new S()).Print(); new C<string>(""hello"").Print(); new C<string>().Print(); } } "; var verify = CompileAndVerify(source, expectedOutput: @"0 0 0 0 hello hello"); verify.VerifyIL("C<T>.Print()", @" { // Code size 59 (0x3b) .maxstack 2 .locals init (T V_0) IL_0000: ldarg.0 IL_0001: ldfld ""T C<T>.t"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: dup IL_000a: ldobj ""T"" IL_000f: box ""T"" IL_0014: brtrue.s IL_001a IL_0016: pop IL_0017: ldnull IL_0018: br.s IL_0025 IL_001a: constrained. ""T"" IL_0020: callvirt ""string object.ToString()"" IL_0025: call ""void System.Console.WriteLine(string)"" IL_002a: ldarg.0 IL_002b: ldfld ""T C<T>.t"" IL_0030: box ""T"" IL_0035: call ""void System.Console.WriteLine(object)"" IL_003a: ret }"); } [Fact] [WorkItem(3519, "https://github.com/dotnet/roslyn/issues/35319")] public void ConditionalAccessUnconstrainedTLocal() { var source = @"using System; public class C<T> { public C(T t) => this.t = t; public C(){} private T t; public void Print() { var temp = t; Console.WriteLine(temp?.ToString()); Console.WriteLine(temp); } } public struct S { int a; public override string ToString() => a++.ToString(); } public static class Program { public static void Main() { new C<S>().Print(); new C<S?>().Print(); new C<S?>(new S()).Print(); new C<string>(""hello"").Print(); new C<string>().Print(); } }"; var verify = CompileAndVerify(source, expectedOutput: @"0 1 0 0 hello hello"); verify.VerifyIL("C<T>.Print()", @" { // Code size 73 (0x49) .maxstack 2 .locals init (T V_0, //temp T V_1) IL_0000: ldarg.0 IL_0001: ldfld ""T C<T>.t"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: ldloca.s V_1 IL_000b: initobj ""T"" IL_0011: ldloc.1 IL_0012: box ""T"" IL_0017: brtrue.s IL_002d IL_0019: ldobj ""T"" IL_001e: stloc.1 IL_001f: ldloca.s V_1 IL_0021: ldloc.1 IL_0022: box ""T"" IL_0027: brtrue.s IL_002d IL_0029: pop IL_002a: ldnull IL_002b: br.s IL_0038 IL_002d: constrained. ""T"" IL_0033: callvirt ""string object.ToString()"" IL_0038: call ""void System.Console.WriteLine(string)"" IL_003d: ldloc.0 IL_003e: box ""T"" IL_0043: call ""void System.Console.WriteLine(object)"" IL_0048: ret }"); } [Fact] [WorkItem(3519, "https://github.com/dotnet/roslyn/issues/35319")] public void ConditionalAccessUnconstrainedTTemp() { var source = @"using System; public class C<T> { public C(T t) => this.t = t; public C(){} T t; T M() => t; public void Print() => Console.WriteLine(M()?.ToString()); } public static class Program { public static void Main() { new C<int>().Print(); new C<int?>().Print(); new C<int?>(0).Print(); new C<string>(""hello"").Print(); new C<string>().Print(); } } "; var verify = CompileAndVerify(source, expectedOutput: @"0 0 hello "); verify.VerifyIL("C<T>.Print()", @" { // Code size 43 (0x2b) .maxstack 2 .locals init (T V_0) IL_0000: ldarg.0 IL_0001: call ""T C<T>.M()"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: dup IL_000a: ldobj ""T"" IL_000f: box ""T"" IL_0014: brtrue.s IL_001a IL_0016: pop IL_0017: ldnull IL_0018: br.s IL_0025 IL_001a: constrained. ""T"" IL_0020: callvirt ""string object.ToString()"" IL_0025: call ""void System.Console.WriteLine(string)"" IL_002a: ret }"); } [Fact, WorkItem(40690, "https://github.com/dotnet/roslyn/issues/40690")] public void ConditionalAccess_GenericExtension_ValueTuple() { var source = @" using System; public static class Extension { public static string GetValue(this object value) => value?.ToString(); } public class Class<T> { public (long, T) Data { get; } public Class((long, T) data) { Data = data; } internal string Value { get { var q2 = Data.Item2?.GetValue(); return q2; } } } class Program { static void Main(string[] args) { Console.WriteLine(new Class<string>((0, ""abc"")).Value); Console.WriteLine(new Class<string>((0, null)).Value); Console.WriteLine(new Class<int>((0, 0)).Value); Console.WriteLine(new Class<int?>((0, 0)).Value); Console.WriteLine(new Class<int?>((0, null)).Value); } }"; var verifier = CompileAndVerify(source, expectedOutput: @"abc 0 0 "); verifier.VerifyIL("Class<T>.Value.get", @" { // Code size 46 (0x2e) .maxstack 2 .locals init (System.ValueTuple<long, T> V_0) IL_0000: ldarg.0 IL_0001: call ""System.ValueTuple<long, T> Class<T>.Data.get"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: ldflda ""T System.ValueTuple<long, T>.Item2"" IL_000e: dup IL_000f: ldobj ""T"" IL_0014: box ""T"" IL_0019: brtrue.s IL_001e IL_001b: pop IL_001c: ldnull IL_001d: ret IL_001e: ldobj ""T"" IL_0023: box ""T"" IL_0028: call ""string Extension.GetValue(object)"" IL_002d: ret }"); } [Fact, WorkItem(40690, "https://github.com/dotnet/roslyn/issues/40690")] public void ConditionalAccess_InstanceMethod_ValueTuple() { var source = @" using System; public class Class<T> { public (long, T) Data { get; } public Class((long, T) data) { Data = data; } internal string Value { get { var q2 = Data.Item2?.ToString(); return q2; } } } class Program { static void Main(string[] args) { Console.WriteLine(new Class<string>((0, ""abc"")).Value); Console.WriteLine(new Class<string>((0, null)).Value); Console.WriteLine(new Class<int>((0, 0)).Value); Console.WriteLine(new Class<int?>((0, 0)).Value); Console.WriteLine(new Class<int?>((0, null)).Value); } }"; var verifier = CompileAndVerify(source, expectedOutput: @"abc 0 0 "); verifier.VerifyIL("Class<T>.Value.get", @" { // Code size 42 (0x2a) .maxstack 2 .locals init (System.ValueTuple<long, T> V_0) IL_0000: ldarg.0 IL_0001: call ""System.ValueTuple<long, T> Class<T>.Data.get"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: ldflda ""T System.ValueTuple<long, T>.Item2"" IL_000e: dup IL_000f: ldobj ""T"" IL_0014: box ""T"" IL_0019: brtrue.s IL_001e IL_001b: pop IL_001c: ldnull IL_001d: ret IL_001e: constrained. ""T"" IL_0024: callvirt ""string object.ToString()"" IL_0029: ret }"); } [Fact, WorkItem(66152, "https://github.com/dotnet/roslyn/issues/66152")] public void NullableSideEffects_01() { var source = @" struct S1 { private int count; public override string ToString() { return (++count).ToString(); } } class Program { static void Main() { var x1 = new S1?(new S1()); System.Console.Write(Test1(ref x1)); System.Console.Write(x1.ToString()); x1 = null; System.Console.Write(Test1(ref x1) is null); } static string Test1<T>(ref T x) { return x?.ToString(); } } "; var verifier = CompileAndVerify(source, expectedOutput: "11True").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", @" { // Code size 48 (0x30) .maxstack 2 .locals init (T V_0) IL_0000: ldarg.0 IL_0001: ldloca.s V_0 IL_0003: initobj ""T"" IL_0009: ldloc.0 IL_000a: box ""T"" IL_000f: brtrue.s IL_0024 IL_0011: ldobj ""T"" IL_0016: stloc.0 IL_0017: ldloca.s V_0 IL_0019: ldloc.0 IL_001a: box ""T"" IL_001f: brtrue.s IL_0024 IL_0021: pop IL_0022: ldnull IL_0023: ret IL_0024: constrained. ""T"" IL_002a: callvirt ""string object.ToString()"" IL_002f: ret } "); } [Fact, WorkItem(66152, "https://github.com/dotnet/roslyn/issues/66152")] public void NullableSideEffects_02() { var source = @" struct S1 { private int count; public override string ToString() { return (++count).ToString(); } } class Program { static void Main() { var x1 = new S1?(new S1()); System.Console.Write(Test1(ref x1)); System.Console.Write(x1.ToString()); x1 = null; System.Console.Write(Test1(ref x1) is null); } static string Test1<T>(ref T x) { var y = x; var result = y?.ToString(); x = y; return result; } } "; var verifier = CompileAndVerify(source, expectedOutput: "11True").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1<T>(ref T)", @" { // Code size 64 (0x40) .maxstack 3 .locals init (T V_0, //y T V_1) IL_0000: ldarg.0 IL_0001: ldobj ""T"" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: ldloca.s V_1 IL_000b: initobj ""T"" IL_0011: ldloc.1 IL_0012: box ""T"" IL_0017: brtrue.s IL_002d IL_0019: ldobj ""T"" IL_001e: stloc.1 IL_001f: ldloca.s V_1 IL_0021: ldloc.1 IL_0022: box ""T"" IL_0027: brtrue.s IL_002d IL_0029: pop IL_002a: ldnull IL_002b: br.s IL_0038 IL_002d: constrained. ""T"" IL_0033: callvirt ""string object.ToString()"" IL_0038: ldarg.0 IL_0039: ldloc.0 IL_003a: stobj ""T"" IL_003f: ret } "); } [Fact, WorkItem(66152, "https://github.com/dotnet/roslyn/issues/66152")] public void NullableSideEffects_03() { var source = @" struct S1 { } abstract class C0<U> { public abstract string Test1<T>(ref T x) where T : U; } class C1 : C0<S1?> { public override string Test1<T>(ref T x) { var y = x; var result = y?.ToString(); x = y; return result; } } "; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (16,23): error CS0023: Operator '?' cannot be applied to operand of type 'T' // var result = y?.ToString(); Diagnostic(ErrorCode.ERR_BadUnaryOp, "?").WithArguments("?", "T").WithLocation(16, 23) ); } } }