/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs
7 155 строк
218 KB
AlekseyTs
Ensure extension receiver is converted for pattern matching Deconstruct and pattern based `using` scenarios. (#81816)
06 янв 2026, 20:42
Не верифицирован
06 янв 2026, 20:42
7e962b9
Код
Авторство
О чём код?
// 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.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { [CompilerTrait(CompilerFeature.Patterns)] public class PatternTests : EmitMetadataTestBase { #region Miscellaneous [Fact, WorkItem(48493, "https://github.com/dotnet/roslyn/issues/48493")] public void Repro_48493() { var source = @" using System; using System.Linq; namespace Sample { internal class Row { public string Message { get; set; } = """"; } internal class Program { private static void Main() { Console.WriteLine(ProcessRow(new Row())); } private static string ProcessRow(Row row) { if (row == null) throw new ArgumentNullException(nameof(row)); return row switch { { Message: ""stringA"" } => ""stringB"", var r when new[] { ""stringC"", ""stringD"" }.Any(x => r.Message.Contains(x)) => ""stringE"", { Message: ""stringF"" } => ""stringG"", _ => ""stringH"", }; } } }"; CompileAndVerify(source, expectedOutput: "stringH"); } [Fact, WorkItem(48493, "https://github.com/dotnet/roslyn/issues/48493")] public void Repro_48493_Simple() { var source = @" using System; internal class Widget { public bool IsGood { get; set; } } internal class Program { private static bool M0(Func<bool> fn) => fn(); private static void Main() { Console.Write(new Widget() switch { { IsGood: true } => 1, _ when M0(() => true) => 2, { } => 3, }); } }"; CompileAndVerify(source, expectedOutput: @"2"); } [Fact, WorkItem(18811, "https://github.com/dotnet/roslyn/issues/18811")] public void MissingNullable_01() { var source = @"namespace System { public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { } public struct Int32 { } } static class C { public static bool M() => ((object)123) is int i; } "; var compilation = CreateEmptyCompilation(source, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.ReleaseDll); compilation.GetDiagnostics().Verify(); compilation.GetEmitDiagnostics().Verify( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1) ); } [Fact, WorkItem(18811, "https://github.com/dotnet/roslyn/issues/18811")] public void MissingNullable_02() { var source = @"namespace System { public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { } public struct Int32 { } public struct Nullable<T> where T : struct { } } static class C { public static bool M() => ((object)123) is int i; } "; var compilation = CreateEmptyCompilation(source, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.UnsafeReleaseDll); compilation.GetDiagnostics().Verify(); compilation.GetEmitDiagnostics().Verify( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion) ); } [Fact] public void MissingNullable_03() { var source = @"namespace System { public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { } public struct Int32 { } public struct Nullable<T> where T : struct { } } static class C { static void M1(int? x) { switch (x) { case int i: break; } } static bool M2(int? x) => x is int i; } "; var compilation = CreateEmptyCompilation(source, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.UnsafeReleaseDll); compilation.GetDiagnostics().Verify(); compilation.GetEmitDiagnostics().Verify( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // (14,18): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue' // case int i: break; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(14, 18), // (14,18): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault' // case int i: break; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "GetValueOrDefault").WithLocation(14, 18), // (14,18): error CS0656: Missing compiler required member 'System.Nullable`1.get_Value' // case int i: break; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_Value").WithLocation(14, 18), // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue' // static bool M2(int? x) => x is int i; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(17, 36), // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.GetValueOrDefault' // static bool M2(int? x) => x is int i; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "GetValueOrDefault").WithLocation(17, 36), // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_Value' // static bool M2(int? x) => x is int i; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_Value").WithLocation(17, 36) ); } [Fact] public void MissingNullable_04() { var source = @"namespace System { public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { } public struct Int32 { } public struct Nullable<T> where T : struct { public T GetValueOrDefault() => default(T); } } static class C { static void M1(int? x) { switch (x) { case int i: break; } } static bool M2(int? x) => x is int i; } "; var compilation = CreateEmptyCompilation(source, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.UnsafeReleaseDll); compilation.GetDiagnostics().Verify(); compilation.GetEmitDiagnostics().Verify( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // (14,18): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue' // case int i: break; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(14, 18), // (17,36): error CS0656: Missing compiler required member 'System.Nullable`1.get_HasValue' // static bool M2(int? x) => x is int i; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "int i").WithArguments("System.Nullable`1", "get_HasValue").WithLocation(17, 36) ); } [Fact, WorkItem(17266, "https://github.com/dotnet/roslyn/issues/17266")] public void DoubleEvaluation01() { var source = @"using System; public class C { public static void Main() { if (TryGet() is int index) { Console.WriteLine(index); } } public static int? TryGet() { Console.WriteLine(""eval""); return null; } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"eval"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.Main", @"{ // Code size 42 (0x2a) .maxstack 1 .locals init (int V_0, //index bool V_1, int? V_2) IL_0000: nop IL_0001: call ""int? C.TryGet()"" IL_0006: stloc.2 IL_0007: ldloca.s V_2 IL_0009: call ""bool int?.HasValue.get"" IL_000e: brfalse.s IL_001b IL_0010: ldloca.s V_2 IL_0012: call ""int int?.GetValueOrDefault()"" IL_0017: stloc.0 IL_0018: ldc.i4.1 IL_0019: br.s IL_001c IL_001b: ldc.i4.0 IL_001c: stloc.1 IL_001d: ldloc.1 IL_001e: brfalse.s IL_0029 IL_0020: nop IL_0021: ldloc.0 IL_0022: call ""void System.Console.WriteLine(int)"" IL_0027: nop IL_0028: nop IL_0029: ret }"); compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.Main", @"{ // Code size 30 (0x1e) .maxstack 1 .locals init (int V_0, //index int? V_1) IL_0000: call ""int? C.TryGet()"" IL_0005: stloc.1 IL_0006: ldloca.s V_1 IL_0008: call ""bool int?.HasValue.get"" IL_000d: brfalse.s IL_001d IL_000f: ldloca.s V_1 IL_0011: call ""int int?.GetValueOrDefault()"" IL_0016: stloc.0 IL_0017: ldloc.0 IL_0018: call ""void System.Console.WriteLine(int)"" IL_001d: ret }"); } [Fact, WorkItem(19122, "https://github.com/dotnet/roslyn/issues/19122")] public void PatternCrash_01() { var source = @"using System; using System.Collections.Generic; using System.Linq; public class Class2 : IDisposable { public Class2(bool parameter = false) { } public void Dispose() { } } class X<T> { IdentityAccessor<T> idAccessor = new IdentityAccessor<T>(); void Y<U>() where U : T { // BUG: The following line is the problem if (GetT().FirstOrDefault(p => idAccessor.GetId(p) == Guid.Empty) is U u) { } } IEnumerable<T> GetT() { yield return default(T); } } class IdentityAccessor<T> { public Guid GetId(T t) { return Guid.Empty; } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("X<T>.Y<U>", @"{ // Code size 61 (0x3d) .maxstack 3 .locals init (U V_0, //u bool V_1, T V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: call ""System.Collections.Generic.IEnumerable<T> X<T>.GetT()"" IL_0007: ldarg.0 IL_0008: ldftn ""bool X<T>.<Y>b__1_0<U>(T)"" IL_000e: newobj ""System.Func<T, bool>..ctor(object, System.IntPtr)"" IL_0013: call ""T System.Linq.Enumerable.FirstOrDefault<T>(System.Collections.Generic.IEnumerable<T>, System.Func<T, bool>)"" IL_0018: stloc.2 IL_0019: ldloc.2 IL_001a: box ""T"" IL_001f: isinst ""U"" IL_0024: brfalse.s IL_0035 IL_0026: ldloc.2 IL_0027: box ""T"" IL_002c: unbox.any ""U"" IL_0031: stloc.0 IL_0032: ldc.i4.1 IL_0033: br.s IL_0036 IL_0035: ldc.i4.0 IL_0036: stloc.1 IL_0037: ldloc.1 IL_0038: brfalse.s IL_003c IL_003a: nop IL_003b: nop IL_003c: ret }"); } [Fact, WorkItem(24522, "https://github.com/dotnet/roslyn/issues/24522")] public void IgnoreDeclaredConversion_01() { var source = @"class Base<T> { public static implicit operator Derived(Base<T> obj) { return new Derived(); } } class Derived : Base<object> { } class Program { static void Main(string[] args) { Base<object> x = new Derived(); System.Console.WriteLine(x is Derived); System.Console.WriteLine(x is Derived y); switch (x) { case Derived z: System.Console.WriteLine(true); break; } System.Console.WriteLine(null != (x as Derived)); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"True True True True"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("Program.Main", @"{ // Code size 84 (0x54) .maxstack 2 .locals init (Base<object> V_0, //x Derived V_1, //y Derived V_2, //z Base<object> V_3, Base<object> V_4) IL_0000: nop IL_0001: newobj ""Derived..ctor()"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: isinst ""Derived"" IL_000d: ldnull IL_000e: cgt.un IL_0010: call ""void System.Console.WriteLine(bool)"" IL_0015: nop IL_0016: ldloc.0 IL_0017: isinst ""Derived"" IL_001c: stloc.1 IL_001d: ldloc.1 IL_001e: ldnull IL_001f: cgt.un IL_0021: call ""void System.Console.WriteLine(bool)"" IL_0026: nop IL_0027: ldloc.0 IL_0028: stloc.s V_4 IL_002a: ldloc.s V_4 IL_002c: stloc.3 IL_002d: ldloc.3 IL_002e: isinst ""Derived"" IL_0033: stloc.2 IL_0034: ldloc.2 IL_0035: brtrue.s IL_0039 IL_0037: br.s IL_0044 IL_0039: br.s IL_003b IL_003b: ldc.i4.1 IL_003c: call ""void System.Console.WriteLine(bool)"" IL_0041: nop IL_0042: br.s IL_0044 IL_0044: ldloc.0 IL_0045: isinst ""Derived"" IL_004a: ldnull IL_004b: cgt.un IL_004d: call ""void System.Console.WriteLine(bool)"" IL_0052: nop IL_0053: ret }"); } [Fact] public void DoublePattern01() { var source = @"using System; class Program { static bool P1(double d) => d is double.NaN; static bool P2(float f) => f is float.NaN; static bool P3(double d) => d is 3.14d; static bool P4(float f) => f is 3.14f; static bool P5(object o) { switch (o) { case double.NaN: return true; case float.NaN: return true; case 3.14d: return true; case 3.14f: return true; default: return false; } } public static void Main(string[] args) { Console.Write(P1(double.NaN)); Console.Write(P1(1.0)); Console.Write(P2(float.NaN)); Console.Write(P2(1.0f)); Console.Write(P3(3.14)); Console.Write(P3(double.NaN)); Console.Write(P4(3.14f)); Console.Write(P4(float.NaN)); Console.Write(P5(double.NaN)); Console.Write(P5(0.0d)); Console.Write(P5(float.NaN)); Console.Write(P5(0.0f)); Console.Write(P5(3.14d)); Console.Write(P5(125)); Console.Write(P5(3.14f)); Console.Write(P5(1.0f)); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"TrueFalseTrueFalseTrueFalseTrueFalseTrueFalseTrueFalseTrueFalseTrueFalse"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("Program.P1", @"{ // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call ""bool double.IsNaN(double)"" IL_0006: ret }"); compVerifier.VerifyIL("Program.P2", @"{ // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call ""bool float.IsNaN(float)"" IL_0006: ret }"); compVerifier.VerifyIL("Program.P3", @"{ // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.r8 3.14 IL_000a: ceq IL_000c: ret }"); compVerifier.VerifyIL("Program.P4", @"{ // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.r4 3.14 IL_0006: ceq IL_0008: ret }"); compVerifier.VerifyIL("Program.P5", @"{ // Code size 103 (0x67) .maxstack 2 .locals init (object V_0, double V_1, float V_2, object V_3, bool V_4) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.3 IL_0003: ldloc.3 IL_0004: stloc.0 IL_0005: ldloc.0 IL_0006: isinst ""double"" IL_000b: brfalse.s IL_002a IL_000d: ldloc.0 IL_000e: unbox.any ""double"" IL_0013: stloc.1 IL_0014: ldloc.1 IL_0015: call ""bool double.IsNaN(double)"" IL_001a: brtrue.s IL_004b IL_001c: ldloc.1 IL_001d: ldc.r8 3.14 IL_0026: beq.s IL_0055 IL_0028: br.s IL_005f IL_002a: ldloc.0 IL_002b: isinst ""float"" IL_0030: brfalse.s IL_005f IL_0032: ldloc.0 IL_0033: unbox.any ""float"" IL_0038: stloc.2 IL_0039: ldloc.2 IL_003a: call ""bool float.IsNaN(float)"" IL_003f: brtrue.s IL_0050 IL_0041: ldloc.2 IL_0042: ldc.r4 3.14 IL_0047: beq.s IL_005a IL_0049: br.s IL_005f IL_004b: ldc.i4.1 IL_004c: stloc.s V_4 IL_004e: br.s IL_0064 IL_0050: ldc.i4.1 IL_0051: stloc.s V_4 IL_0053: br.s IL_0064 IL_0055: ldc.i4.1 IL_0056: stloc.s V_4 IL_0058: br.s IL_0064 IL_005a: ldc.i4.1 IL_005b: stloc.s V_4 IL_005d: br.s IL_0064 IL_005f: ldc.i4.0 IL_0060: stloc.s V_4 IL_0062: br.s IL_0064 IL_0064: ldloc.s V_4 IL_0066: ret }"); } [Fact] public void DecimalEquality() { // demonstrate that pattern-matching against a decimal constant is // at least as efficient as simply using == var source = @"using System; public class C { public static void Main() { Console.Write(M1(1.0m)); Console.Write(M2(1.0m)); Console.Write(M1(2.0m)); Console.Write(M2(2.0m)); } static int M1(decimal d) { if (M(d) is 1.0m) return 1; return 0; } static int M2(decimal d) { if (M(d) == 1.0m) return 1; return 0; } public static decimal M(decimal d) { return d; } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"1100"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @"{ // Code size 37 (0x25) .maxstack 6 .locals init (bool V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: call ""decimal C.M(decimal)"" IL_0007: ldc.i4.s 10 IL_0009: ldc.i4.0 IL_000a: ldc.i4.0 IL_000b: ldc.i4.0 IL_000c: ldc.i4.1 IL_000d: newobj ""decimal..ctor(int, int, int, bool, byte)"" IL_0012: call ""bool decimal.op_Equality(decimal, decimal)"" IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: brfalse.s IL_001f IL_001b: ldc.i4.1 IL_001c: stloc.1 IL_001d: br.s IL_0023 IL_001f: ldc.i4.0 IL_0020: stloc.1 IL_0021: br.s IL_0023 IL_0023: ldloc.1 IL_0024: ret }"); compVerifier.VerifyIL("C.M2", @"{ // Code size 37 (0x25) .maxstack 6 .locals init (bool V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: call ""decimal C.M(decimal)"" IL_0007: ldc.i4.s 10 IL_0009: ldc.i4.0 IL_000a: ldc.i4.0 IL_000b: ldc.i4.0 IL_000c: ldc.i4.1 IL_000d: newobj ""decimal..ctor(int, int, int, bool, byte)"" IL_0012: call ""bool decimal.op_Equality(decimal, decimal)"" IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: brfalse.s IL_001f IL_001b: ldc.i4.1 IL_001c: stloc.1 IL_001d: br.s IL_0023 IL_001f: ldc.i4.0 IL_0020: stloc.1 IL_0021: br.s IL_0023 IL_0023: ldloc.1 IL_0024: ret }"); compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @"{ // Code size 28 (0x1c) .maxstack 6 IL_0000: ldarg.0 IL_0001: call ""decimal C.M(decimal)"" IL_0006: ldc.i4.s 10 IL_0008: ldc.i4.0 IL_0009: ldc.i4.0 IL_000a: ldc.i4.0 IL_000b: ldc.i4.1 IL_000c: newobj ""decimal..ctor(int, int, int, bool, byte)"" IL_0011: call ""bool decimal.op_Equality(decimal, decimal)"" IL_0016: brfalse.s IL_001a IL_0018: ldc.i4.1 IL_0019: ret IL_001a: ldc.i4.0 IL_001b: ret }"); compVerifier.VerifyIL("C.M2", @"{ // Code size 28 (0x1c) .maxstack 6 IL_0000: ldarg.0 IL_0001: call ""decimal C.M(decimal)"" IL_0006: ldc.i4.s 10 IL_0008: ldc.i4.0 IL_0009: ldc.i4.0 IL_000a: ldc.i4.0 IL_000b: ldc.i4.1 IL_000c: newobj ""decimal..ctor(int, int, int, bool, byte)"" IL_0011: call ""bool decimal.op_Equality(decimal, decimal)"" IL_0016: brfalse.s IL_001a IL_0018: ldc.i4.1 IL_0019: ret IL_001a: ldc.i4.0 IL_001b: ret }"); } [Fact, WorkItem(16878, "https://github.com/dotnet/roslyn/issues/16878")] public void RedundantNullCheck() { var source = @"public class C { static int M1(bool? b1, bool b2) { switch (b1) { case null: return 1; case var _ when b2: return 2; case true: return 3; case false: return 4; } } static int M2(object o, bool b) { switch (o) { case string a when b: return 1; case string a: return 2; } return 3; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1", @"{ // Code size 35 (0x23) .maxstack 1 .locals init (bool? V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: call ""bool bool?.HasValue.get"" IL_0009: brfalse.s IL_0018 IL_000b: br.s IL_001a IL_000d: ldloca.s V_0 IL_000f: call ""bool bool?.GetValueOrDefault()"" IL_0014: brtrue.s IL_001f IL_0016: br.s IL_0021 IL_0018: ldc.i4.1 IL_0019: ret IL_001a: ldarg.1 IL_001b: brfalse.s IL_000d IL_001d: ldc.i4.2 IL_001e: ret IL_001f: ldc.i4.3 IL_0020: ret IL_0021: ldc.i4.4 IL_0022: ret }"); compVerifier.VerifyIL("C.M2", @"{ // Code size 19 (0x13) .maxstack 1 .locals init (string V_0) //a IL_0000: ldarg.0 IL_0001: isinst ""string"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0011 IL_000a: ldarg.1 IL_000b: brfalse.s IL_000f IL_000d: ldc.i4.1 IL_000e: ret IL_000f: ldc.i4.2 IL_0010: ret IL_0011: ldc.i4.3 IL_0012: ret }"); } [Fact, WorkItem(12813, "https://github.com/dotnet/roslyn/issues/12813")] public void NoBoxingOnIntegerConstantPattern() { var source = @"public class C { static bool M1(int x) { return x is 42; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1", @"{ // Code size 6 (0x6) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s 42 IL_0003: ceq IL_0005: ret }"); } [Fact, WorkItem(40403, "https://github.com/dotnet/roslyn/issues/40403")] public void RefParameter_StoreToTemp() { var source = @"public class C { static bool M1(ref object x) { return x is 42; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1", @"{ // Code size 24 (0x18) .maxstack 2 .locals init (object V_0) IL_0000: ldarg.0 IL_0001: ldind.ref IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: isinst ""int"" IL_0009: brfalse.s IL_0016 IL_000b: ldloc.0 IL_000c: unbox.any ""int"" IL_0011: ldc.i4.s 42 IL_0013: ceq IL_0015: ret IL_0016: ldc.i4.0 IL_0017: ret }"); } [Fact, WorkItem(40403, "https://github.com/dotnet/roslyn/issues/40403")] public void RefLocal_StoreToTemp() { var source = @"public class C { static bool M1(bool b, ref object x, ref object y) { ref object z = ref b ? ref x : ref y; return z is 42; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1", @"{ // Code size 30 (0x1e) .maxstack 2 .locals init (object V_0) IL_0000: ldarg.0 IL_0001: brtrue.s IL_0006 IL_0003: ldarg.2 IL_0004: br.s IL_0007 IL_0006: ldarg.1 IL_0007: ldind.ref IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: isinst ""int"" IL_000f: brfalse.s IL_001c IL_0011: ldloc.0 IL_0012: unbox.any ""int"" IL_0017: ldc.i4.s 42 IL_0019: ceq IL_001b: ret IL_001c: ldc.i4.0 IL_001d: ret }"); } [Fact, WorkItem(40403, "https://github.com/dotnet/roslyn/issues/40403")] public void InParameter_StoreToTemp() { var source = @"public class C { static bool M1(in object x) { return x is 42; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1", @"{ // Code size 24 (0x18) .maxstack 2 .locals init (object V_0) IL_0000: ldarg.0 IL_0001: ldind.ref IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: isinst ""int"" IL_0009: brfalse.s IL_0016 IL_000b: ldloc.0 IL_000c: unbox.any ""int"" IL_0011: ldc.i4.s 42 IL_0013: ceq IL_0015: ret IL_0016: ldc.i4.0 IL_0017: ret }"); } [Fact, WorkItem(40403, "https://github.com/dotnet/roslyn/issues/40403")] public void RefReadonlyLocal_StoreToTemp() { var source = @"public class C { static bool M1(bool b, in object x, in object y) { ref readonly object z = ref b ? ref x : ref y; return z is 42; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1", @"{ // Code size 30 (0x1e) .maxstack 2 .locals init (object V_0) IL_0000: ldarg.0 IL_0001: brtrue.s IL_0006 IL_0003: ldarg.2 IL_0004: br.s IL_0007 IL_0006: ldarg.1 IL_0007: ldind.ref IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: isinst ""int"" IL_000f: brfalse.s IL_001c IL_0011: ldloc.0 IL_0012: unbox.any ""int"" IL_0017: ldc.i4.s 42 IL_0019: ceq IL_001b: ret IL_001c: ldc.i4.0 IL_001d: ret }"); } [Fact, WorkItem(40403, "https://github.com/dotnet/roslyn/issues/40403")] public void OutParameter_StoreToTemp() { var source = @"public class C { static bool M1(out object x) { x = null; return x is 42; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1", @"{ // Code size 27 (0x1b) .maxstack 2 .locals init (object V_0) IL_0000: ldarg.0 IL_0001: ldnull IL_0002: stind.ref IL_0003: ldarg.0 IL_0004: ldind.ref IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: isinst ""int"" IL_000c: brfalse.s IL_0019 IL_000e: ldloc.0 IL_000f: unbox.any ""int"" IL_0014: ldc.i4.s 42 IL_0016: ceq IL_0018: ret IL_0019: ldc.i4.0 IL_001a: ret }"); } [Fact, WorkItem(22654, "https://github.com/dotnet/roslyn/issues/22654")] public void NoRedundantTypeCheck() { var source = @"using System; public class C { public void SwitchBasedPatternMatching(object o) { switch (o) { case int n when n == 1: Console.WriteLine(""1""); break; case string s: Console.WriteLine(""s""); break; case int n when n == 2: Console.WriteLine(""2""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.SwitchBasedPatternMatching", @"{ // Code size 69 (0x45) .maxstack 2 .locals init (int V_0, //n object V_1) IL_0000: ldarg.1 IL_0001: stloc.1 IL_0002: ldloc.1 IL_0003: isinst ""int"" IL_0008: brfalse.s IL_0013 IL_000a: ldloc.1 IL_000b: unbox.any ""int"" IL_0010: stloc.0 IL_0011: br.s IL_001c IL_0013: ldloc.1 IL_0014: isinst ""string"" IL_0019: brtrue.s IL_002b IL_001b: ret IL_001c: ldloc.0 IL_001d: ldc.i4.1 IL_001e: bne.un.s IL_0036 IL_0020: ldstr ""1"" IL_0025: call ""void System.Console.WriteLine(string)"" IL_002a: ret IL_002b: ldstr ""s"" IL_0030: call ""void System.Console.WriteLine(string)"" IL_0035: ret IL_0036: ldloc.0 IL_0037: ldc.i4.2 IL_0038: bne.un.s IL_0044 IL_003a: ldstr ""2"" IL_003f: call ""void System.Console.WriteLine(string)"" IL_0044: ret }"); } [Fact, WorkItem(15437, "https://github.com/dotnet/roslyn/issues/15437")] public void IsTypeDiscard() { var source = @"public class C { public bool IsString(object o) { return o is string _; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.IsString", @"{ // Code size 10 (0xa) .maxstack 2 IL_0000: ldarg.1 IL_0001: isinst ""string"" IL_0006: ldnull IL_0007: cgt.un IL_0009: ret }"); } [Fact, WorkItem(19150, "https://github.com/dotnet/roslyn/issues/19150")] public void RedundantHasValue() { var source = @"using System; public class C { public static void M(int? x) { switch (x) { case int i: Console.Write(i); break; case null: Console.Write(""null""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 33 (0x21) .maxstack 1 IL_0000: ldarga.s V_0 IL_0002: call ""bool int?.HasValue.get"" IL_0007: brfalse.s IL_0016 IL_0009: ldarga.s V_0 IL_000b: call ""int int?.GetValueOrDefault()"" IL_0010: call ""void System.Console.Write(int)"" IL_0015: ret IL_0016: ldstr ""null"" IL_001b: call ""void System.Console.Write(string)"" IL_0020: ret }"); } [Fact, WorkItem(19153, "https://github.com/dotnet/roslyn/issues/19153")] public void RedundantBox() { var source = @"using System; public class C { public static void M<T, U>(U x) where T : U { // when T is not known to be a reference type, there is an unboxing conversion from // a type parameter U to T, provided T depends on U. switch (x) { case T i: Console.Write(i); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M<T, U>(U)", @"{ // Code size 35 (0x23) .maxstack 1 IL_0000: ldarg.0 IL_0001: box ""U"" IL_0006: isinst ""T"" IL_000b: brfalse.s IL_0022 IL_000d: ldarg.0 IL_000e: box ""U"" IL_0013: unbox.any ""T"" IL_0018: box ""T"" IL_001d: call ""void System.Console.Write(object)"" IL_0022: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead01() { var source = @"using System; public class Person { public string Name { get; set; } } public class C { public void M(Person p) { switch (p) { case { Name: ""Bill"" }: Console.WriteLine(""Hey Bill!""); break; case { Name: ""Bob"" }: Console.WriteLine(""Hey Bob!""); break; case { Name: var name }: Console.WriteLine($""Hello {name}!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 82 (0x52) .maxstack 3 .locals init (string V_0) //name IL_0000: ldarg.1 IL_0001: brfalse.s IL_0051 IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: ldstr ""Bill"" IL_0010: call ""bool string.op_Equality(string, string)"" IL_0015: brtrue.s IL_0026 IL_0017: ldloc.0 IL_0018: ldstr ""Bob"" IL_001d: call ""bool string.op_Equality(string, string)"" IL_0022: brtrue.s IL_0031 IL_0024: br.s IL_003c IL_0026: ldstr ""Hey Bill!"" IL_002b: call ""void System.Console.WriteLine(string)"" IL_0030: ret IL_0031: ldstr ""Hey Bob!"" IL_0036: call ""void System.Console.WriteLine(string)"" IL_003b: ret IL_003c: ldstr ""Hello "" IL_0041: ldloc.0 IL_0042: ldstr ""!"" IL_0047: call ""string string.Concat(string, string, string)"" IL_004c: call ""void System.Console.WriteLine(string)"" IL_0051: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead02() { var source = @"using System; public class Person { public string Name { get; set; } public int Age; } public class C { public void M(Person p) { switch (p) { case Person { Name: var name, Age: 0}: Console.WriteLine($""Hello baby { name }!""); break; case Person { Name: var name }: Console.WriteLine($""Hello { name }!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 64 (0x40) .maxstack 3 .locals init (string V_0, //name string V_1) //name IL_0000: ldarg.1 IL_0001: brfalse.s IL_003f IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: stloc.0 IL_000a: ldarg.1 IL_000b: ldfld ""int Person.Age"" IL_0010: brtrue.s IL_0028 IL_0012: ldstr ""Hello baby "" IL_0017: ldloc.0 IL_0018: ldstr ""!"" IL_001d: call ""string string.Concat(string, string, string)"" IL_0022: call ""void System.Console.WriteLine(string)"" IL_0027: ret IL_0028: ldloc.0 IL_0029: stloc.1 IL_002a: ldstr ""Hello "" IL_002f: ldloc.1 IL_0030: ldstr ""!"" IL_0035: call ""string string.Concat(string, string, string)"" IL_003a: call ""void System.Console.WriteLine(string)"" IL_003f: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead03() { var source = @"using System; public class Person { public string Name { get; set; } } public class Student : Person { } public class C { public void M(Person p) { switch (p) { case { Name: ""Bill"" }: Console.WriteLine(""Hey Bill!""); break; case Student { Name: var name }: Console.WriteLine($""Hello student { name}!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 65 (0x41) .maxstack 3 .locals init (string V_0) //name IL_0000: ldarg.1 IL_0001: brfalse.s IL_0040 IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: ldstr ""Bill"" IL_0010: call ""bool string.op_Equality(string, string)"" IL_0015: brtrue.s IL_0020 IL_0017: ldarg.1 IL_0018: isinst ""Student"" IL_001d: brtrue.s IL_002b IL_001f: ret IL_0020: ldstr ""Hey Bill!"" IL_0025: call ""void System.Console.WriteLine(string)"" IL_002a: ret IL_002b: ldstr ""Hello student "" IL_0030: ldloc.0 IL_0031: ldstr ""!"" IL_0036: call ""string string.Concat(string, string, string)"" IL_003b: call ""void System.Console.WriteLine(string)"" IL_0040: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead04() { // Cannot combine the evaluations of name here, since we first check if p is Teacher, // and only if that fails check if p is null. // Combining the evaluations would mean first checking if p is null, then evaluating name, then checking if p is Teacher. // This would not necessarily be more performant. var source = @"using System; public class Person { public string Name { get; set; } } public class Teacher : Person { } public class C { public void M(Person p) { switch (p) { case Teacher { Name: var name }: Console.WriteLine($""Hello teacher { name}!""); break; case Person { Name: var name }: Console.WriteLine($""Hello { name}!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 75 (0x4b) .maxstack 3 .locals init (string V_0, //name string V_1) //name IL_0000: ldarg.1 IL_0001: isinst ""Teacher"" IL_0006: brfalse.s IL_0011 IL_0008: ldarg.1 IL_0009: callvirt ""string Person.Name.get"" IL_000e: stloc.0 IL_000f: br.s IL_001d IL_0011: ldarg.1 IL_0012: brfalse.s IL_004a IL_0014: ldarg.1 IL_0015: callvirt ""string Person.Name.get"" IL_001a: stloc.0 IL_001b: br.s IL_0033 IL_001d: ldstr ""Hello teacher "" IL_0022: ldloc.0 IL_0023: ldstr ""!"" IL_0028: call ""string string.Concat(string, string, string)"" IL_002d: call ""void System.Console.WriteLine(string)"" IL_0032: ret IL_0033: ldloc.0 IL_0034: stloc.1 IL_0035: ldstr ""Hello "" IL_003a: ldloc.1 IL_003b: ldstr ""!"" IL_0040: call ""string string.Concat(string, string, string)"" IL_0045: call ""void System.Console.WriteLine(string)"" IL_004a: ret } "); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead05() { // Cannot combine the evaluations of name here, since we first check if p is Teacher, // and only if that fails check if p is Student. // Combining the evaluations would mean first checking if p is null, // then evaluating name, // then checking if p is Teacher, // then checking if p is Student. // This would not necessarily be more performant. var source = @"using System; public class Person { public string Name { get; set; } } public class Student : Person { } public class Teacher : Person { } public class C { public void M(Person p) { switch (p) { case Teacher { Name: var name }: Console.WriteLine($""Hello teacher { name}!""); break; case Student { Name: var name }: Console.WriteLine($""Hello student { name}!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 80 (0x50) .maxstack 3 .locals init (string V_0, //name string V_1) //name IL_0000: ldarg.1 IL_0001: isinst ""Teacher"" IL_0006: brfalse.s IL_0011 IL_0008: ldarg.1 IL_0009: callvirt ""string Person.Name.get"" IL_000e: stloc.0 IL_000f: br.s IL_0022 IL_0011: ldarg.1 IL_0012: isinst ""Student"" IL_0017: brfalse.s IL_004f IL_0019: ldarg.1 IL_001a: callvirt ""string Person.Name.get"" IL_001f: stloc.0 IL_0020: br.s IL_0038 IL_0022: ldstr ""Hello teacher "" IL_0027: ldloc.0 IL_0028: ldstr ""!"" IL_002d: call ""string string.Concat(string, string, string)"" IL_0032: call ""void System.Console.WriteLine(string)"" IL_0037: ret IL_0038: ldloc.0 IL_0039: stloc.1 IL_003a: ldstr ""Hello student "" IL_003f: ldloc.1 IL_0040: ldstr ""!"" IL_0045: call ""string string.Concat(string, string, string)"" IL_004a: call ""void System.Console.WriteLine(string)"" IL_004f: ret } "); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead06() { var source = @"using System; public class Person { public string Name { get; set; } } public class Student : Person { } public class C { public void M(Person p) { switch (p) { case { Name: { Length: 0 } }: Console.WriteLine(""Hello!""); break; case Student { Name: { Length : 1 } }: Console.WriteLine($""Hello student!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 58 (0x3a) .maxstack 2 .locals init (string V_0, int V_1) IL_0000: ldarg.1 IL_0001: brfalse.s IL_0039 IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brfalse.s IL_0039 IL_000d: ldloc.0 IL_000e: callvirt ""int string.Length.get"" IL_0013: stloc.1 IL_0014: ldloc.1 IL_0015: brfalse.s IL_0024 IL_0017: ldarg.1 IL_0018: isinst ""Student"" IL_001d: brfalse.s IL_0039 IL_001f: ldloc.1 IL_0020: ldc.i4.1 IL_0021: beq.s IL_002f IL_0023: ret IL_0024: ldstr ""Hello!"" IL_0029: call ""void System.Console.WriteLine(string)"" IL_002e: ret IL_002f: ldstr ""Hello student!"" IL_0034: call ""void System.Console.WriteLine(string)"" IL_0039: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead07() { // Cannot combine the evaluations of name here, since we first check if name is MemoryStream, // and only if that fails check if name is null. // Combining the evaluations would mean first checking if name is null, then evaluating name, then checking if p is Teacher. // This would not necessarily be more performant. var source = @"using System; using System.IO; public class Person { public Stream Name { get; set; } } public class C { public void M(Person p) { switch (p) { case Person { Name: MemoryStream { Length: 1 } }: Console.WriteLine(""Your Names A MemoryStream!""); break; case Person { Name: { Length : var x } }: Console.WriteLine(""Your Names A Stream!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 66 (0x42) .maxstack 2 .locals init (System.IO.Stream V_0, System.IO.MemoryStream V_1) IL_0000: ldarg.1 IL_0001: brfalse.s IL_0041 IL_0003: ldarg.1 IL_0004: callvirt ""System.IO.Stream Person.Name.get"" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: isinst ""System.IO.MemoryStream"" IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: brfalse.s IL_0020 IL_0014: ldloc.1 IL_0015: callvirt ""long System.IO.Stream.Length.get"" IL_001a: ldc.i4.1 IL_001b: conv.i8 IL_001c: beq.s IL_002c IL_001e: br.s IL_0023 IL_0020: ldloc.0 IL_0021: brfalse.s IL_0041 IL_0023: ldloc.0 IL_0024: callvirt ""long System.IO.Stream.Length.get"" IL_0029: pop IL_002a: br.s IL_0037 IL_002c: ldstr ""Your Names A MemoryStream!"" IL_0031: call ""void System.Console.WriteLine(string)"" IL_0036: ret IL_0037: ldstr ""Your Names A Stream!"" IL_003c: call ""void System.Console.WriteLine(string)"" IL_0041: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead08() { var source = @" public class Person { public string Name { get; set; } } public class Student : Person { } public class C { public string M(Person p) { return p switch { { Name: ""Bill"" } => ""Hey Bill!"", Student { Name: var name } => ""Hello student { name}!"", _ => null, }; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 51 (0x33) .maxstack 2 .locals init (string V_0) IL_0000: ldarg.1 IL_0001: brfalse.s IL_002f IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: ldstr ""Bill"" IL_000e: call ""bool string.op_Equality(string, string)"" IL_0013: brtrue.s IL_001f IL_0015: ldarg.1 IL_0016: isinst ""Student"" IL_001b: brtrue.s IL_0027 IL_001d: br.s IL_002f IL_001f: ldstr ""Hey Bill!"" IL_0024: stloc.0 IL_0025: br.s IL_0031 IL_0027: ldstr ""Hello student { name}!"" IL_002c: stloc.0 IL_002d: br.s IL_0031 IL_002f: ldnull IL_0030: stloc.0 IL_0031: ldloc.0 IL_0032: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead09() { var source = @"using System; public class Person { public virtual string Name { get; set; } } public class Student : Person { } public class C { public void M(Person p) { switch (p) { case { Name: ""Bill"" }: Console.WriteLine(""Hey Bill!""); break; case Student { Name: var name }: Console.WriteLine($""Hello student { name}!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 65 (0x41) .maxstack 3 .locals init (string V_0) //name IL_0000: ldarg.1 IL_0001: brfalse.s IL_0040 IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: ldstr ""Bill"" IL_0010: call ""bool string.op_Equality(string, string)"" IL_0015: brtrue.s IL_0020 IL_0017: ldarg.1 IL_0018: isinst ""Student"" IL_001d: brtrue.s IL_002b IL_001f: ret IL_0020: ldstr ""Hey Bill!"" IL_0025: call ""void System.Console.WriteLine(string)"" IL_002a: ret IL_002b: ldstr ""Hello student "" IL_0030: ldloc.0 IL_0031: ldstr ""!"" IL_0036: call ""string string.Concat(string, string, string)"" IL_003b: call ""void System.Console.WriteLine(string)"" IL_0040: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void NoRedundantPropertyRead10() { // Currently we don't combine these redundant property reads. // However we could do so in the future. var source = @"using System; public abstract class Person { public abstract string Name { get; set; } } public class Student : Person { public override string Name { get; set; } } public class C { public void M(Person p) { switch (p) { case { Name: ""Bill"" }: Console.WriteLine(""Hey Bill!""); break; case Student { Name: var name }: Console.WriteLine($""Hello student { name}!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 73 (0x49) .maxstack 3 .locals init (string V_0, //name Student V_1) IL_0000: ldarg.1 IL_0001: brfalse.s IL_0048 IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: ldstr ""Bill"" IL_000e: call ""bool string.op_Equality(string, string)"" IL_0013: brtrue.s IL_0028 IL_0015: ldarg.1 IL_0016: isinst ""Student"" IL_001b: stloc.1 IL_001c: ldloc.1 IL_001d: brfalse.s IL_0048 IL_001f: ldloc.1 IL_0020: callvirt ""string Person.Name.get"" IL_0025: stloc.0 IL_0026: br.s IL_0033 IL_0028: ldstr ""Hey Bill!"" IL_002d: call ""void System.Console.WriteLine(string)"" IL_0032: ret IL_0033: ldstr ""Hello student "" IL_0038: ldloc.0 IL_0039: ldstr ""!"" IL_003e: call ""string string.Concat(string, string, string)"" IL_0043: call ""void System.Console.WriteLine(string)"" IL_0048: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void CombiningRedundantPropertyReadsDoesNotChangeNullabilityAnalysis01() { // Currently we don't combine the redundant property reads at all. // However this could be improved in the future so this test is important var source = @"using System; #nullable enable public class Person { public virtual string? Name { get; } } public class Student : Person { public override string Name { get => base.Name ?? """"; } } public class C { public void M(Person p) { switch (p) { case { Name: ""Bill""}: Console.WriteLine(""Hey Bill!""); break; case Student { Name: var name }: Console.WriteLine($""Student has name of length { name.Length }!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 78 (0x4e) .maxstack 2 .locals init (string V_0, //name Student V_1) IL_0000: ldarg.1 IL_0001: brfalse.s IL_004d IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: ldstr ""Bill"" IL_000e: call ""bool string.op_Equality(string, string)"" IL_0013: brtrue.s IL_0028 IL_0015: ldarg.1 IL_0016: isinst ""Student"" IL_001b: stloc.1 IL_001c: ldloc.1 IL_001d: brfalse.s IL_004d IL_001f: ldloc.1 IL_0020: callvirt ""string Person.Name.get"" IL_0025: stloc.0 IL_0026: br.s IL_0033 IL_0028: ldstr ""Hey Bill!"" IL_002d: call ""void System.Console.WriteLine(string)"" IL_0032: ret IL_0033: ldstr ""Student has name of length {0}!"" IL_0038: ldloc.0 IL_0039: callvirt ""int string.Length.get"" IL_003e: box ""int"" IL_0043: call ""string string.Format(string, object)"" IL_0048: call ""void System.Console.WriteLine(string)"" IL_004d: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void CombiningRedundantPropertyReadsDoesNotChangeNullabilityAnalysis02() { var source = @"using System; #nullable enable public class Person { public string? Name { get;} } public class Student : Person { } public class C { public void M(Person p) { switch (p) { case { Name: var name } when name is null: Console.WriteLine($""Hey { name }""); break; case Student { Name: var name } when name != null: Console.WriteLine($""Student has name of length { name.Length }!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 75 (0x4b) .maxstack 2 .locals init (string V_0, //name string V_1, //name Person V_2) IL_0000: ldarg.1 IL_0001: stloc.2 IL_0002: ldloc.2 IL_0003: brfalse.s IL_004a IL_0005: ldloc.2 IL_0006: callvirt ""string Person.Name.get"" IL_000b: stloc.0 IL_000c: br.s IL_0017 IL_000e: ldloc.2 IL_000f: isinst ""Student"" IL_0014: brtrue.s IL_002b IL_0016: ret IL_0017: ldloc.0 IL_0018: brtrue.s IL_000e IL_001a: ldstr ""Hey "" IL_001f: ldloc.0 IL_0020: call ""string string.Concat(string, string)"" IL_0025: call ""void System.Console.WriteLine(string)"" IL_002a: ret IL_002b: ldloc.0 IL_002c: stloc.1 IL_002d: ldloc.1 IL_002e: brfalse.s IL_004a IL_0030: ldstr ""Student has name of length {0}!"" IL_0035: ldloc.1 IL_0036: callvirt ""int string.Length.get"" IL_003b: box ""int"" IL_0040: call ""string string.Format(string, object)"" IL_0045: call ""void System.Console.WriteLine(string)"" IL_004a: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void CombiningRedundantPropertyReadsDoesNotChangeNullabilityAnalysis03() { var source = @"using System; #nullable enable public class Person { public string? Name { get; } } public class Student : Person { } public class C { public void M(Person p) { switch (p) { case { Name: string name }: Console.WriteLine($""Hey {name}""); break; case Student { Name: var name }: Console.WriteLine($""Student has name of length { name.Length }!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics( // (19,66): warning CS8602: Dereference of a possibly null reference. // Console.WriteLine($"Student has name of length { name.Length }!"); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "name").WithLocation(19, 66)); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 68 (0x44) .maxstack 2 .locals init (string V_0, //name string V_1) //name IL_0000: ldarg.1 IL_0001: brfalse.s IL_0043 IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: brtrue.s IL_0016 IL_000d: ldarg.1 IL_000e: isinst ""Student"" IL_0013: brtrue.s IL_0027 IL_0015: ret IL_0016: ldstr ""Hey "" IL_001b: ldloc.0 IL_001c: call ""string string.Concat(string, string)"" IL_0021: call ""void System.Console.WriteLine(string)"" IL_0026: ret IL_0027: ldloc.0 IL_0028: stloc.1 IL_0029: ldstr ""Student has name of length {0}!"" IL_002e: ldloc.1 IL_002f: callvirt ""int string.Length.get"" IL_0034: box ""int"" IL_0039: call ""string string.Format(string, object)"" IL_003e: call ""void System.Console.WriteLine(string)"" IL_0043: ret }"); } [Fact, WorkItem(34933, "https://github.com/dotnet/roslyn/issues/34933")] public void DoNotCombineDifferentPropertyReadsWithSameName() { var source = @"using System; public class Person { public string Name { get; set; } } public class Student : Person { public new string Name { get; set; } } public class C { public void M(Person p) { switch (p) { case { Name: ""Bill"" }: Console.WriteLine(""Hey Bill!""); break; case Student { Name: var name }: Console.WriteLine($""Hello student { name}!""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M", @"{ // Code size 73 (0x49) .maxstack 3 .locals init (string V_0, //name Student V_1) IL_0000: ldarg.1 IL_0001: brfalse.s IL_0048 IL_0003: ldarg.1 IL_0004: callvirt ""string Person.Name.get"" IL_0009: ldstr ""Bill"" IL_000e: call ""bool string.op_Equality(string, string)"" IL_0013: brtrue.s IL_0028 IL_0015: ldarg.1 IL_0016: isinst ""Student"" IL_001b: stloc.1 IL_001c: ldloc.1 IL_001d: brfalse.s IL_0048 IL_001f: ldloc.1 IL_0020: callvirt ""string Student.Name.get"" IL_0025: stloc.0 IL_0026: br.s IL_0033 IL_0028: ldstr ""Hey Bill!"" IL_002d: call ""void System.Console.WriteLine(string)"" IL_0032: ret IL_0033: ldstr ""Hello student "" IL_0038: ldloc.0 IL_0039: ldstr ""!"" IL_003e: call ""string string.Concat(string, string, string)"" IL_0043: call ""void System.Console.WriteLine(string)"" IL_0048: ret }"); } [Fact, WorkItem(51801, "https://github.com/dotnet/roslyn/issues/51801")] public void PropertyOverrideLacksAccessor() { var source = @" #nullable enable class Base { public virtual bool IsOk { get { return true; } set { } } } class C : Base { public override bool IsOk { set { } } public string? Value { get; } public string M() { switch (this) { case { IsOk: true }: return Value; default: return Value; } } } "; var verifier = CompileAndVerify(source); verifier.VerifyIL("C.M", @" { // Code size 26 (0x1a) .maxstack 1 .locals init (C V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: brfalse.s IL_0013 IL_0005: ldloc.0 IL_0006: callvirt ""bool Base.IsOk.get"" IL_000b: pop IL_000c: ldarg.0 IL_000d: call ""string C.Value.get"" IL_0012: ret IL_0013: ldarg.0 IL_0014: call ""string C.Value.get"" IL_0019: ret }"); } [Fact, WorkItem(20641, "https://github.com/dotnet/roslyn/issues/20641")] public void PatternsVsAs01() { var source = @"using System.Collections; using System.Collections.Generic; class Program { static void Main() { } internal static bool TryGetCount1<T>(IEnumerable<T> source, out int count) { ICollection nonGeneric = source as ICollection; if (nonGeneric != null) { count = nonGeneric.Count; return true; } ICollection<T> generic = source as ICollection<T>; if (generic != null) { count = generic.Count; return true; } count = -1; return false; } internal static bool TryGetCount2<T>(IEnumerable<T> source, out int count) { switch (source) { case ICollection nonGeneric: count = nonGeneric.Count; return true; case ICollection<T> generic: count = generic.Count; return true; default: count = -1; return false; } } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("Program.TryGetCount1<T>", @"{ // Code size 45 (0x2d) .maxstack 2 .locals init (System.Collections.ICollection V_0, //nonGeneric System.Collections.Generic.ICollection<T> V_1) //generic IL_0000: ldarg.0 IL_0001: isinst ""System.Collections.ICollection"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0014 IL_000a: ldarg.1 IL_000b: ldloc.0 IL_000c: callvirt ""int System.Collections.ICollection.Count.get"" IL_0011: stind.i4 IL_0012: ldc.i4.1 IL_0013: ret IL_0014: ldarg.0 IL_0015: isinst ""System.Collections.Generic.ICollection<T>"" IL_001a: stloc.1 IL_001b: ldloc.1 IL_001c: brfalse.s IL_0028 IL_001e: ldarg.1 IL_001f: ldloc.1 IL_0020: callvirt ""int System.Collections.Generic.ICollection<T>.Count.get"" IL_0025: stind.i4 IL_0026: ldc.i4.1 IL_0027: ret IL_0028: ldarg.1 IL_0029: ldc.i4.m1 IL_002a: stind.i4 IL_002b: ldc.i4.0 IL_002c: ret }"); compVerifier.VerifyIL("Program.TryGetCount2<T>", @"{ // Code size 47 (0x2f) .maxstack 2 .locals init (System.Collections.ICollection V_0, //nonGeneric System.Collections.Generic.ICollection<T> V_1) //generic IL_0000: ldarg.0 IL_0001: isinst ""System.Collections.ICollection"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brtrue.s IL_0016 IL_000a: ldarg.0 IL_000b: isinst ""System.Collections.Generic.ICollection<T>"" IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: brtrue.s IL_0020 IL_0014: br.s IL_002a IL_0016: ldarg.1 IL_0017: ldloc.0 IL_0018: callvirt ""int System.Collections.ICollection.Count.get"" IL_001d: stind.i4 IL_001e: ldc.i4.1 IL_001f: ret IL_0020: ldarg.1 IL_0021: ldloc.1 IL_0022: callvirt ""int System.Collections.Generic.ICollection<T>.Count.get"" IL_0027: stind.i4 IL_0028: ldc.i4.1 IL_0029: ret IL_002a: ldarg.1 IL_002b: ldc.i4.m1 IL_002c: stind.i4 IL_002d: ldc.i4.0 IL_002e: ret }"); } [Fact, WorkItem(20641, "https://github.com/dotnet/roslyn/issues/20641")] public void PatternsVsAs02() { var source = @"using System.Collections; class Program { static void Main() { } internal static bool IsEmpty1(IEnumerable source) { var c = source as ICollection; return c != null && c.Count > 0; } internal static bool IsEmpty2(IEnumerable source) { return source is ICollection c && c.Count > 0; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("Program.IsEmpty1", @"{ // Code size 22 (0x16) .maxstack 2 .locals init (System.Collections.ICollection V_0) //c IL_0000: ldarg.0 IL_0001: isinst ""System.Collections.ICollection"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0014 IL_000a: ldloc.0 IL_000b: callvirt ""int System.Collections.ICollection.Count.get"" IL_0010: ldc.i4.0 IL_0011: cgt IL_0013: ret IL_0014: ldc.i4.0 IL_0015: ret }"); compVerifier.VerifyIL("Program.IsEmpty2", @"{ // Code size 22 (0x16) .maxstack 2 .locals init (System.Collections.ICollection V_0) //c IL_0000: ldarg.0 IL_0001: isinst ""System.Collections.ICollection"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0014 IL_000a: ldloc.0 IL_000b: callvirt ""int System.Collections.ICollection.Count.get"" IL_0010: ldc.i4.0 IL_0011: cgt IL_0013: ret IL_0014: ldc.i4.0 IL_0015: ret }"); } [Fact] [WorkItem(20641, "https://github.com/dotnet/roslyn/issues/20641")] [WorkItem(1395, "https://github.com/dotnet/csharplang/issues/1395")] public void TupleSwitch01() { var source = @"using System; public class Door { public DoorState State; public enum DoorState { Opened, Closed, Locked } public enum Action { Open, Close, Lock, Unlock } public void Act0(Action action, bool haveKey = false) { Console.Write($""{State} {action}{(haveKey ? "" withKey"" : null)}""); State = ChangeState0(State, action, haveKey); Console.WriteLine($"" -> {State}""); } public void Act1(Action action, bool haveKey = false) { Console.Write($""{State} {action}{(haveKey ? "" withKey"" : null)}""); State = ChangeState1(State, action, haveKey); Console.WriteLine($"" -> {State}""); } public static DoorState ChangeState0(DoorState state, Action action, bool haveKey = false) { switch (state, action) { case (DoorState.Opened, Action.Close): return DoorState.Closed; case (DoorState.Closed, Action.Open): return DoorState.Opened; case (DoorState.Closed, Action.Lock) when haveKey: return DoorState.Locked; case (DoorState.Locked, Action.Unlock) when haveKey: return DoorState.Closed; case var (oldState, _): return oldState; } } public static DoorState ChangeState1(DoorState state, Action action, bool haveKey = false) => (state, action) switch { (DoorState.Opened, Action.Close) => DoorState.Closed, (DoorState.Closed, Action.Open) => DoorState.Opened, (DoorState.Closed, Action.Lock) when haveKey => DoorState.Locked, (DoorState.Locked, Action.Unlock) when haveKey => DoorState.Closed, _ => state }; } class Program { static void Main(string[] args) { var door = new Door(); door.Act0(Door.Action.Close); door.Act0(Door.Action.Lock); door.Act0(Door.Action.Lock, true); door.Act0(Door.Action.Open); door.Act0(Door.Action.Unlock); door.Act0(Door.Action.Unlock, true); door.Act0(Door.Action.Open); Console.WriteLine(); door = new Door(); door.Act1(Door.Action.Close); door.Act1(Door.Action.Lock); door.Act1(Door.Action.Lock, true); door.Act1(Door.Action.Open); door.Act1(Door.Action.Unlock); door.Act1(Door.Action.Unlock, true); door.Act1(Door.Action.Open); } }"; var expectedOutput = @"Opened Close -> Closed Closed Lock -> Closed Closed Lock withKey -> Locked Locked Open -> Locked Locked Unlock -> Locked Locked Unlock withKey -> Closed Closed Open -> Opened Opened Close -> Closed Closed Lock -> Closed Closed Lock withKey -> Locked Locked Open -> Locked Locked Unlock -> Locked Locked Unlock withKey -> Closed Closed Open -> Opened "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithRecursivePatterns); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("Door.ChangeState0", @"{ // Code size 61 (0x3d) .maxstack 2 .locals init (Door.DoorState V_0, //oldState Door.Action V_1) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldarg.1 IL_0003: stloc.1 IL_0004: ldloc.0 IL_0005: switch ( IL_0018, IL_001e, IL_0027) IL_0016: br.s IL_003b IL_0018: ldloc.1 IL_0019: ldc.i4.1 IL_001a: beq.s IL_002d IL_001c: br.s IL_003b IL_001e: ldloc.1 IL_001f: brfalse.s IL_002f IL_0021: ldloc.1 IL_0022: ldc.i4.2 IL_0023: beq.s IL_0031 IL_0025: br.s IL_003b IL_0027: ldloc.1 IL_0028: ldc.i4.3 IL_0029: beq.s IL_0036 IL_002b: br.s IL_003b IL_002d: ldc.i4.1 IL_002e: ret IL_002f: ldc.i4.0 IL_0030: ret IL_0031: ldarg.2 IL_0032: brfalse.s IL_003b IL_0034: ldc.i4.2 IL_0035: ret IL_0036: ldarg.2 IL_0037: brfalse.s IL_003b IL_0039: ldc.i4.1 IL_003a: ret IL_003b: ldloc.0 IL_003c: ret }"); compVerifier.VerifyIL("Door.ChangeState1", @"{ // Code size 71 (0x47) .maxstack 2 .locals init (Door.DoorState V_0, Door.DoorState V_1, Door.Action V_2) IL_0000: ldarg.0 IL_0001: stloc.1 IL_0002: ldarg.1 IL_0003: stloc.2 IL_0004: ldloc.1 IL_0005: switch ( IL_0018, IL_001e, IL_0027) IL_0016: br.s IL_0043 IL_0018: ldloc.2 IL_0019: ldc.i4.1 IL_001a: beq.s IL_002d IL_001c: br.s IL_0043 IL_001e: ldloc.2 IL_001f: brfalse.s IL_0031 IL_0021: ldloc.2 IL_0022: ldc.i4.2 IL_0023: beq.s IL_0035 IL_0025: br.s IL_0043 IL_0027: ldloc.2 IL_0028: ldc.i4.3 IL_0029: beq.s IL_003c IL_002b: br.s IL_0043 IL_002d: ldc.i4.1 IL_002e: stloc.0 IL_002f: br.s IL_0045 IL_0031: ldc.i4.0 IL_0032: stloc.0 IL_0033: br.s IL_0045 IL_0035: ldarg.2 IL_0036: brfalse.s IL_0043 IL_0038: ldc.i4.2 IL_0039: stloc.0 IL_003a: br.s IL_0045 IL_003c: ldarg.2 IL_003d: brfalse.s IL_0043 IL_003f: ldc.i4.1 IL_0040: stloc.0 IL_0041: br.s IL_0045 IL_0043: ldarg.0 IL_0044: stloc.0 IL_0045: ldloc.0 IL_0046: ret }"); } [Fact] [WorkItem(20641, "https://github.com/dotnet/roslyn/issues/20641")] [WorkItem(1395, "https://github.com/dotnet/csharplang/issues/1395")] public void SharingTemps01() { var source = @"class Program { static void Main(string[] args) { } void M1(string x) { switch (x) { case ""a"": case ""b"" when Mutate(ref x): // prevents sharing temps case ""c"": break; } } void M2(string x) { switch (x) { case ""a"": case ""b"" when Pure(x): case ""c"": break; } } static bool Mutate(ref string x) { x = null; return false; } static bool Pure(string x) { return false; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithoutRecursivePatterns); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("Program.M1", @"{ // Code size 50 (0x32) .maxstack 2 .locals init (string V_0) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldstr ""a"" IL_0008: call ""bool string.op_Equality(string, string)"" IL_000d: brtrue.s IL_0031 IL_000f: ldloc.0 IL_0010: ldstr ""b"" IL_0015: call ""bool string.op_Equality(string, string)"" IL_001a: brtrue.s IL_0029 IL_001c: ldloc.0 IL_001d: ldstr ""c"" IL_0022: call ""bool string.op_Equality(string, string)"" IL_0027: pop IL_0028: ret IL_0029: ldarga.s V_1 IL_002b: call ""bool Program.Mutate(ref string)"" IL_0030: pop IL_0031: ret }"); compVerifier.VerifyIL("Program.M2", @"{ // Code size 49 (0x31) .maxstack 2 .locals init (string V_0) IL_0000: ldarg.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldstr ""a"" IL_0008: call ""bool string.op_Equality(string, string)"" IL_000d: brtrue.s IL_0030 IL_000f: ldloc.0 IL_0010: ldstr ""b"" IL_0015: call ""bool string.op_Equality(string, string)"" IL_001a: brtrue.s IL_0029 IL_001c: ldloc.0 IL_001d: ldstr ""c"" IL_0022: call ""bool string.op_Equality(string, string)"" IL_0027: pop IL_0028: ret IL_0029: ldarg.1 IL_002a: call ""bool Program.Pure(string)"" IL_002f: pop IL_0030: ret }"); } [Fact, WorkItem(17266, "https://github.com/dotnet/roslyn/issues/17266")] public void IrrefutablePatternInIs01() { var source = @"using System; public class C { public static void Main() { if (Get() is int index) { } Console.WriteLine(index); } public static int Get() { Console.WriteLine(""eval""); return 1; } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"eval 1"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact, WorkItem(17266, "https://github.com/dotnet/roslyn/issues/17266")] public void IrrefutablePatternInIs02() { var source = @"using System; public class C { public static void Main() { if (Get() is Assignment(int left, var right)) { } Console.WriteLine(left); Console.WriteLine(right); } public static Assignment Get() { Console.WriteLine(""eval""); return new Assignment(1, 2); } } public struct Assignment { public int Left, Right; public Assignment(int left, int right) => (Left, Right) = (left, right); public void Deconstruct(out int left, out int right) => (left, right) = (Left, Right); } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns); compilation.VerifyDiagnostics(); var expectedOutput = @"eval 1 2"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void MissingNullCheck01() { var source = @"class Program { public static void Main() { string s = null; System.Console.WriteLine(s is string { Length: 3 }); } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithRecursivePatterns); compilation.VerifyDiagnostics(); var expectedOutput = @"False"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] [WorkItem(24550, "https://github.com/dotnet/roslyn/issues/24550")] [WorkItem(1284, "https://github.com/dotnet/csharplang/issues/1284")] public void ConstantPatternVsUnconstrainedTypeParameter01() { var source = @"using System; class Program { static void Main() { Console.WriteLine(Test1<object>(null)); Console.WriteLine(Test1<int>(1)); Console.WriteLine(Test1<int?>(null)); Console.WriteLine(Test1<int?>(1)); Console.WriteLine(Test2<object>(0)); Console.WriteLine(Test2<int>(1)); Console.WriteLine(Test2<int?>(0)); Console.WriteLine(Test2<string>(""frog"")); Console.WriteLine(Test3<object>(""frog"")); Console.WriteLine(Test3<int>(1)); Console.WriteLine(Test3<string>(""frog"")); Console.WriteLine(Test3<int?>(1)); } public static bool Test1<T>(T t) { return t is null; } public static bool Test2<T>(T t) { return t is 0; } public static bool Test3<T>(T t) { return t is ""frog""; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"True False True False True False True False True False True False "; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("Program.Test1<T>(T)", @"{ // Code size 10 (0xa) .maxstack 2 IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: ldnull IL_0007: ceq IL_0009: ret }"); compVerifier.VerifyIL("Program.Test2<T>(T)", @"{ // Code size 35 (0x23) .maxstack 2 IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: isinst ""int"" IL_000b: brfalse.s IL_0021 IL_000d: ldarg.0 IL_000e: box ""T"" IL_0013: isinst ""int"" IL_0018: unbox.any ""int"" IL_001d: ldc.i4.0 IL_001e: ceq IL_0020: ret IL_0021: ldc.i4.0 IL_0022: ret } "); compVerifier.VerifyIL("Program.Test3<T>(T)", @"{ // Code size 29 (0x1d) .maxstack 2 .locals init (string V_0) IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: isinst ""string"" IL_000b: stloc.0 IL_000c: ldloc.0 IL_000d: brfalse.s IL_001b IL_000f: ldloc.0 IL_0010: ldstr ""frog"" IL_0015: call ""bool string.op_Equality(string, string)"" IL_001a: ret IL_001b: ldc.i4.0 IL_001c: ret }"); } [Fact] [WorkItem(24550, "https://github.com/dotnet/roslyn/issues/24550")] [WorkItem(1284, "https://github.com/dotnet/csharplang/issues/1284")] public void ConstantPatternVsUnconstrainedTypeParameter02() { var source = @"class C<T> { internal struct S { } static bool Test(S s) { return s is 1; } }"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C<T>.Test(C<T>.S)", @"{ // Code size 35 (0x23) .maxstack 2 IL_0000: ldarg.0 IL_0001: box ""C<T>.S"" IL_0006: isinst ""int"" IL_000b: brfalse.s IL_0021 IL_000d: ldarg.0 IL_000e: box ""C<T>.S"" IL_0013: isinst ""int"" IL_0018: unbox.any ""int"" IL_001d: ldc.i4.1 IL_001e: ceq IL_0020: ret IL_0021: ldc.i4.0 IL_0022: ret } "); } [Fact] [WorkItem(26274, "https://github.com/dotnet/roslyn/issues/26274")] public void VariablesInSwitchExpressionArms() { var source = @"class C { public override bool Equals(object obj) => obj switch { C x1 when x1 is var x2 => x2 is var x3 && x3 is {}, _ => false }; public override int GetHashCode() => 1; public static void Main() { C c = new C(); System.Console.Write(c.Equals(new C())); System.Console.Write(c.Equals(new object())); } } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: "TrueFalse"); compVerifier.VerifyIL("C.Equals(object)", @"{ // Code size 25 (0x19) .maxstack 2 .locals init (C V_0, //x1 C V_1, //x2 C V_2, //x3 bool V_3) IL_0000: ldarg.1 IL_0001: isinst ""C"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0015 IL_000a: ldloc.0 IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: stloc.2 IL_000e: ldloc.2 IL_000f: ldnull IL_0010: cgt.un IL_0012: stloc.3 IL_0013: br.s IL_0017 IL_0015: ldc.i4.0 IL_0016: stloc.3 IL_0017: ldloc.3 IL_0018: ret }"); } [Fact, WorkItem(26387, "https://github.com/dotnet/roslyn/issues/26387")] public void ValueTypeArgument01() { var source = @"using System; class TestHelper { static void Main() { Console.WriteLine(IsValueTypeT0(new S())); Console.WriteLine(IsValueTypeT1(new S())); Console.WriteLine(IsValueTypeT2(new S())); } static bool IsValueTypeT0<T>(T result) { return result is T; } static bool IsValueTypeT1<T>(T result) { return result is T v; } static bool IsValueTypeT2<T>(T result) { return result is T _; } } struct S { } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"True True True"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("TestHelper.IsValueTypeT0<T>(T)", @"{ // Code size 15 (0xf) .maxstack 2 .locals init (bool V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: box ""T"" IL_0007: ldnull IL_0008: cgt.un IL_000a: stloc.0 IL_000b: br.s IL_000d IL_000d: ldloc.0 IL_000e: ret }"); compVerifier.VerifyIL("TestHelper.IsValueTypeT1<T>(T)", @"{ // Code size 20 (0x14) .maxstack 1 .locals init (T V_0, //v bool V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: box ""T"" IL_0007: brfalse.s IL_000e IL_0009: ldarg.0 IL_000a: stloc.0 IL_000b: ldc.i4.1 IL_000c: br.s IL_000f IL_000e: ldc.i4.0 IL_000f: stloc.1 IL_0010: br.s IL_0012 IL_0012: ldloc.1 IL_0013: ret }"); compVerifier.VerifyIL("TestHelper.IsValueTypeT2<T>(T)", @"{ // Code size 15 (0xf) .maxstack 2 .locals init (bool V_0) IL_0000: nop IL_0001: ldarg.0 IL_0002: box ""T"" IL_0007: ldnull IL_0008: cgt.un IL_000a: stloc.0 IL_000b: br.s IL_000d IL_000d: ldloc.0 IL_000e: ret }"); } [Fact, WorkItem(26387, "https://github.com/dotnet/roslyn/issues/26387")] public void ValueTypeArgument02() { var source = @"namespace ConsoleApp1 { public class TestHelper { public static void Main() { IsValueTypeT(new Result<(Cls, IInt)>((new Cls(), new Int()))); System.Console.WriteLine(""done""); } public static void IsValueTypeT<T>(Result<T> result) { if (!(result.Value is T v)) throw new NotPossibleException(); } } public class Result<T> { public T Value { get; } public Result(T value) { Value = value; } } public class Cls { } public interface IInt { } public class Int : IInt { } public class NotPossibleException : System.Exception { } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"done"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("ConsoleApp1.TestHelper.IsValueTypeT<T>(ConsoleApp1.Result<T>)", @"{ // Code size 31 (0x1f) .maxstack 2 .locals init (T V_0, //v bool V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: callvirt ""T ConsoleApp1.Result<T>.Value.get"" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: box ""T"" IL_000e: ldnull IL_000f: cgt.un IL_0011: ldc.i4.0 IL_0012: ceq IL_0014: stloc.1 IL_0015: ldloc.1 IL_0016: brfalse.s IL_001e IL_0018: newobj ""ConsoleApp1.NotPossibleException..ctor()"" IL_001d: throw IL_001e: ret }"); } [Fact] public void DeconstructNullableTuple_01() { var source = @" class C { static int i = 3; static (int,int)? GetNullableTuple() => (i++, i++); static void Main() { if (GetNullableTuple() is (int x1, int y1) tupl1) { System.Console.WriteLine($""x = {x1}, y = {y1}""); } if (GetNullableTuple() is (int x2, int y2) _) { System.Console.WriteLine($""x = {x2}, y = {y2}""); } switch (GetNullableTuple()) { case (int x3, int y3) s: System.Console.WriteLine($""x = {x3}, y = {y3}""); break; } } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"x = 3, y = 4 x = 5, y = 6 x = 7, y = 8"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void DeconstructNullable_01() { var source = @" class C { static int i = 3; static S? GetNullableTuple() => new S(i++, i++); static void Main() { if (GetNullableTuple() is (int x1, int y1) tupl1) { System.Console.WriteLine($""x = {x1}, y = {y1}""); } if (GetNullableTuple() is (int x2, int y2) _) { System.Console.WriteLine($""x = {x2}, y = {y2}""); } switch (GetNullableTuple()) { case (int x3, int y3) s: System.Console.WriteLine($""x = {x3}, y = {y3}""); break; } } } struct S { int x, y; public S(int X, int Y) => (this.x, this.y) = (X, Y); public void Deconstruct(out int X, out int Y) => (X, Y) = (x, y); } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"x = 3, y = 4 x = 5, y = 6 x = 7, y = 8"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact, WorkItem(28632, "https://github.com/dotnet/roslyn/issues/28632")] public void UnboxNullableInRecursivePattern01() { var source = @" static class Program { public static int M1(int? x) { return x is int y ? y : 1; } public static int M2(int? x) { return x is { } y ? y : 2; } public static void Main() { System.Console.WriteLine(M1(null)); System.Console.WriteLine(M2(null)); System.Console.WriteLine(M1(3)); System.Console.WriteLine(M2(4)); } } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"1 2 3 4"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("Program.M1", @"{ // Code size 23 (0x17) .maxstack 1 .locals init (int V_0) //y IL_0000: ldarga.s V_0 IL_0002: call ""bool int?.HasValue.get"" IL_0007: brfalse.s IL_0013 IL_0009: ldarga.s V_0 IL_000b: call ""int int?.GetValueOrDefault()"" IL_0010: stloc.0 IL_0011: br.s IL_0015 IL_0013: ldc.i4.1 IL_0014: ret IL_0015: ldloc.0 IL_0016: ret }"); compVerifier.VerifyIL("Program.M2", @"{ // Code size 23 (0x17) .maxstack 1 .locals init (int V_0) //y IL_0000: ldarga.s V_0 IL_0002: call ""bool int?.HasValue.get"" IL_0007: brfalse.s IL_0013 IL_0009: ldarga.s V_0 IL_000b: call ""int int?.GetValueOrDefault()"" IL_0010: stloc.0 IL_0011: br.s IL_0015 IL_0013: ldc.i4.2 IL_0014: ret IL_0015: ldloc.0 IL_0016: ret }"); } [Fact] public void DoNotShareInputForMutatingWhenClause() { var source = @"using System; class Program { static void Main() { Console.Write(M1(1)); Console.Write(M2(1)); Console.Write(M3(1)); Console.Write(M4(1)); Console.Write(M5(1)); Console.Write(M6(1)); Console.Write(M7(1)); Console.Write(M8(1)); Console.Write(M9(1)); } public static int M1(int x) { return x switch { _ when (++x) == 5 => 1, 1 => 2, _ => 3 }; } public static int M2(int x) { return x switch { _ when (x+=1) == 5 => 1, 1 => 2, _ => 3 }; } public static int M3(int x) { return x switch { _ when ((x, _) = (5, 6)) == (0, 0) => 1, 1 => 2, _ => 3 }; } public static int M4(int x) { dynamic d = new Program(); return x switch { _ when d.M(ref x) => 1, 1 => 2, _ => 3 }; } bool M(ref int x) { x = 100; return false; } public static int M5(int x) { return x switch { _ when new Program(ref x).P => 1, 1 => 2, _ => 3 }; } public static int M6(int x) { dynamic d = x; return x switch { _ when new Program(d, ref x).P => 1, 1 => 2, _ => 3 }; } public static int M7(int x) { return x switch { _ when new Program(ref x).P && new Program().P => 1, 1 => 2, _ => 3 }; } public static int M8(int x) { dynamic d = x; return x switch { _ when new Program(d, ref x).P && new Program().P => 1, 1 => 2, _ => 3 }; } public static int M9(int x) { return x switch { _ when (x=100) == 1 => 1, 1 => 2, _ => 3 }; } Program() { } Program(ref int x) { x = 100; } Program(int a, ref int x) { x = 100; } bool P => false; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, references: new[] { CSharpRef }); compilation.VerifyDiagnostics(); var expectedOutput = @"222222222"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void GenerateStringHashOnlyOnce() { var source = @"using System; class Program { static void Main() { Console.Write(M1(string.Empty)); Console.Write(M2(string.Empty)); } public static int M1(string s) { return s switch { ""a""=>1, ""b""=>2, ""c""=>3, ""d""=>4, ""e""=>5, ""f""=>6, ""g""=>7, ""h""=>8, _ => 9 }; } public static int M2(string s) { return s switch { ""a""=>1, ""b""=>2, ""c""=>3, ""d""=>4, ""e""=>5, ""f""=>6, ""g""=>7, ""h""=>8, _ => 9 }; } } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"99"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void BindVariablesInWhenClause() { var source = @"using System; class Program { static void Main() { var t = (1, 2); switch (t) { case var (x, y) when x+1 == y: Console.Write(1); break; } Console.Write(t switch { var (x, y) when x+1 == y => 1, _ => 2 }); } } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"11"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void MissingExceptions_01() { var source = @"namespace System { public class Object { } public abstract class ValueType { } public struct Void { } public struct Boolean { } public struct Int32 { } } static class C { public static bool M(int i) => i switch { 1 => true }; } "; var compilation = CreateEmptyCompilation(source, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.ReleaseDll); compilation.GetDiagnostics().Verify( // (9,38): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(9, 38) ); compilation.GetEmitDiagnostics().Verify( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // (9,5): error CS0518: Predefined type 'System.Byte' is not defined or imported // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "public static bool M(int i) => i switch { 1 => true };").WithArguments("System.Byte").WithLocation(9, 5), // (9,5): error CS0518: Predefined type 'System.Byte' is not defined or imported // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "public static bool M(int i) => i switch { 1 => true };").WithArguments("System.Byte").WithLocation(9, 5), // (9,5): error CS0518: Predefined type 'System.Int16' is not defined or imported // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "public static bool M(int i) => i switch { 1 => true };").WithArguments("System.Int16").WithLocation(9, 5), // (9,5): error CS0518: Predefined type 'System.Int16' is not defined or imported // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "public static bool M(int i) => i switch { 1 => true };").WithArguments("System.Int16").WithLocation(9, 5), // (9,5): error CS0518: Predefined type 'System.Int64' is not defined or imported // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "public static bool M(int i) => i switch { 1 => true };").WithArguments("System.Int64").WithLocation(9, 5), // (9,5): error CS0518: Predefined type 'System.Int64' is not defined or imported // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "public static bool M(int i) => i switch { 1 => true };").WithArguments("System.Int64").WithLocation(9, 5), // (9,36): error CS0656: Missing compiler required member 'System.InvalidOperationException..ctor' // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "i switch { 1 => true }").WithArguments("System.InvalidOperationException", ".ctor").WithLocation(9, 36), // (9,38): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(9, 38) ); } [Fact, WorkItem(32774, "https://github.com/dotnet/roslyn/issues/32774")] public void BadCode_32774() { var source = @" public class Class1 { static void Main() { System.Console.WriteLine(SwitchCaseThatFails(12.123)); } public static bool SwitchCaseThatFails(object someObject) { switch (someObject) { case IObject x when x.SubObject != null: return false; case IOtherObject x: return false; case double x: return true; default: return false; } } public interface IObject { IObject SubObject { get; } } public interface IOtherObject { } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"True"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("Class1.SwitchCaseThatFails", @"{ // Code size 97 (0x61) .maxstack 1 .locals init (Class1.IObject V_0, //x Class1.IOtherObject V_1, //x double V_2, //x object V_3, object V_4, bool V_5) IL_0000: nop IL_0001: ldarg.0 IL_0002: stloc.s V_4 IL_0004: ldloc.s V_4 IL_0006: stloc.3 IL_0007: ldloc.3 IL_0008: isinst ""Class1.IObject"" IL_000d: stloc.0 IL_000e: ldloc.0 IL_000f: brtrue.s IL_003c IL_0011: br.s IL_001f IL_0013: ldloc.3 IL_0014: isinst ""Class1.IOtherObject"" IL_0019: stloc.1 IL_001a: ldloc.1 IL_001b: brtrue.s IL_004b IL_001d: br.s IL_0059 IL_001f: ldloc.3 IL_0020: isinst ""Class1.IOtherObject"" IL_0025: stloc.1 IL_0026: ldloc.1 IL_0027: brtrue.s IL_004b IL_0029: br.s IL_002b IL_002b: ldloc.3 IL_002c: isinst ""double"" IL_0031: brfalse.s IL_0059 IL_0033: ldloc.3 IL_0034: unbox.any ""double"" IL_0039: stloc.2 IL_003a: br.s IL_0052 IL_003c: ldloc.0 IL_003d: callvirt ""Class1.IObject Class1.IObject.SubObject.get"" IL_0042: brtrue.s IL_0046 IL_0044: br.s IL_0013 IL_0046: ldc.i4.0 IL_0047: stloc.s V_5 IL_0049: br.s IL_005e IL_004b: br.s IL_004d IL_004d: ldc.i4.0 IL_004e: stloc.s V_5 IL_0050: br.s IL_005e IL_0052: br.s IL_0054 IL_0054: ldc.i4.1 IL_0055: stloc.s V_5 IL_0057: br.s IL_005e IL_0059: ldc.i4.0 IL_005a: stloc.s V_5 IL_005c: br.s IL_005e IL_005e: ldloc.s V_5 IL_0060: ret }"); } // Possible test helper bug on Linux; see https://github.com/dotnet/roslyn/issues/33356 [ConditionalFact(typeof(WindowsOnly))] public void SwitchExpressionSequencePoints() { string source = @" public class Program { public static void Main() { int i = 0; var y = (i switch { 0 => new Program(), 1 => new Program(), _ => new Program(), }).Chain(); y.Chain2(); } public Program Chain() => this; public Program Chain2() => this; } "; var v = CompileAndVerify(source, options: TestOptions.DebugExe); v.VerifyIL(qualifiedMethodName: "Program.Main", @" { // Code size 61 (0x3d) .maxstack 2 .locals init (int V_0, //i Program V_1, //y Program V_2) // sequence point: { IL_0000: nop // sequence point: int i = 0; IL_0001: ldc.i4.0 IL_0002: stloc.0 // sequence point: var y = (i s ... }).Chain() IL_0003: ldc.i4.1 IL_0004: brtrue.s IL_0007 // sequence point: switch ... } IL_0006: nop // sequence point: <hidden> IL_0007: ldloc.0 IL_0008: brfalse.s IL_0012 IL_000a: br.s IL_000c IL_000c: ldloc.0 IL_000d: ldc.i4.1 IL_000e: beq.s IL_001a IL_0010: br.s IL_0022 // sequence point: new Program() IL_0012: newobj ""Program..ctor()"" IL_0017: stloc.2 IL_0018: br.s IL_002a // sequence point: new Program() IL_001a: newobj ""Program..ctor()"" IL_001f: stloc.2 IL_0020: br.s IL_002a // sequence point: new Program() IL_0022: newobj ""Program..ctor()"" IL_0027: stloc.2 IL_0028: br.s IL_002a // sequence point: <hidden> IL_002a: ldc.i4.1 IL_002b: brtrue.s IL_002e // sequence point: var y = (i s ... }).Chain() IL_002d: nop // sequence point: <hidden> IL_002e: ldloc.2 IL_002f: callvirt ""Program Program.Chain()"" IL_0034: stloc.1 // sequence point: y.Chain2(); IL_0035: ldloc.1 IL_0036: callvirt ""Program Program.Chain2()"" IL_003b: pop // sequence point: } IL_003c: ret } ", sequencePointDisplay: SequencePointDisplayMode.Enhanced); } [Fact, WorkItem(33675, "https://github.com/dotnet/roslyn/issues/33675")] public void ParsingParenthesizedExpressionAsPatternOfExpressionSwitch() { var source = @" public class Class1 { static void Main() { System.Console.Write(M(42)); System.Console.Write(M(41)); } static bool M(object o) { const int X = 42; return o switch { (X) => true, _ => false }; } }"; foreach (var options in new[] { TestOptions.DebugExe, TestOptions.ReleaseExe }) { var compilation = CreateCompilation(source, options: options); compilation.VerifyDiagnostics(); var expectedOutput = @"TrueFalse"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); if (options.OptimizationLevel == OptimizationLevel.Debug) { compVerifier.VerifyIL("Class1.M", @" { // Code size 45 (0x2d) .maxstack 2 .locals init (bool V_0, int V_1, bool V_2) IL_0000: nop IL_0001: ldc.i4.1 IL_0002: brtrue.s IL_0005 IL_0004: nop IL_0005: ldarg.0 IL_0006: isinst ""int"" IL_000b: brfalse.s IL_001f IL_000d: ldarg.0 IL_000e: unbox.any ""int"" IL_0013: stloc.1 IL_0014: ldloc.1 IL_0015: ldc.i4.s 42 IL_0017: beq.s IL_001b IL_0019: br.s IL_001f IL_001b: ldc.i4.1 IL_001c: stloc.0 IL_001d: br.s IL_0023 IL_001f: ldc.i4.0 IL_0020: stloc.0 IL_0021: br.s IL_0023 IL_0023: ldc.i4.1 IL_0024: brtrue.s IL_0027 IL_0026: nop IL_0027: ldloc.0 IL_0028: stloc.2 IL_0029: br.s IL_002b IL_002b: ldloc.2 IL_002c: ret } "); } else { compVerifier.VerifyIL("Class1.M", @"{ // Code size 26 (0x1a) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: isinst ""int"" IL_0006: brfalse.s IL_0016 IL_0008: ldarg.0 IL_0009: unbox.any ""int"" IL_000e: ldc.i4.s 42 IL_0010: bne.un.s IL_0016 IL_0012: ldc.i4.1 IL_0013: stloc.0 IL_0014: br.s IL_0018 IL_0016: ldc.i4.0 IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: ret }"); } } } [Fact, WorkItem(35584, "https://github.com/dotnet/roslyn/issues/35584")] public void MatchToTypeParameterUnbox_01() { var source = @" class Program { public static void Main() => System.Console.WriteLine(P<int>(0)); public static string P<T>(T t) => (t is object o) ? o.ToString() : string.Empty; } "; var expectedOutput = @"0"; foreach (var options in new[] { TestOptions.DebugExe, TestOptions.ReleaseExe }) { var compilation = CreateCompilation(source, options: options); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); if (options.OptimizationLevel == OptimizationLevel.Debug) { compVerifier.VerifyIL("Program.P<T>", @"{ // Code size 24 (0x18) .maxstack 1 .locals init (object V_0) //o IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brtrue.s IL_0011 IL_000a: ldsfld ""string string.Empty"" IL_000f: br.s IL_0017 IL_0011: ldloc.0 IL_0012: callvirt ""string object.ToString()"" IL_0017: ret } "); } else { compVerifier.VerifyIL("Program.P<T>", @"{ // Code size 23 (0x17) .maxstack 1 .locals init (object V_0) //o IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brtrue.s IL_0010 IL_000a: ldsfld ""string string.Empty"" IL_000f: ret IL_0010: ldloc.0 IL_0011: callvirt ""string object.ToString()"" IL_0016: ret } "); } } } [Fact, WorkItem(35584, "https://github.com/dotnet/roslyn/issues/35584")] public void MatchToTypeParameterUnbox_02() { var source = @"using System; class Program { public static void Main() { var generic = new Generic<int>(0); } } public class Generic<T> { public Generic(T value) { if (value is object obj && obj == null) { throw new Exception(""Kaboom!""); } } } "; var expectedOutput = @""; foreach (var options in new[] { TestOptions.DebugExe, TestOptions.ReleaseExe }) { var compilation = CreateCompilation(source, options: options); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); if (options.OptimizationLevel == OptimizationLevel.Debug) { compVerifier.VerifyIL("Generic<T>..ctor(T)", @"{ // Code size 42 (0x2a) .maxstack 2 .locals init (object V_0, //obj bool V_1) IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: nop IL_0007: nop IL_0008: ldarg.1 IL_0009: box ""T"" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: brfalse.s IL_0018 IL_0012: ldloc.0 IL_0013: ldnull IL_0014: ceq IL_0016: br.s IL_0019 IL_0018: ldc.i4.0 IL_0019: stloc.1 IL_001a: ldloc.1 IL_001b: brfalse.s IL_0029 IL_001d: nop IL_001e: ldstr ""Kaboom!"" IL_0023: newobj ""System.Exception..ctor(string)"" IL_0028: throw IL_0029: ret } "); } else { compVerifier.VerifyIL("Generic<T>..ctor(T)", @"{ // Code size 31 (0x1f) .maxstack 1 .locals init (object V_0) //obj IL_0000: ldarg.0 IL_0001: call ""object..ctor()"" IL_0006: ldarg.1 IL_0007: box ""T"" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: brfalse.s IL_001e IL_0010: ldloc.0 IL_0011: brtrue.s IL_001e IL_0013: ldstr ""Kaboom!"" IL_0018: newobj ""System.Exception..ctor(string)"" IL_001d: throw IL_001e: ret } "); } } } [Fact, WorkItem(35584, "https://github.com/dotnet/roslyn/issues/35584")] public void MatchToTypeParameterUnbox_03() { var source = @"using System; class Program { public static void Main() => System.Console.WriteLine(P<Enum>(null)); public static string P<T>(T t) where T: Enum => (t is ValueType o) ? o.ToString() : ""1""; } "; var expectedOutput = @"1"; foreach (var options in new[] { TestOptions.DebugExe, TestOptions.ReleaseExe }) { var compilation = CreateCompilation(source, options: options); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); if (options.OptimizationLevel == OptimizationLevel.Debug) { compVerifier.VerifyIL("Program.P<T>", @"{ // Code size 24 (0x18) .maxstack 1 .locals init (System.ValueType V_0) //o IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brtrue.s IL_0011 IL_000a: ldstr ""1"" IL_000f: br.s IL_0017 IL_0011: ldloc.0 IL_0012: callvirt ""string object.ToString()"" IL_0017: ret } "); } else { compVerifier.VerifyIL("Program.P<T>", @"{ // Code size 23 (0x17) .maxstack 1 .locals init (System.ValueType V_0) //o IL_0000: ldarg.0 IL_0001: box ""T"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brtrue.s IL_0010 IL_000a: ldstr ""1"" IL_000f: ret IL_0010: ldloc.0 IL_0011: callvirt ""string object.ToString()"" IL_0016: ret } "); } } } [Fact] public void CompileTimeRuntimeInstanceofMismatch_01() { var source = @"using System; class Program { static void Main() { M(new byte[1]); M(new sbyte[1]); M(new Ebyte[1]); M(new Esbyte[1]); M(new short[1]); M(new ushort[1]); M(new Eshort[1]); M(new Eushort[1]); M(new int[1]); M(new uint[1]); M(new Eint[1]); M(new Euint[1]); M(new int[1][]); M(new uint[1][]); M(new Eint[1][]); M(new Euint[1][]); M(new long[1]); M(new ulong[1]); M(new Elong[1]); M(new Eulong[1]); M(new IntPtr[1]); M(new UIntPtr[1]); M(new IntPtr[1][]); M(new UIntPtr[1][]); } static void M(object o) { switch (o) { case byte[] _ when o.GetType() == typeof(byte[]): Console.WriteLine(""byte[]""); break; case sbyte[] _ when o.GetType() == typeof(sbyte[]): Console.WriteLine(""sbyte[]""); break; case Ebyte[] _ when o.GetType() == typeof(Ebyte[]): Console.WriteLine(""Ebyte[]""); break; case Esbyte[] _ when o.GetType() == typeof(Esbyte[]): Console.WriteLine(""Esbyte[]""); break; case short[] _ when o.GetType() == typeof(short[]): Console.WriteLine(""short[]""); break; case ushort[] _ when o.GetType() == typeof(ushort[]): Console.WriteLine(""ushort[]""); break; case Eshort[] _ when o.GetType() == typeof(Eshort[]): Console.WriteLine(""Eshort[]""); break; case Eushort[] _ when o.GetType() == typeof(Eushort[]): Console.WriteLine(""Eushort[]""); break; case int[] _ when o.GetType() == typeof(int[]): Console.WriteLine(""int[]""); break; case uint[] _ when o.GetType() == typeof(uint[]): Console.WriteLine(""uint[]""); break; case Eint[] _ when o.GetType() == typeof(Eint[]): Console.WriteLine(""Eint[]""); break; case Euint[] _ when o.GetType() == typeof(Euint[]): Console.WriteLine(""Euint[]""); break; case int[][] _ when o.GetType() == typeof(int[][]): Console.WriteLine(""int[][]""); break; case uint[][] _ when o.GetType() == typeof(uint[][]): Console.WriteLine(""uint[][]""); break; case Eint[][] _ when o.GetType() == typeof(Eint[][]): Console.WriteLine(""Eint[][]""); break; case Euint[][] _ when o.GetType() == typeof(Euint[][]): Console.WriteLine(""Euint[][]""); break; case long[] _ when o.GetType() == typeof(long[]): Console.WriteLine(""long[]""); break; case ulong[] _ when o.GetType() == typeof(ulong[]): Console.WriteLine(""ulong[]""); break; case Elong[] _ when o.GetType() == typeof(Elong[]): Console.WriteLine(""Elong[]""); break; case Eulong[] _ when o.GetType() == typeof(Eulong[]): Console.WriteLine(""Eulong[]""); break; case IntPtr[] _ when o.GetType() == typeof(IntPtr[]): Console.WriteLine(""IntPtr[]""); break; case UIntPtr[] _ when o.GetType() == typeof(UIntPtr[]): Console.WriteLine(""UIntPtr[]""); break; case IntPtr[][] _ when o.GetType() == typeof(IntPtr[][]): Console.WriteLine(""IntPtr[][]""); break; case UIntPtr[][] _ when o.GetType() == typeof(UIntPtr[][]): Console.WriteLine(""UIntPtr[][]""); break; default: Console.WriteLine(""oops: "" + o.GetType()); break; } } } enum Ebyte : byte {} enum Esbyte : sbyte {} enum Eshort : short {} enum Eushort : ushort {} enum Eint : int {} enum Euint : uint {} enum Elong : long {} enum Eulong : ulong {} "; var expectedOutput = @"byte[] sbyte[] Ebyte[] Esbyte[] short[] ushort[] Eshort[] Eushort[] int[] uint[] Eint[] Euint[] int[][] uint[][] Eint[][] Euint[][] long[] ulong[] Elong[] Eulong[] IntPtr[] UIntPtr[] IntPtr[][] UIntPtr[][] "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: expectedOutput); compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void CompileTimeRuntimeInstanceofMismatch_02() { var source = @"using System; class Program { static void Main() { M(new byte[1]); M(new sbyte[1]); } static void M(object o) { switch (o) { case byte[] _: Console.WriteLine(""byte[]""); break; case sbyte[] _: // not subsumed, even though it will never occur due to CLR behavior Console.WriteLine(""sbyte[]""); break; } } } "; var expectedOutput = @"byte[] byte[] "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: expectedOutput); compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] [WorkItem(36496, "https://github.com/dotnet/roslyn/issues/36496")] public void EmptyVarPatternVsDeconstruct() { var source = @"using System; public class C { public static void Main() { Console.Write(M(new C())); Console.Write(M(null)); } public static bool M(C c) { return c is var (); } public void Deconstruct() { } } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: "TrueFalse"); compVerifier.VerifyIL("C.M(C)", @" { // Code size 5 (0x5) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un IL_0004: ret } "); } [Fact] [WorkItem(36496, "https://github.com/dotnet/roslyn/issues/36496")] public void EmptyPositionalPatternVsDeconstruct() { var source = @"using System; public class C { public static void Main() { Console.Write(M(new C())); Console.Write(M(null)); } public static bool M(C c) { return c is (); } public void Deconstruct() { } } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: "TrueFalse"); compVerifier.VerifyIL("C.M(C)", @" { // Code size 5 (0x5) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldnull IL_0002: cgt.un IL_0004: ret } "); } [Fact, WorkItem(31494, "https://github.com/dotnet/roslyn/issues/31494")] public void NoRedundantNullCheckForStringConstantPattern_01() { var source = @"public class C { public static bool M1(string s) => s is ""Frog""; public static bool M2(string s) => s == ""Frog""; public static bool M3(string s) => s switch { ""Frog"" => true, _ => false }; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1(string)", @" { // Code size 12 (0xc) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldstr ""Frog"" IL_0006: call ""bool string.op_Equality(string, string)"" IL_000b: ret } "); compVerifier.VerifyIL("C.M2(string)", @" { // Code size 12 (0xc) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldstr ""Frog"" IL_0006: call ""bool string.op_Equality(string, string)"" IL_000b: ret } "); compVerifier.VerifyIL("C.M3(string)", @" { // Code size 21 (0x15) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: ldstr ""Frog"" IL_0006: call ""bool string.op_Equality(string, string)"" IL_000b: brfalse.s IL_0011 IL_000d: ldc.i4.1 IL_000e: stloc.0 IL_000f: br.s IL_0013 IL_0011: ldc.i4.0 IL_0012: stloc.0 IL_0013: ldloc.0 IL_0014: ret } "); } [Fact, WorkItem(31494, "https://github.com/dotnet/roslyn/issues/31494")] public void NoRedundantNullCheckForStringConstantPattern_02() { var source = @"public class C { public static bool M1(string s) => s switch { ""Frog"" => true, ""Newt"" => true, _ => false }; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1(string)", @" { // Code size 40 (0x28) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: ldstr ""Frog"" IL_0006: call ""bool string.op_Equality(string, string)"" IL_000b: brtrue.s IL_001c IL_000d: ldarg.0 IL_000e: ldstr ""Newt"" IL_0013: call ""bool string.op_Equality(string, string)"" IL_0018: brtrue.s IL_0020 IL_001a: br.s IL_0024 IL_001c: ldc.i4.1 IL_001d: stloc.0 IL_001e: br.s IL_0026 IL_0020: ldc.i4.1 IL_0021: stloc.0 IL_0022: br.s IL_0026 IL_0024: ldc.i4.0 IL_0025: stloc.0 IL_0026: ldloc.0 IL_0027: ret } "); } [Fact, WorkItem(31494, "https://github.com/dotnet/roslyn/issues/31494")] public void NoRedundantNullCheckForStringConstantPattern_03() { var source = @"public class C { public static bool M1(System.Type x) => x is { Name: ""Program"" }; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1(System.Type)", @" { // Code size 22 (0x16) .maxstack 2 IL_0000: ldarg.0 IL_0001: brfalse.s IL_0014 IL_0003: ldarg.0 IL_0004: callvirt ""string System.Reflection.MemberInfo.Name.get"" IL_0009: ldstr ""Program"" IL_000e: call ""bool string.op_Equality(string, string)"" IL_0013: ret IL_0014: ldc.i4.0 IL_0015: ret } "); } [Fact] [WorkItem(42912, "https://github.com/dotnet/roslyn/issues/42912")] [WorkItem(31494, "https://github.com/dotnet/roslyn/issues/31494")] public void NoRedundantNullCheckForNullableConstantPattern_04() { // Note that we do not produce the same code for `x is 1` and `x == 1`. // The latter has been optimized to avoid branches. var source = @"public class C { public static bool M1(int? x) => x is 1; public static bool M2(int? x) => x == 1; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation); compVerifier.VerifyIL("C.M1(int?)", @" { // Code size 22 (0x16) .maxstack 2 IL_0000: ldarga.s V_0 IL_0002: call ""bool int?.HasValue.get"" IL_0007: brfalse.s IL_0014 IL_0009: ldarga.s V_0 IL_000b: call ""int int?.GetValueOrDefault()"" IL_0010: ldc.i4.1 IL_0011: ceq IL_0013: ret IL_0014: ldc.i4.0 IL_0015: ret } "); compVerifier.VerifyIL("C.M2(int?)", @" { // Code size 11 (0xb) .maxstack 2 IL_0000: ldarga.s V_0 IL_0002: call ""int int?.GetValueOrDefault()"" IL_0007: ldc.i4.1 IL_0008: ceq IL_000a: ret } "); } [Fact] public void SwitchExpressionAsExceptionFilter_01() { var source = @" using System; class C { const string K1 = ""frog""; const string K2 = ""toad""; public static void M(string msg) { try { T(msg); } catch (Exception e) when (e.Message switch { K1 => true, K2 => true, _ => false, }) { throw new Exception(e.Message); } catch { } } static void T(string msg) { throw new Exception(msg); } static void Main() { Try(K1); Try(K2); Try(""fox""); } static void Try(string s) { try { M(s); } catch (Exception ex) { Console.WriteLine(ex.Message); } } } "; var expectedOutput = @"frog toad"; foreach (var compilationOptions in new[] { TestOptions.ReleaseExe, TestOptions.DebugExe }) { var compilation = CreateCompilation(source, options: compilationOptions); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); if (compilationOptions.OptimizationLevel == OptimizationLevel.Debug) { compVerifier.VerifyIL("C.M(string)", @" { // Code size 108 (0x6c) .maxstack 2 .locals init (System.Exception V_0, //e bool V_1, string V_2, bool V_3) IL_0000: nop .try { IL_0001: nop IL_0002: ldarg.0 IL_0003: call ""void C.T(string)"" IL_0008: nop IL_0009: nop IL_000a: leave.s IL_006b } filter { IL_000c: isinst ""System.Exception"" IL_0011: dup IL_0012: brtrue.s IL_0018 IL_0014: pop IL_0015: ldc.i4.0 IL_0016: br.s IL_0056 IL_0018: stloc.0 IL_0019: ldloc.0 IL_001a: callvirt ""string System.Exception.Message.get"" IL_001f: stloc.2 IL_0020: ldc.i4.1 IL_0021: brtrue.s IL_0024 IL_0023: nop IL_0024: ldloc.2 IL_0025: ldstr ""frog"" IL_002a: call ""bool string.op_Equality(string, string)"" IL_002f: brtrue.s IL_0040 IL_0031: ldloc.2 IL_0032: ldstr ""toad"" IL_0037: call ""bool string.op_Equality(string, string)"" IL_003c: brtrue.s IL_0044 IL_003e: br.s IL_0048 IL_0040: ldc.i4.1 IL_0041: stloc.1 IL_0042: br.s IL_004c IL_0044: ldc.i4.1 IL_0045: stloc.1 IL_0046: br.s IL_004c IL_0048: ldc.i4.0 IL_0049: stloc.1 IL_004a: br.s IL_004c IL_004c: ldc.i4.1 IL_004d: brtrue.s IL_0050 IL_004f: nop IL_0050: ldloc.1 IL_0051: stloc.3 IL_0052: ldloc.3 IL_0053: ldc.i4.0 IL_0054: cgt.un IL_0056: endfilter } // end filter { // handler IL_0058: pop IL_0059: nop IL_005a: ldloc.0 IL_005b: callvirt ""string System.Exception.Message.get"" IL_0060: newobj ""System.Exception..ctor(string)"" IL_0065: throw } catch object { IL_0066: pop IL_0067: nop IL_0068: nop IL_0069: leave.s IL_006b } IL_006b: ret } "); } else { compVerifier.VerifyIL("C.M(string)", @" { // Code size 89 (0x59) .maxstack 2 .locals init (System.Exception V_0, //e bool V_1, string V_2) .try { IL_0000: ldarg.0 IL_0001: call ""void C.T(string)"" IL_0006: leave.s IL_0058 } filter { IL_0008: isinst ""System.Exception"" IL_000d: dup IL_000e: brtrue.s IL_0014 IL_0010: pop IL_0011: ldc.i4.0 IL_0012: br.s IL_0046 IL_0014: stloc.0 IL_0015: ldloc.0 IL_0016: callvirt ""string System.Exception.Message.get"" IL_001b: stloc.2 IL_001c: ldloc.2 IL_001d: ldstr ""frog"" IL_0022: call ""bool string.op_Equality(string, string)"" IL_0027: brtrue.s IL_0038 IL_0029: ldloc.2 IL_002a: ldstr ""toad"" IL_002f: call ""bool string.op_Equality(string, string)"" IL_0034: brtrue.s IL_003c IL_0036: br.s IL_0040 IL_0038: ldc.i4.1 IL_0039: stloc.1 IL_003a: br.s IL_0042 IL_003c: ldc.i4.1 IL_003d: stloc.1 IL_003e: br.s IL_0042 IL_0040: ldc.i4.0 IL_0041: stloc.1 IL_0042: ldloc.1 IL_0043: ldc.i4.0 IL_0044: cgt.un IL_0046: endfilter } // end filter { // handler IL_0048: pop IL_0049: ldloc.0 IL_004a: callvirt ""string System.Exception.Message.get"" IL_004f: newobj ""System.Exception..ctor(string)"" IL_0054: throw } catch object { IL_0055: pop IL_0056: leave.s IL_0058 } IL_0058: ret } "); } } } [Fact] public void SwitchExpressionAsExceptionFilter_02() { var source = @" using System; class C { public static void Main() { try { throw new Exception(); } catch when ((3 is int i) switch { true when M(() => i) => true, _ => false }) { Console.WriteLine(""correct""); } } static bool M(Func<int> func) { func(); return true; } } "; var expectedOutput = @"correct"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] [WorkItem(48259, "https://github.com/dotnet/roslyn/issues/48259")] public void SwitchExpressionAsExceptionFilter_03() { var source = @" using System; using System.Threading.Tasks; public static class Program { static async Task Main() { Exception ex = new ArgumentException(); try { throw ex; } catch (Exception e) when (e switch { InvalidOperationException => true, _ => false }) { return; } catch (Exception) { Console.WriteLine(""correct""); } } } "; var expectedOutput = "correct"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] [WorkItem(48259, "https://github.com/dotnet/roslyn/issues/48259")] public void SwitchExpressionAsExceptionFilter_04() { var source = @" using System; using System.Threading.Tasks; public static class Program { static async Task Main() { Exception ex = new ArgumentException(); try { throw ex; } catch (Exception e) when (e switch { ArgumentException => true, _ => false }) { Console.WriteLine(""correct""); return; } } } "; var expectedOutput = "correct"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [WorkItem(48563, "https://github.com/dotnet/roslyn/issues/48563")] [Theory] [InlineData("void*")] [InlineData("char*")] [InlineData("delegate*<void>")] public void IsNull_01(string pointerType) { var source = $@"using static System.Console; unsafe class Program {{ static void Main() {{ Check(0); Check(-1); }} static void Check(nint i) {{ {pointerType} p = ({pointerType})i; WriteLine(EqualNull(p)); WriteLine(IsNull(p)); WriteLine(NotEqualNull(p)); WriteLine(IsNotNull(p)); }} static bool EqualNull({pointerType} p) => p == null; static bool NotEqualNull({pointerType} p) => p != null; static bool IsNull({pointerType} p) => p is null; static bool IsNotNull({pointerType} p) => p is not null; }}"; var verifier = CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe, verify: Verification.Skipped, expectedOutput: @"True True False False False False True True"); string expectedEqualNull = @"{ // Code size 6 (0x6) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: conv.u IL_0003: ceq IL_0005: ret }"; string expectedNotEqualNull = @"{ // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: conv.u IL_0003: ceq IL_0005: ldc.i4.0 IL_0006: ceq IL_0008: ret }"; verifier.VerifyIL("Program.EqualNull", expectedEqualNull); verifier.VerifyIL("Program.NotEqualNull", expectedNotEqualNull); verifier.VerifyIL("Program.IsNull", expectedEqualNull); verifier.VerifyIL("Program.IsNotNull", expectedNotEqualNull); } [WorkItem(48563, "https://github.com/dotnet/roslyn/issues/48563")] [Fact] public void IsNull_02() { var source = @"using static System.Console; unsafe class Program { static void Main() { Check(0); Check(-1); } static void Check(nint i) { char* p = (char*)i; WriteLine(EqualNull(p)); } static bool EqualNull(char* p) => p switch { null => true, _ => false }; }"; var verifier = CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe, verify: Verification.Skipped, expectedOutput: @"True False"); verifier.VerifyIL("Program.EqualNull", @"{ // Code size 13 (0xd) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: conv.u IL_0003: bne.un.s IL_0009 IL_0005: ldc.i4.1 IL_0006: stloc.0 IL_0007: br.s IL_000b IL_0009: ldc.i4.0 IL_000a: stloc.0 IL_000b: ldloc.0 IL_000c: ret }"); } [WorkItem(48563, "https://github.com/dotnet/roslyn/issues/48563")] [Fact] public void IsNull_03() { var source = @"using static System.Console; unsafe class C { public char* P; } unsafe class Program { static void Main() { Check(0); Check(-1); } static void Check(nint i) { char* p = (char*)i; WriteLine(EqualNull(new C() { P = p })); } static bool EqualNull(C c) => c switch { { P: null } => true, _ => false }; }"; var verifier = CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe, verify: Verification.Skipped, expectedOutput: @"True False"); verifier.VerifyIL("Program.EqualNull", @"{ // Code size 21 (0x15) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: brfalse.s IL_0011 IL_0003: ldarg.0 IL_0004: ldfld ""char* C.P"" IL_0009: ldc.i4.0 IL_000a: conv.u IL_000b: bne.un.s IL_0011 IL_000d: ldc.i4.1 IL_000e: stloc.0 IL_000f: br.s IL_0013 IL_0011: ldc.i4.0 IL_0012: stloc.0 IL_0013: ldloc.0 IL_0014: ret }"); } #endregion Miscellaneous #region Target Typed Switch [Fact] public void TargetTypedSwitch_Assignment() { var source = @" using System; class Program { public static void Main() { Console.Write(M(false)); Console.Write(M(true)); } static object M(bool b) { int result = b switch { false => new A(), true => new B() }; return result; } } class A { public static implicit operator int(A a) => throw null; public static implicit operator B(A a) => new B(); } class B { public static implicit operator int(B b) => (b == null) ? throw null : 2; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"22"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void TargetTypedSwitch_Return() { var source = @" using System; class Program { public static void Main() { Console.Write(M(false)); Console.Write(M(true)); } static long M(bool b) { return b switch { false => new A(), true => new B() }; } } class A { public static implicit operator int(A a) => throw null; public static implicit operator B(A a) => new B(); } class B { public static implicit operator int(B b) => (b == null) ? throw null : 2; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"22"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void TargetTypedSwitch_Argument_01() { var source = @" using System; class Program { public static void Main() { Console.Write(M1(false)); Console.Write(M1(true)); } static object M1(bool b) { return M2(b switch { false => new A(), true => new B() }); } static Exception M2(Exception ex) => ex; static int M2(int i) => i; static int M2(string s) => s.Length; } class A : Exception { public static implicit operator int(A a) => throw null; public static implicit operator B(A a) => throw null; } class B : Exception { public static implicit operator int(B b) => throw null; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics( // (12,16): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M2(Exception)' and 'Program.M2(int)' // return M2(b switch { false => new A(), true => new B() }); Diagnostic(ErrorCode.ERR_AmbigCall, "M2").WithArguments("Program.M2(System.Exception)", "Program.M2(int)").WithLocation(12, 16) ); } [Fact] public void TargetTypedSwitch_Argument_02() { var source = @" using System; class Program { public static void Main() { Console.Write(M1(false)); Console.Write(M1(true)); } static object M1(bool b) { return M2(b switch { false => new A(), true => new B() }); } // static Exception M2(Exception ex) => ex; static int M2(int i) => i; static int M2(string s) => s.Length; } class A : Exception { public static implicit operator int(A a) => throw null; public static implicit operator B(A a) => new B(); } class B : Exception { public static implicit operator int(B b) => (b == null) ? throw null : 2; } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"22"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [ConditionalFact(typeof(WindowsDesktopOnly), Reason = ConditionalSkipReason.RestrictedTypesNeedDesktop)] public void TargetTypedSwitch_Arglist() { var source = @" using System; class Program { public static void Main() { Console.WriteLine(M1(false)); Console.WriteLine(M1(true)); } static object M1(bool b) { return M2(__arglist(b switch { false => new A(), true => new B() })); } static int M2(__arglist) => 1; } class A { public A() { Console.Write(""new A; ""); } public static implicit operator B(A a) { Console.Write(""A->""); return new B(); } } class B { public B() { Console.Write(""new B; ""); } } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"new A; A->new B; 1 new B; 1"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void TargetTypedSwitch_StackallocSize() { var source = @" using System; class Program { public static void Main() { M1(false); M1(true); } static void M1(bool b) { Span<int> s = stackalloc int[b switch { false => new A(), true => new B() }]; Console.WriteLine(s.Length); } } class A { public A() { Console.Write(""new A; ""); } public static implicit operator int(A a) { Console.Write(""A->int; ""); return 4; } } class B { public B() { Console.Write(""new B; ""); } public static implicit operator int(B b) { Console.Write(""B->int; ""); return 2; } } "; var compilation = CreateCompilationWithMscorlibAndSpan(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); var expectedOutput = @"new A; A->int; 4 new B; B->int; 2"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput, verify: Verification.Skipped); } [Fact] public void TargetTypedSwitch_Attribute() { var source = @" using System; class Program { [My(1 switch { 1 => 1, _ => 2 })] public static void M1() { } [My(1 switch { 1 => new A(), _ => new B() })] public static void M2() { } [My(1 switch { 1 => 1, _ => string.Empty })] public static void M3() { } } public class MyAttribute : Attribute { public MyAttribute(int Value) { } } public class A { public static implicit operator int(A a) => 4; } public class B { public static implicit operator int(B b) => 2; } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (5,9): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [My(1 switch { 1 => 1, _ => 2 })] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "1 switch { 1 => 1, _ => 2 }").WithLocation(5, 9), // (8,9): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [My(1 switch { 1 => new A(), _ => new B() })] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "1 switch { 1 => new A(), _ => new B() }").WithLocation(8, 9), // (11,9): error CS1503: Argument 1: cannot convert from '<switch expression>' to 'int' // [My(1 switch { 1 => 1, _ => string.Empty })] Diagnostic(ErrorCode.ERR_BadArgType, "1 switch { 1 => 1, _ => string.Empty }").WithArguments("1", "<switch expression>", "int").WithLocation(11, 9) ); var tree = compilation.SyntaxTrees[0]; var model = compilation.GetSemanticModel(tree); var switchExpressions = tree.GetRoot().DescendantNodes().OfType<SwitchExpressionSyntax>().ToArray(); VerifyOperationTreeForNode(compilation, model, switchExpressions[0], @" ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: System.Int32, IsInvalid) (Syntax: '1 switch { ... 1, _ => 2 }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '1 => 1') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => 2') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null, IsInvalid) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2') "); VerifyOperationTreeForNode(compilation, model, switchExpressions[1], @" ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: System.Int32, IsInvalid) (Syntax: '1 switch { ... > new B() }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '1 => new A()') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Value: IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Int32 A.op_Implicit(A a)) (OperationKind.Conversion, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: 'new A()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Int32 A.op_Implicit(A a)) Operand: IObjectCreationOperation (Constructor: A..ctor()) (OperationKind.ObjectCreation, Type: A, IsInvalid) (Syntax: 'new A()') Arguments(0) Initializer: null ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => new B()') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null, IsInvalid) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Int32 B.op_Implicit(B b)) (OperationKind.Conversion, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: 'new B()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Int32 B.op_Implicit(B b)) Operand: IObjectCreationOperation (Constructor: B..ctor()) (OperationKind.ObjectCreation, Type: B, IsInvalid) (Syntax: 'new B()') Arguments(0) Initializer: null "); VerifyOperationTreeForNode(compilation, model, switchExpressions[2], @" ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: ?, IsInvalid) (Syntax: '1 switch { ... ing.Empty }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '1 => 1') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsInvalid, IsImplicit) (Syntax: '1') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => string.Empty') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null, IsInvalid) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsInvalid, IsImplicit) (Syntax: 'string.Empty') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IFieldReferenceOperation: System.String System.String.Empty (Static) (OperationKind.FieldReference, Type: System.String, IsInvalid) (Syntax: 'string.Empty') Instance Receiver: null "); } [Fact] public void TargetTypedSwitch_Attribute_NamedArgument() { var source = @" using System; class Program { [My(Value = 1 switch { 1 => 1, _ => 2 })] public static void M1() { } [My(Value = 1 switch { 1 => new A(), _ => new B() })] public static void M2() { } [My(Value = 1 switch { 1 => 1, _ => string.Empty })] public static void M3() { } } public class MyAttribute : Attribute { public MyAttribute() { } public int Value { get; set; } } public class A { public static implicit operator int(A a) => 4; } public class B { public static implicit operator int(B b) => 2; } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (5,17): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [My(Value = 1 switch { 1 => 1, _ => 2 })] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "1 switch { 1 => 1, _ => 2 }").WithLocation(5, 17), // (8,17): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [My(Value = 1 switch { 1 => new A(), _ => new B() })] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "1 switch { 1 => new A(), _ => new B() }").WithLocation(8, 17), // (11,41): error CS0029: Cannot implicitly convert type 'string' to 'int' // [My(Value = 1 switch { 1 => 1, _ => string.Empty })] Diagnostic(ErrorCode.ERR_NoImplicitConv, "string.Empty").WithArguments("string", "int").WithLocation(11, 41) ); var tree = compilation.SyntaxTrees[0]; var model = compilation.GetSemanticModel(tree); var attributeArguments = tree.GetRoot().DescendantNodes().OfType<AttributeArgumentSyntax>().ToArray(); VerifyOperationTreeForNode(compilation, model, attributeArguments[0], @" ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: 'Value = 1 s ... 1, _ => 2 }') Left: IPropertyReferenceOperation: System.Int32 MyAttribute.Value { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'Value') Instance Receiver: null Right: ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: System.Int32, IsInvalid) (Syntax: '1 switch { ... 1, _ => 2 }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '1 => 1') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => 2') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null, IsInvalid) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsInvalid) (Syntax: '2') "); VerifyOperationTreeForNode(compilation, model, attributeArguments[1], @" ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: 'Value = 1 s ... > new B() }') Left: IPropertyReferenceOperation: System.Int32 MyAttribute.Value { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'Value') Instance Receiver: null Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: '1 switch { ... > new B() }') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: System.Int32, IsInvalid) (Syntax: '1 switch { ... > new B() }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '1 => new A()') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') Value: IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Int32 A.op_Implicit(A a)) (OperationKind.Conversion, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: 'new A()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Int32 A.op_Implicit(A a)) Operand: IObjectCreationOperation (Constructor: A..ctor()) (OperationKind.ObjectCreation, Type: A, IsInvalid) (Syntax: 'new A()') Arguments(0) Initializer: null ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => new B()') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null, IsInvalid) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Int32 B.op_Implicit(B b)) (OperationKind.Conversion, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: 'new B()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Int32 B.op_Implicit(B b)) Operand: IObjectCreationOperation (Constructor: B..ctor()) (OperationKind.ObjectCreation, Type: B, IsInvalid) (Syntax: 'new B()') Arguments(0) Initializer: null "); VerifyOperationTreeForNode(compilation, model, attributeArguments[2], @" ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: 'Value = 1 s ... ing.Empty }') Left: IPropertyReferenceOperation: System.Int32 MyAttribute.Value { get; set; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'Value') Instance Receiver: null Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: '1 switch { ... ing.Empty }') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: ?, IsInvalid) (Syntax: '1 switch { ... ing.Empty }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null) (Syntax: '1 => 1') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsImplicit) (Syntax: '1') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => string.Empty') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsInvalid, IsImplicit) (Syntax: 'string.Empty') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IFieldReferenceOperation: System.String System.String.Empty (Static) (OperationKind.FieldReference, Type: System.String, IsInvalid) (Syntax: 'string.Empty') Instance Receiver: null "); } [Fact] public void TargetTypedSwitch_Attribute_MissingNamedArgument() { var source = @" using System; class Program { [My(Value = 1 switch { 1 => 1, _ => 2 })] public static void M1() { } [My(Value = 1 switch { 1 => new A(), _ => new B() })] public static void M2() { } [My(Value = 1 switch { 1 => 1, _ => string.Empty })] public static void M3() { } } public class MyAttribute : Attribute { public MyAttribute() { } } public class A { public static implicit operator int(A a) => 4; } public class B { public static implicit operator int(B b) => 2; } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (5,9): error CS0246: The type or namespace name 'Value' could not be found (are you missing a using directive or an assembly reference?) // [My(Value = 1 switch { 1 => 1, _ => 2 })] Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Value").WithArguments("Value").WithLocation(5, 9), // (8,9): error CS0246: The type or namespace name 'Value' could not be found (are you missing a using directive or an assembly reference?) // [My(Value = 1 switch { 1 => new A(), _ => new B() })] Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Value").WithArguments("Value").WithLocation(8, 9), // (11,9): error CS0246: The type or namespace name 'Value' could not be found (are you missing a using directive or an assembly reference?) // [My(Value = 1 switch { 1 => 1, _ => string.Empty })] Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Value").WithArguments("Value").WithLocation(11, 9) ); var tree = compilation.SyntaxTrees[0]; var model = compilation.GetSemanticModel(tree); var attributeArguments = tree.GetRoot().DescendantNodes().OfType<AttributeArgumentSyntax>().ToArray(); VerifyOperationTreeForNode(compilation, model, attributeArguments[0], @" ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: 'Value = 1 s ... 1, _ => 2 }') Left: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'Value') Children(0) Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsImplicit) (Syntax: '1 switch { ... 1, _ => 2 }') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: System.Int32) (Syntax: '1 switch { ... 1, _ => 2 }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null) (Syntax: '1 => 1') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null) (Syntax: '_ => 2') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') "); VerifyOperationTreeForNode(compilation, model, attributeArguments[1], @" ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: 'Value = 1 s ... > new B() }') Left: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'Value') Children(0) Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsImplicit) (Syntax: '1 switch { ... > new B() }') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: ?) (Syntax: '1 switch { ... > new B() }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null) (Syntax: '1 => new A()') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsImplicit) (Syntax: 'new A()') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IObjectCreationOperation (Constructor: A..ctor()) (OperationKind.ObjectCreation, Type: A) (Syntax: 'new A()') Arguments(0) Initializer: null ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null) (Syntax: '_ => new B()') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsImplicit) (Syntax: 'new B()') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IObjectCreationOperation (Constructor: B..ctor()) (OperationKind.ObjectCreation, Type: B) (Syntax: 'new B()') Arguments(0) Initializer: null "); VerifyOperationTreeForNode(compilation, model, attributeArguments[2], @" ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: 'Value = 1 s ... ing.Empty }') Left: IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'Value') Children(0) Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsImplicit) (Syntax: '1 switch { ... ing.Empty }') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ISwitchExpressionOperation (2 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: ?) (Syntax: '1 switch { ... ing.Empty }') Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Arms(2): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null) (Syntax: '1 => 1') Pattern: IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32) Value: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsImplicit) (Syntax: '1') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null) (Syntax: '_ => string.Empty') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsImplicit) (Syntax: 'string.Empty') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IFieldReferenceOperation: System.String System.String.Empty (Static) (OperationKind.FieldReference, Type: System.String) (Syntax: 'string.Empty') Instance Receiver: null "); } [Fact] public void TargetTypedSwitch_As() { var source = @" class Program { public static void M(int i, string s) { // we do not target-type the left-hand-side of an as expression _ = i switch { 1 => i, _ => s } as object; } } "; var compilation = CreateCompilation(source); compilation.VerifyDiagnostics( // (7,15): error CS8506: No best type was found for the switch expression. // _ = i switch { 1 => i, _ => s } as object; Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(7, 15) ); } [Fact] public void TargetTypedSwitch_DoubleConversion() { var source = @"using System; class Program { public static void Main(string[] args) { M(false); M(true); } public static void M(bool b) { C c = b switch { false => new A(), true => new B() }; Console.WriteLine("".""); } } class A { public A() { Console.Write(""new A; ""); } public static implicit operator B(A a) { Console.Write(""A->""); return new B(); } } class B { public B() { Console.Write(""new B; ""); } public static implicit operator C(B a) { Console.Write(""B->""); return new C(); } } class C { public C() { Console.Write(""new C; ""); } } "; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); compilation.VerifyDiagnostics(); var expectedOutput = @"new A; A->new B; B->new C; . new B; B->new C; ."; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } [Fact] public void TargetTypedSwitch_StringInsert() { var source = @" using System; class Program { public static void Main() { Console.Write($""{false switch { false => new A(), true => new B() }}""); Console.Write($""{true switch { false => new A(), true => new B() }}""); } } class A { } class B { } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "AB"); } #endregion Target Typed Switch #region Pattern Combinators [Fact] public void IsPatternDisjunct_01() { var source = @" using System; class C { static bool M1(object o) => o is int or long; static bool M2(object o) => o is int || o is long; public static void Main() { Console.Write(M1(1)); Console.Write(M1(string.Empty)); Console.Write(M1(1L)); Console.Write(M1(1UL)); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); var expectedOutput = @"TrueFalseTrueFalse"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", """ { // Code size 26 (0x1a) .maxstack 1 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: isinst "int" IL_0006: brtrue.s IL_0012 IL_0008: ldarg.0 IL_0009: isinst "long" IL_000e: brtrue.s IL_0012 IL_0010: br.s IL_0016 IL_0012: ldc.i4.1 IL_0013: stloc.0 IL_0014: br.s IL_0018 IL_0016: ldc.i4.0 IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: ret } """); compVerifier.VerifyIL("C.M2", """ { // Code size 21 (0x15) .maxstack 2 IL_0000: ldarg.0 IL_0001: isinst "int" IL_0006: brtrue.s IL_0013 IL_0008: ldarg.0 IL_0009: isinst "long" IL_000e: ldnull IL_000f: cgt.un IL_0011: br.s IL_0014 IL_0013: ldc.i4.1 IL_0014: ret } """); compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", """ { // Code size 24 (0x18) .maxstack 1 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: isinst "int" IL_0006: brtrue.s IL_0010 IL_0008: ldarg.0 IL_0009: isinst "long" IL_000e: brfalse.s IL_0014 IL_0010: ldc.i4.1 IL_0011: stloc.0 IL_0012: br.s IL_0016 IL_0014: ldc.i4.0 IL_0015: stloc.0 IL_0016: ldloc.0 IL_0017: ret } """); compVerifier.VerifyIL("C.M2", @" { // Code size 20 (0x14) .maxstack 2 IL_0000: ldarg.0 IL_0001: isinst ""int"" IL_0006: brtrue.s IL_0012 IL_0008: ldarg.0 IL_0009: isinst ""long"" IL_000e: ldnull IL_000f: cgt.un IL_0011: ret IL_0012: ldc.i4.1 IL_0013: ret } "); } [Fact] public void IsPatternDisjunct_02() { var source = @" using System; class C { static string M1(object o) => (o is int or long) ? ""True"" : ""False""; static string M2(object o) => (o is int || o is long) ? ""True"" : ""False""; public static void Main() { Console.Write(M1(1)); Console.Write(M1(string.Empty)); Console.Write(M1(1L)); Console.Write(M1(1UL)); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); var expectedOutput = @"TrueFalseTrueFalse"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", """ { // Code size 40 (0x28) .maxstack 1 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: isinst "int" IL_0006: brtrue.s IL_0012 IL_0008: ldarg.0 IL_0009: isinst "long" IL_000e: brtrue.s IL_0012 IL_0010: br.s IL_0016 IL_0012: ldc.i4.1 IL_0013: stloc.0 IL_0014: br.s IL_0018 IL_0016: ldc.i4.0 IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: brtrue.s IL_0022 IL_001b: ldstr "False" IL_0020: br.s IL_0027 IL_0022: ldstr "True" IL_0027: ret } """); compVerifier.VerifyIL("C.M2", """ { // Code size 29 (0x1d) .maxstack 1 IL_0000: ldarg.0 IL_0001: isinst "int" IL_0006: brtrue.s IL_0017 IL_0008: ldarg.0 IL_0009: isinst "long" IL_000e: brtrue.s IL_0017 IL_0010: ldstr "False" IL_0015: br.s IL_001c IL_0017: ldstr "True" IL_001c: ret } """); compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", """ { // Code size 37 (0x25) .maxstack 1 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: isinst "int" IL_0006: brtrue.s IL_0010 IL_0008: ldarg.0 IL_0009: isinst "long" IL_000e: brfalse.s IL_0014 IL_0010: ldc.i4.1 IL_0011: stloc.0 IL_0012: br.s IL_0016 IL_0014: ldc.i4.0 IL_0015: stloc.0 IL_0016: ldloc.0 IL_0017: brtrue.s IL_001f IL_0019: ldstr "False" IL_001e: ret IL_001f: ldstr "True" IL_0024: ret } """); compVerifier.VerifyIL("C.M2", @" { // Code size 28 (0x1c) .maxstack 1 IL_0000: ldarg.0 IL_0001: isinst ""int"" IL_0006: brtrue.s IL_0016 IL_0008: ldarg.0 IL_0009: isinst ""long"" IL_000e: brtrue.s IL_0016 IL_0010: ldstr ""False"" IL_0015: ret IL_0016: ldstr ""True"" IL_001b: ret } "); } [Fact] public void IsPatternDisjunct_03() { var source = @" using System; class C { static bool M1(object o) => o is >= 'A' and <= 'Z' or >= 'a' and <= 'z'; static bool M2(object o) => o is char c && (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'); public static void Main() { Console.Write(M1('A')); Console.Write(M1('0')); Console.Write(M1('q')); Console.Write(M1(2)); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); var expectedOutput = @"TrueFalseTrueFalse"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @" { // Code size 47 (0x2f) .maxstack 2 .locals init (char V_0, bool V_1) IL_0000: ldarg.0 IL_0001: isinst ""char"" IL_0006: brfalse.s IL_002b IL_0008: ldarg.0 IL_0009: unbox.any ""char"" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: ldc.i4.s 97 IL_0012: blt.s IL_001b IL_0014: ldloc.0 IL_0015: ldc.i4.s 122 IL_0017: ble.s IL_0027 IL_0019: br.s IL_002b IL_001b: ldloc.0 IL_001c: ldc.i4.s 65 IL_001e: blt.s IL_002b IL_0020: ldloc.0 IL_0021: ldc.i4.s 90 IL_0023: ble.s IL_0027 IL_0025: br.s IL_002b IL_0027: ldc.i4.1 IL_0028: stloc.1 IL_0029: br.s IL_002d IL_002b: ldc.i4.0 IL_002c: stloc.1 IL_002d: ldloc.1 IL_002e: ret } "); compVerifier.VerifyIL("C.M2", @" { // Code size 48 (0x30) .maxstack 2 .locals init (char V_0) //c IL_0000: ldarg.0 IL_0001: isinst ""char"" IL_0006: brfalse.s IL_002e IL_0008: ldarg.0 IL_0009: unbox.any ""char"" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: ldc.i4.s 65 IL_0012: blt.s IL_0019 IL_0014: ldloc.0 IL_0015: ldc.i4.s 90 IL_0017: ble.s IL_002b IL_0019: ldloc.0 IL_001a: ldc.i4.s 97 IL_001c: blt.s IL_0028 IL_001e: ldloc.0 IL_001f: ldc.i4.s 122 IL_0021: cgt IL_0023: ldc.i4.0 IL_0024: ceq IL_0026: br.s IL_0029 IL_0028: ldc.i4.0 IL_0029: br.s IL_002c IL_002b: ldc.i4.1 IL_002c: br.s IL_002f IL_002e: ldc.i4.0 IL_002f: ret } "); compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @" { // Code size 45 (0x2d) .maxstack 2 .locals init (char V_0, bool V_1) IL_0000: ldarg.0 IL_0001: isinst ""char"" IL_0006: brfalse.s IL_0029 IL_0008: ldarg.0 IL_0009: unbox.any ""char"" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: ldc.i4.s 97 IL_0012: blt.s IL_001b IL_0014: ldloc.0 IL_0015: ldc.i4.s 122 IL_0017: ble.s IL_0025 IL_0019: br.s IL_0029 IL_001b: ldloc.0 IL_001c: ldc.i4.s 65 IL_001e: blt.s IL_0029 IL_0020: ldloc.0 IL_0021: ldc.i4.s 90 IL_0023: bgt.s IL_0029 IL_0025: ldc.i4.1 IL_0026: stloc.1 IL_0027: br.s IL_002b IL_0029: ldc.i4.0 IL_002a: stloc.1 IL_002b: ldloc.1 IL_002c: ret } "); compVerifier.VerifyIL("C.M2", @" { // Code size 45 (0x2d) .maxstack 2 .locals init (char V_0) //c IL_0000: ldarg.0 IL_0001: isinst ""char"" IL_0006: brfalse.s IL_002b IL_0008: ldarg.0 IL_0009: unbox.any ""char"" IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: ldc.i4.s 65 IL_0012: blt.s IL_0019 IL_0014: ldloc.0 IL_0015: ldc.i4.s 90 IL_0017: ble.s IL_0029 IL_0019: ldloc.0 IL_001a: ldc.i4.s 97 IL_001c: blt.s IL_0027 IL_001e: ldloc.0 IL_001f: ldc.i4.s 122 IL_0021: cgt IL_0023: ldc.i4.0 IL_0024: ceq IL_0026: ret IL_0027: ldc.i4.0 IL_0028: ret IL_0029: ldc.i4.1 IL_002a: ret IL_002b: ldc.i4.0 IL_002c: ret } "); } [Fact] public void IsPatternDisjunct_04() { var source = @" using System; class C { static int M1(char c) => (c is >= 'A' and <= 'Z' or >= 'a' and <= 'z') ? 1 : 0; static int M2(char c) => (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') ? 1 : 0; public static void Main() { Console.Write(M1('A')); Console.Write(M1('0')); Console.Write(M1('q')); Console.Write(M1(' ')); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); var expectedOutput = @"1010"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @" { // Code size 38 (0x26) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: ldc.i4.s 97 IL_0003: blt.s IL_000c IL_0005: ldarg.0 IL_0006: ldc.i4.s 122 IL_0008: ble.s IL_0018 IL_000a: br.s IL_001c IL_000c: ldarg.0 IL_000d: ldc.i4.s 65 IL_000f: blt.s IL_001c IL_0011: ldarg.0 IL_0012: ldc.i4.s 90 IL_0014: ble.s IL_0018 IL_0016: br.s IL_001c IL_0018: ldc.i4.1 IL_0019: stloc.0 IL_001a: br.s IL_001e IL_001c: ldc.i4.0 IL_001d: stloc.0 IL_001e: ldloc.0 IL_001f: brtrue.s IL_0024 IL_0021: ldc.i4.0 IL_0022: br.s IL_0025 IL_0024: ldc.i4.1 IL_0025: ret } "); compVerifier.VerifyIL("C.M2", @" { // Code size 25 (0x19) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s 65 IL_0003: blt.s IL_000a IL_0005: ldarg.0 IL_0006: ldc.i4.s 90 IL_0008: ble.s IL_0017 IL_000a: ldarg.0 IL_000b: ldc.i4.s 97 IL_000d: blt.s IL_0014 IL_000f: ldarg.0 IL_0010: ldc.i4.s 122 IL_0012: ble.s IL_0017 IL_0014: ldc.i4.0 IL_0015: br.s IL_0018 IL_0017: ldc.i4.1 IL_0018: ret } "); compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @" { // Code size 33 (0x21) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: ldc.i4.s 97 IL_0003: blt.s IL_000c IL_0005: ldarg.0 IL_0006: ldc.i4.s 122 IL_0008: ble.s IL_0016 IL_000a: br.s IL_001a IL_000c: ldarg.0 IL_000d: ldc.i4.s 65 IL_000f: blt.s IL_001a IL_0011: ldarg.0 IL_0012: ldc.i4.s 90 IL_0014: bgt.s IL_001a IL_0016: ldc.i4.1 IL_0017: stloc.0 IL_0018: br.s IL_001c IL_001a: ldc.i4.0 IL_001b: stloc.0 IL_001c: ldloc.0 IL_001d: ldc.i4.0 IL_001e: cgt.un IL_0020: ret } "); compVerifier.VerifyIL("C.M2", @" { // Code size 24 (0x18) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s 65 IL_0003: blt.s IL_000a IL_0005: ldarg.0 IL_0006: ldc.i4.s 90 IL_0008: ble.s IL_0016 IL_000a: ldarg.0 IL_000b: ldc.i4.s 97 IL_000d: blt.s IL_0014 IL_000f: ldarg.0 IL_0010: ldc.i4.s 122 IL_0012: ble.s IL_0016 IL_0014: ldc.i4.0 IL_0015: ret IL_0016: ldc.i4.1 IL_0017: ret } "); } [Fact] public void IsPatternDisjunct_05() { var source = @" using System; class C { static int M1(char c) { if (c is >= 'A' and <= 'Z' or >= 'a' and <= 'z') return 1; else return 0; } static int M2(char c) { if (c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z') return 1; else return 0; } public static void Main() { Console.Write(M1('A')); Console.Write(M1('0')); Console.Write(M1('q')); Console.Write(M1(' ')); } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); var expectedOutput = @"1010"; var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @" { // Code size 46 (0x2e) .maxstack 2 .locals init (bool V_0, bool V_1, int V_2) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.s 97 IL_0004: blt.s IL_000d IL_0006: ldarg.0 IL_0007: ldc.i4.s 122 IL_0009: ble.s IL_0019 IL_000b: br.s IL_001d IL_000d: ldarg.0 IL_000e: ldc.i4.s 65 IL_0010: blt.s IL_001d IL_0012: ldarg.0 IL_0013: ldc.i4.s 90 IL_0015: ble.s IL_0019 IL_0017: br.s IL_001d IL_0019: ldc.i4.1 IL_001a: stloc.0 IL_001b: br.s IL_001f IL_001d: ldc.i4.0 IL_001e: stloc.0 IL_001f: ldloc.0 IL_0020: stloc.1 IL_0021: ldloc.1 IL_0022: brfalse.s IL_0028 IL_0024: ldc.i4.1 IL_0025: stloc.2 IL_0026: br.s IL_002c IL_0028: ldc.i4.0 IL_0029: stloc.2 IL_002a: br.s IL_002c IL_002c: ldloc.2 IL_002d: ret } "); compVerifier.VerifyIL("C.M2", @" { // Code size 44 (0x2c) .maxstack 2 .locals init (bool V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.s 65 IL_0004: blt.s IL_000b IL_0006: ldarg.0 IL_0007: ldc.i4.s 90 IL_0009: ble.s IL_001d IL_000b: ldarg.0 IL_000c: ldc.i4.s 97 IL_000e: blt.s IL_001a IL_0010: ldarg.0 IL_0011: ldc.i4.s 122 IL_0013: cgt IL_0015: ldc.i4.0 IL_0016: ceq IL_0018: br.s IL_001b IL_001a: ldc.i4.0 IL_001b: br.s IL_001e IL_001d: ldc.i4.1 IL_001e: stloc.0 IL_001f: ldloc.0 IL_0020: brfalse.s IL_0026 IL_0022: ldc.i4.1 IL_0023: stloc.1 IL_0024: br.s IL_002a IL_0026: ldc.i4.0 IL_0027: stloc.1 IL_0028: br.s IL_002a IL_002a: ldloc.1 IL_002b: ret } "); compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics(); compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @" { // Code size 35 (0x23) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 IL_0001: ldc.i4.s 97 IL_0003: blt.s IL_000c IL_0005: ldarg.0 IL_0006: ldc.i4.s 122 IL_0008: ble.s IL_0016 IL_000a: br.s IL_001a IL_000c: ldarg.0 IL_000d: ldc.i4.s 65 IL_000f: blt.s IL_001a IL_0011: ldarg.0 IL_0012: ldc.i4.s 90 IL_0014: bgt.s IL_001a IL_0016: ldc.i4.1 IL_0017: stloc.0 IL_0018: br.s IL_001c IL_001a: ldc.i4.0 IL_001b: stloc.0 IL_001c: ldloc.0 IL_001d: brfalse.s IL_0021 IL_001f: ldc.i4.1 IL_0020: ret IL_0021: ldc.i4.0 IL_0022: ret } "); compVerifier.VerifyIL("C.M2", @" { // Code size 24 (0x18) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldc.i4.s 65 IL_0003: blt.s IL_000a IL_0005: ldarg.0 IL_0006: ldc.i4.s 90 IL_0008: ble.s IL_0014 IL_000a: ldarg.0 IL_000b: ldc.i4.s 97 IL_000d: blt.s IL_0016 IL_000f: ldarg.0 IL_0010: ldc.i4.s 122 IL_0012: bgt.s IL_0016 IL_0014: ldc.i4.1 IL_0015: ret IL_0016: ldc.i4.0 IL_0017: ret } "); } [Fact, WorkItem(46536, "https://github.com/dotnet/roslyn/issues/46536")] public void MultiplePathsToNode_SwitchDispatch_01() { var source = @" using System; class Program { static void Main() { Console.Write(M("""", 0)); // 0 Console.Write(M("""", 1)); // 1 Console.Write(M("""", 2)); // 2 Console.Write(M("""", 3)); // 3 Console.Write(M(""a"", 2)); // 2 Console.Write(M(""a"", 10)); // 3 } static int M(string x, int y) { return (x, y) switch { ("""", 0) => 0, ("""", 1) => 1, (_, 2) => 2, _ => 3 }; } } "; var verifier = CompileAndVerify(source, expectedOutput: "012323", options: TestOptions.DebugExe); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", @"{ // Code size 70 (0x46) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: nop IL_0001: ldc.i4.1 IL_0002: brtrue.s IL_0005 IL_0004: nop IL_0005: ldarg.0 IL_0006: ldstr """" IL_000b: call ""bool string.op_Equality(string, string)"" IL_0010: brfalse.s IL_0026 IL_0012: ldarg.1 IL_0013: switch ( IL_002c, IL_0030, IL_0034) IL_0024: br.s IL_0038 IL_0026: ldarg.1 IL_0027: ldc.i4.2 IL_0028: beq.s IL_0034 IL_002a: br.s IL_0038 IL_002c: ldc.i4.0 IL_002d: stloc.0 IL_002e: br.s IL_003c IL_0030: ldc.i4.1 IL_0031: stloc.0 IL_0032: br.s IL_003c IL_0034: ldc.i4.2 IL_0035: stloc.0 IL_0036: br.s IL_003c IL_0038: ldc.i4.3 IL_0039: stloc.0 IL_003a: br.s IL_003c IL_003c: ldc.i4.1 IL_003d: brtrue.s IL_0040 IL_003f: nop IL_0040: ldloc.0 IL_0041: stloc.1 IL_0042: br.s IL_0044 IL_0044: ldloc.1 IL_0045: ret }"); } [Fact, WorkItem(46536, "https://github.com/dotnet/roslyn/issues/46536")] public void MultiplePathsToNode_SwitchDispatch_02() { var source = @" using System; class Program { static void Main() { Console.Write(M0("""", 0)); // 0 Console.Write(M0("""", 1)); // 1 Console.Write(M0("""", 2)); // 2 Console.Write(M0("""", 3)); // 3 Console.Write(M0(""a"", 2)); // 2 Console.Write(M0(""a"", 10)); // 3 } static int M0(string x, int y) { return (x, y) switch { ("""", 0) => M1(0), ("""", 1) => M1(1), (_, 2) => M1(2), _ => M1(3) }; } static int M1(int z) { Console.Write(' '); return z; } } "; var verifier = CompileAndVerify(source, expectedOutput: " 0 1 2 3 2 3", options: TestOptions.DebugExe); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M0", @"{ // Code size 90 (0x5a) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: nop IL_0001: ldc.i4.1 IL_0002: brtrue.s IL_0005 IL_0004: nop IL_0005: ldarg.0 IL_0006: ldstr """" IL_000b: call ""bool string.op_Equality(string, string)"" IL_0010: brfalse.s IL_0026 IL_0012: ldarg.1 IL_0013: switch ( IL_002c, IL_0035, IL_003e) IL_0024: br.s IL_0047 IL_0026: ldarg.1 IL_0027: ldc.i4.2 IL_0028: beq.s IL_003e IL_002a: br.s IL_0047 IL_002c: ldc.i4.0 IL_002d: call ""int Program.M1(int)"" IL_0032: stloc.0 IL_0033: br.s IL_0050 IL_0035: ldc.i4.1 IL_0036: call ""int Program.M1(int)"" IL_003b: stloc.0 IL_003c: br.s IL_0050 IL_003e: ldc.i4.2 IL_003f: call ""int Program.M1(int)"" IL_0044: stloc.0 IL_0045: br.s IL_0050 IL_0047: ldc.i4.3 IL_0048: call ""int Program.M1(int)"" IL_004d: stloc.0 IL_004e: br.s IL_0050 IL_0050: ldc.i4.1 IL_0051: brtrue.s IL_0054 IL_0053: nop IL_0054: ldloc.0 IL_0055: stloc.1 IL_0056: br.s IL_0058 IL_0058: ldloc.1 IL_0059: ret }"); } [Fact, WorkItem(46536, "https://github.com/dotnet/roslyn/issues/46536")] public void MultiplePathsToNode_SwitchDispatch_03() { var source = @" using System; class Program { static void Main() { Console.Write(M("""", 0)); // 0 Console.Write(M("""", 1)); // 1 Console.Write(M("""", 2)); // 2 Console.Write(M("""", 3)); // 3 Console.Write(M("""", 4)); // 4 Console.Write(M("""", 5)); // 5 Console.Write(M(""a"", 2)); // 2 Console.Write(M(""a"", 3)); // 3 Console.Write(M(""a"", 4)); // 4 Console.Write(M(""a"", 10)); // 5 } static int M(string x, int y) { return (x, y) switch { ("""", 0) => 0, ("""", 1) => 1, (_, 2) => 2, (_, 3) => 3, (_, 4) => 4, _ => 5 }; } } "; var verifier = CompileAndVerify(source, expectedOutput: "0123452345", options: TestOptions.DebugExe); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", @"{ // Code size 98 (0x62) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: nop IL_0001: ldc.i4.1 IL_0002: brtrue.s IL_0005 IL_0004: nop IL_0005: ldarg.0 IL_0006: ldstr """" IL_000b: call ""bool string.op_Equality(string, string)"" IL_0010: brfalse.s IL_002e IL_0012: ldarg.1 IL_0013: switch ( IL_0040, IL_0044, IL_0048, IL_004c, IL_0050) IL_002c: br.s IL_0054 IL_002e: ldarg.1 IL_002f: ldc.i4.2 IL_0030: beq.s IL_0048 IL_0032: br.s IL_0034 IL_0034: ldarg.1 IL_0035: ldc.i4.3 IL_0036: beq.s IL_004c IL_0038: br.s IL_003a IL_003a: ldarg.1 IL_003b: ldc.i4.4 IL_003c: beq.s IL_0050 IL_003e: br.s IL_0054 IL_0040: ldc.i4.0 IL_0041: stloc.0 IL_0042: br.s IL_0058 IL_0044: ldc.i4.1 IL_0045: stloc.0 IL_0046: br.s IL_0058 IL_0048: ldc.i4.2 IL_0049: stloc.0 IL_004a: br.s IL_0058 IL_004c: ldc.i4.3 IL_004d: stloc.0 IL_004e: br.s IL_0058 IL_0050: ldc.i4.4 IL_0051: stloc.0 IL_0052: br.s IL_0058 IL_0054: ldc.i4.5 IL_0055: stloc.0 IL_0056: br.s IL_0058 IL_0058: ldc.i4.1 IL_0059: brtrue.s IL_005c IL_005b: nop IL_005c: ldloc.0 IL_005d: stloc.1 IL_005e: br.s IL_0060 IL_0060: ldloc.1 IL_0061: ret }"); } [Fact, WorkItem(46536, "https://github.com/dotnet/roslyn/issues/46536")] public void MultiplePathsToNode_SwitchDispatch_04() { var source = @" using System; class Program { static void Main() { Console.Write(M(""a"", ""x"")); // 0 Console.Write(M(""a"", ""y"")); // 1 Console.Write(M(""a"", ""z"")); // 2 Console.Write(M(""a"", ""w"")); // 3 Console.Write(M(""b"", ""z"")); // 2 Console.Write(M(""c"", ""z"")); // 3 Console.Write(M(""c"", ""w"")); // 3 } static int M(string x, string y) { return (x, y) switch { (""a"", ""x"") => 0, (""a"", ""y"") => 1, (""a"" or ""b"", ""z"") => 2, _ => 3 }; } } "; var verifier = CompileAndVerify(source, expectedOutput: "0123233", options: TestOptions.DebugExe, parseOptions: TestOptions.RegularPreview); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", @"{ // Code size 115 (0x73) .maxstack 2 .locals init (int V_0, int V_1) IL_0000: nop IL_0001: ldc.i4.1 IL_0002: brtrue.s IL_0005 IL_0004: nop IL_0005: ldarg.0 IL_0006: ldstr ""a"" IL_000b: call ""bool string.op_Equality(string, string)"" IL_0010: brtrue.s IL_0021 IL_0012: ldarg.0 IL_0013: ldstr ""b"" IL_0018: call ""bool string.op_Equality(string, string)"" IL_001d: brtrue.s IL_004a IL_001f: br.s IL_0065 IL_0021: ldarg.1 IL_0022: ldstr ""z"" IL_0027: call ""bool string.op_Equality(string, string)"" IL_002c: brtrue.s IL_0061 IL_002e: ldarg.1 IL_002f: ldstr ""x"" IL_0034: call ""bool string.op_Equality(string, string)"" IL_0039: brtrue.s IL_0059 IL_003b: ldarg.1 IL_003c: ldstr ""y"" IL_0041: call ""bool string.op_Equality(string, string)"" IL_0046: brtrue.s IL_005d IL_0048: br.s IL_0065 IL_004a: ldarg.1 IL_004b: ldstr ""z"" IL_0050: call ""bool string.op_Equality(string, string)"" IL_0055: brtrue.s IL_0061 IL_0057: br.s IL_0065 IL_0059: ldc.i4.0 IL_005a: stloc.0 IL_005b: br.s IL_0069 IL_005d: ldc.i4.1 IL_005e: stloc.0 IL_005f: br.s IL_0069 IL_0061: ldc.i4.2 IL_0062: stloc.0 IL_0063: br.s IL_0069 IL_0065: ldc.i4.3 IL_0066: stloc.0 IL_0067: br.s IL_0069 IL_0069: ldc.i4.1 IL_006a: brtrue.s IL_006d IL_006c: nop IL_006d: ldloc.0 IL_006e: stloc.1 IL_006f: br.s IL_0071 IL_0071: ldloc.1 IL_0072: ret }"); } [Fact, WorkItem(46536, "https://github.com/dotnet/roslyn/issues/46536")] public void MultiplePathsToNode_SwitchDispatch_05() { var source = @" using System; public enum EnumA { A, B, C } public enum EnumB { X, Y, Z } public class Class1 { public string Repro(EnumA a, EnumB b) => (a, b) switch { (EnumA.A, EnumB.X) => ""AX"", (_, EnumB.Y) => ""_Y"", (EnumA.B, EnumB.X) => ""BZ"", (_, EnumB.Z) => ""_Z"", (_, _) => throw new ArgumentException() }; } "; var verifier = CompileAndVerify(source, options: TestOptions.DebugDll); verifier.VerifyDiagnostics(); verifier.VerifyIL("Class1.Repro", @"{ // Code size 90 (0x5a) .maxstack 2 .locals init (string V_0) IL_0000: ldc.i4.1 IL_0001: brtrue.s IL_0004 IL_0003: nop IL_0004: ldarg.1 IL_0005: brtrue.s IL_001b IL_0007: ldarg.2 IL_0008: switch ( IL_002e, IL_0036, IL_0046) IL_0019: br.s IL_004e IL_001b: ldarg.2 IL_001c: ldc.i4.1 IL_001d: beq.s IL_0036 IL_001f: ldarg.1 IL_0020: ldc.i4.1 IL_0021: bne.un.s IL_0028 IL_0023: ldarg.2 IL_0024: brfalse.s IL_003e IL_0026: br.s IL_0028 IL_0028: ldarg.2 IL_0029: ldc.i4.2 IL_002a: beq.s IL_0046 IL_002c: br.s IL_004e IL_002e: ldstr ""AX"" IL_0033: stloc.0 IL_0034: br.s IL_0054 IL_0036: ldstr ""_Y"" IL_003b: stloc.0 IL_003c: br.s IL_0054 IL_003e: ldstr ""BZ"" IL_0043: stloc.0 IL_0044: br.s IL_0054 IL_0046: ldstr ""_Z"" IL_004b: stloc.0 IL_004c: br.s IL_0054 IL_004e: newobj ""System.ArgumentException..ctor()"" IL_0053: throw IL_0054: ldc.i4.1 IL_0055: brtrue.s IL_0058 IL_0057: nop IL_0058: ldloc.0 IL_0059: ret }"); } #endregion Pattern Combinators [Fact, WorkItem(62563, "https://github.com/dotnet/roslyn/issues/62563")] public void AssignToStructFieldOnClassTypeThroughCall_01() { var source = @" using System; struct Inner { public int Value { get; set; } } class Outer { public Inner Inner; } class Program { static T Id<T>(T t) => t; static void Main() { var outer = new Outer(); Console.Write(outer.Inner.Value); M1(outer); M2(outer); } static void M1(Outer outer) { Id(outer).Inner.Value = 1; Console.Write(outer.Inner.Value); } static void M2(Outer outer) { Id(outer).Inner.Value = outer switch { _ => 2 }; Console.Write(outer.Inner.Value); } } "; var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "012"); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M1", """ { // Code size 37 (0x25) .maxstack 2 IL_0000: nop IL_0001: ldarg.0 IL_0002: call "Outer Program.Id<Outer>(Outer)" IL_0007: ldflda "Inner Outer.Inner" IL_000c: ldc.i4.1 IL_000d: call "void Inner.Value.set" IL_0012: nop IL_0013: ldarg.0 IL_0014: ldflda "Inner Outer.Inner" IL_0019: call "readonly int Inner.Value.get" IL_001e: call "void System.Console.Write(int)" IL_0023: nop IL_0024: ret } """); verifier.VerifyIL("Program.M2", """ { // Code size 53 (0x35) .maxstack 2 .locals init (Outer V_0, int V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "Outer Program.Id<Outer>(Outer)" IL_0007: stloc.0 IL_0008: ldc.i4.1 IL_0009: brtrue.s IL_000c IL_000b: nop IL_000c: br.s IL_000e IL_000e: ldc.i4.2 IL_000f: stloc.1 IL_0010: br.s IL_0012 IL_0012: ldc.i4.1 IL_0013: brtrue.s IL_0016 IL_0015: nop IL_0016: ldloc.0 IL_0017: ldflda "Inner Outer.Inner" IL_001c: ldloc.1 IL_001d: call "void Inner.Value.set" IL_0022: nop IL_0023: ldarg.0 IL_0024: ldflda "Inner Outer.Inner" IL_0029: call "readonly int Inner.Value.get" IL_002e: call "void System.Console.Write(int)" IL_0033: nop IL_0034: ret } """); } [Fact, WorkItem(62563, "https://github.com/dotnet/roslyn/issues/62563")] public void AssignToStructFieldOnClassTypeThroughCall_02() { var source = """ using System; var val = 0.1; var obj = new Obj(); ThrowWhenNull(obj).Color.Value = val switch { 0 => "green", _ => "red" }; Console.WriteLine($"{obj.Color.Value ?? "null"} should not be null"); ThrowWhenNull(obj).Color.Value = "yikes"; Console.WriteLine(obj.Color.Value); // yikes ThrowWhenNull(obj).Color.Value = val == 0 ? "green" : "red"; Console.WriteLine(obj.Color.Value); // red static T ThrowWhenNull<T>(T obj) => obj ?? throw new System.InvalidOperationException(); struct Wrapper<T> { private T _value; public T Value { get => _value; set => _value = value; } } class Obj { public Wrapper<string> Color; } """; var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: """ red should not be null yikes red """); verifier.VerifyDiagnostics(); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 186 (0xba) .maxstack 4 .locals init (double V_0, //val string V_1) IL_0000: ldc.r8 0.1 IL_0009: stloc.0 IL_000a: newobj "Obj..ctor()" IL_000f: dup IL_0010: call "Obj Program.<<Main>$>g__ThrowWhenNull|0_0<Obj>(Obj)" IL_0015: ldloc.0 IL_0016: ldc.r8 0 IL_001f: bne.un.s IL_0029 IL_0021: ldstr "green" IL_0026: stloc.1 IL_0027: br.s IL_002f IL_0029: ldstr "red" IL_002e: stloc.1 IL_002f: ldflda "Wrapper<string> Obj.Color" IL_0034: ldloc.1 IL_0035: call "void Wrapper<string>.Value.set" IL_003a: dup IL_003b: ldflda "Wrapper<string> Obj.Color" IL_0040: call "string Wrapper<string>.Value.get" IL_0045: dup IL_0046: brtrue.s IL_004e IL_0048: pop IL_0049: ldstr "null" IL_004e: ldstr " should not be null" IL_0053: call "string string.Concat(string, string)" IL_0058: call "void System.Console.WriteLine(string)" IL_005d: dup IL_005e: call "Obj Program.<<Main>$>g__ThrowWhenNull|0_0<Obj>(Obj)" IL_0063: ldflda "Wrapper<string> Obj.Color" IL_0068: ldstr "yikes" IL_006d: call "void Wrapper<string>.Value.set" IL_0072: dup IL_0073: ldflda "Wrapper<string> Obj.Color" IL_0078: call "string Wrapper<string>.Value.get" IL_007d: call "void System.Console.WriteLine(string)" IL_0082: dup IL_0083: call "Obj Program.<<Main>$>g__ThrowWhenNull|0_0<Obj>(Obj)" IL_0088: ldflda "Wrapper<string> Obj.Color" IL_008d: ldloc.0 IL_008e: ldc.r8 0 IL_0097: beq.s IL_00a0 IL_0099: ldstr "red" IL_009e: br.s IL_00a5 IL_00a0: ldstr "green" IL_00a5: call "void Wrapper<string>.Value.set" IL_00aa: ldflda "Wrapper<string> Obj.Color" IL_00af: call "string Wrapper<string>.Value.get" IL_00b4: call "void System.Console.WriteLine(string)" IL_00b9: ret } """); } [Fact, WorkItem(62563, "https://github.com/dotnet/roslyn/issues/62563")] public void AssignToEventFieldOnClassTypeThroughCall() { var source = @" using System; class Outer { static readonly Action A = () => { }; static T Id<T>(T t) => t; public event Action Inner; static void Main() { var outer = new Outer(); Console.Write(outer.Inner); M1(outer); outer.Inner = null; M2(outer); } static void M1(Outer outer) { Id(outer).Inner = A; Console.Write(outer.Inner); } static void M2(Outer outer) { Id(outer).Inner = outer switch { _ => A }; Console.Write(outer.Inner); } } "; var verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: "System.ActionSystem.Action"); verifier.VerifyDiagnostics(); verifier.VerifyIL("Outer.M1", """ { // Code size 30 (0x1e) .maxstack 2 IL_0000: nop IL_0001: ldarg.0 IL_0002: call "Outer Outer.Id<Outer>(Outer)" IL_0007: ldsfld "System.Action Outer.A" IL_000c: stfld "System.Action Outer.Inner" IL_0011: ldarg.0 IL_0012: ldfld "System.Action Outer.Inner" IL_0017: call "void System.Console.Write(object)" IL_001c: nop IL_001d: ret } """); verifier.VerifyIL("Outer.M2", """ { // Code size 46 (0x2e) .maxstack 2 .locals init (Outer V_0, System.Action V_1) IL_0000: nop IL_0001: ldarg.0 IL_0002: call "Outer Outer.Id<Outer>(Outer)" IL_0007: stloc.0 IL_0008: ldc.i4.1 IL_0009: brtrue.s IL_000c IL_000b: nop IL_000c: br.s IL_000e IL_000e: ldsfld "System.Action Outer.A" IL_0013: stloc.1 IL_0014: br.s IL_0016 IL_0016: ldc.i4.1 IL_0017: brtrue.s IL_001a IL_0019: nop IL_001a: ldloc.0 IL_001b: ldloc.1 IL_001c: stfld "System.Action Outer.Inner" IL_0021: ldarg.0 IL_0022: ldfld "System.Action Outer.Inner" IL_0027: call "void System.Console.Write(object)" IL_002c: nop IL_002d: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80326")] public void InterfaceOrNull() { var source = """ #nullable enable using System; Console.WriteLine(M1(null)); Console.WriteLine(M2(null)); Console.WriteLine(M3(null)); Console.WriteLine(M4(null)); Console.WriteLine(M5(null)); Console.WriteLine(M6(null)); Console.WriteLine("---"); var a = new A(); Console.WriteLine(M1(a)); Console.WriteLine(M2(a)); Console.WriteLine(M3(a)); Console.WriteLine(M4(a)); Console.WriteLine(M5(a)); Console.WriteLine(M6(a)); bool M1(A? a) => a is I or null; bool M2(A? a) => a is null or I; bool M3(A? a) => a is I || a is null; bool M4(A? a) => a is A or null; bool M5(A? a) => a is I or (null and null); bool M6(A? a) { if (a is (I or null) and var x) { x?.M(); return true; } return false; } interface I { void M(); } class A : I { void I.M() => Console.WriteLine("A.M"); } """; CompileAndVerify(source, expectedOutput: """ True True True True True True --- True True True True True A.M True """).VerifyDiagnostics( // (19,18): warning CS8794: An expression of type 'A' always matches the provided pattern. // bool M1(A? a) => a is I or null; Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is I or null").WithArguments("A").WithLocation(19, 18), // (20,18): warning CS8794: An expression of type 'A' always matches the provided pattern. // bool M2(A? a) => a is null or I; Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is null or I").WithArguments("A").WithLocation(20, 18), // (22,18): warning CS8794: An expression of type 'A' always matches the provided pattern. // bool M4(A? a) => a is A or null; Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is A or null").WithArguments("A").WithLocation(22, 18), // (23,18): warning CS8794: An expression of type 'A' always matches the provided pattern. // bool M5(A? a) => a is I or (null and null); Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is I or (null and null)").WithArguments("A").WithLocation(23, 18), // (26,9): warning CS8794: An expression of type 'A' always matches the provided pattern. // if (a is (I or null) and var x) Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is (I or null) and var x").WithArguments("A").WithLocation(26, 9)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80326")] public void BaseClassOrNull() { var source = """ #nullable enable using System; Console.WriteLine(M1(null)); Console.WriteLine(M2(null)); Console.WriteLine(M3(null)); Console.WriteLine(M4(null)); Console.WriteLine(M5(null)); Console.WriteLine(M6(null)); Console.WriteLine("---"); var a = new A(); Console.WriteLine(M1(a)); Console.WriteLine(M2(a)); Console.WriteLine(M3(a)); Console.WriteLine(M4(a)); Console.WriteLine(M5(a)); Console.WriteLine(M6(a)); bool M1(A? a) => a is I or null; bool M2(A? a) => a is null or I; bool M3(A? a) => a is I || a is null; bool M4(A? a) => a is A or null; bool M5(A? a) => a is I or (null and null); bool M6(A? a) { if (a is (I or null) and var x) { x?.M(); return true; } return false; } class I { public virtual void M() => Console.WriteLine("I.M"); } class A : I { public override void M() => Console.WriteLine("A.M"); } """; CompileAndVerify(source, expectedOutput: """ True True True True True True --- True True True True True A.M True """).VerifyDiagnostics( // (19,18): warning CS8794: An expression of type 'A' always matches the provided pattern. // bool M1(A? a) => a is I or null; Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is I or null").WithArguments("A").WithLocation(19, 18), // (20,18): warning CS8794: An expression of type 'A' always matches the provided pattern. // bool M2(A? a) => a is null or I; Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is null or I").WithArguments("A").WithLocation(20, 18), // (22,18): warning CS8794: An expression of type 'A' always matches the provided pattern. // bool M4(A? a) => a is A or null; Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is A or null").WithArguments("A").WithLocation(22, 18), // (23,18): warning CS8794: An expression of type 'A' always matches the provided pattern. // bool M5(A? a) => a is I or (null and null); Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is I or (null and null)").WithArguments("A").WithLocation(23, 18), // (26,9): warning CS8794: An expression of type 'A' always matches the provided pattern. // if (a is (I or null) and var x) Diagnostic(ErrorCode.WRN_IsPatternAlways, "a is (I or null) and var x").WithArguments("A").WithLocation(26, 9)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81173")] public void DeconstructExtension_01() { var source = """ class Program { static void Main() { System.Console.Write(Test2(new C())); } static bool Test2(C u) { return u is var (_ , (i, _)) && (int)i == 10; } static void Test3(C c, int i) { var (a, b) = c; var (u, v) = i; } } public struct C { } static class Extensions { public static void Deconstruct(this object o, out int x, out int y) { x = 10; y = 2; } } """; var verifier = CompileAndVerify(source, expectedOutput: "True").VerifyDiagnostics(); verifier.VerifyIL("Program.Test2", @" { // Code size 36 (0x24) .maxstack 3 .locals init (int V_0, //i int V_1, int V_2, int V_3) IL_0000: ldarg.0 IL_0001: box ""C"" IL_0006: ldloca.s V_1 IL_0008: ldloca.s V_2 IL_000a: call ""void Extensions.Deconstruct(object, out int, out int)"" IL_000f: ldloc.2 IL_0010: box ""int"" IL_0015: ldloca.s V_0 IL_0017: ldloca.s V_3 IL_0019: call ""void Extensions.Deconstruct(object, out int, out int)"" IL_001e: ldloc.0 IL_001f: ldc.i4.s 10 IL_0021: ceq IL_0023: ret } "); } [Fact] public void DeconstructExtension_02() { // We check conversion during initial binding var src = """ #pragma warning disable CS0436 // The type 'Span<T>' in '' conflicts with the imported type 'Span<T>' var (x, y) = new int[] { 42 }; System.Console.Write((x, y)); class C { } static class E { public static void Deconstruct(this System.Span<int> s, out int i, out int j) => throw null; } namespace System { public ref struct Span<T> { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); comp.VerifyEmitDiagnostics( // (3,1): error CS0656: Missing compiler required member 'Span<T>.op_Implicit' // var (x, y) = new int[] { 42 }; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "var (x, y) = new int[] { 42 }").WithArguments("System.Span<T>", "op_Implicit").WithLocation(3, 1) ); } } }