diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs index 8a57e0daae122..46d37fdd2d589 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -998,7 +999,7 @@ private BoundExpression LowerLiftedBuiltInComparisonOperator( // Optimization: If one side is non-default constant, checking HasValue is not needed. if (kind.Operator() is BinaryOperatorKind.Equal or BinaryOperatorKind.NotEqual) { - if (xNonNull?.ConstantValueOpt is { IsDefaultValue: false }) + if (canNotBeEqualToDefaultValue(xNonNull?.ConstantValueOpt)) { Debug.Assert(yNonNull is null, "Handled by trivial optimization above; otherwise we should use yNonNull here."); return MakeBinaryOperator( @@ -1011,7 +1012,7 @@ private BoundExpression LowerLiftedBuiltInComparisonOperator( constrainedToTypeOpt: null); } - if (yNonNull?.ConstantValueOpt is { IsDefaultValue: false }) + if (canNotBeEqualToDefaultValue(yNonNull?.ConstantValueOpt)) { Debug.Assert(xNonNull is null, "Handled by trivial optimization above; otherwise we should use xNonNull here."); return MakeBinaryOperator( @@ -1098,6 +1099,27 @@ private BoundExpression LowerLiftedBuiltInComparisonOperator( sideEffects: ImmutableArray.Create(tempAssignmentX, tempAssignmentY), value: binaryExpression, type: boolType); + + static bool canNotBeEqualToDefaultValue( + [NotNullWhen(returnValue: true)] ConstantValue? constantValue) + { + // This is an explicit list so new constant values are not accidentally supported without consideration. + // Decimal is not in the list because it is possible to have a non-default decimal constant + // which is equal to the default decimal (0.0m == default(decimal)). + return constantValue is + { + IsDefaultValue: false, + Discriminator: ConstantValueTypeDiscriminator.Boolean + or ConstantValueTypeDiscriminator.Double + or ConstantValueTypeDiscriminator.Int32 + or ConstantValueTypeDiscriminator.Int64 + or ConstantValueTypeDiscriminator.NInt + or ConstantValueTypeDiscriminator.NUInt + or ConstantValueTypeDiscriminator.Single + or ConstantValueTypeDiscriminator.UInt32 + or ConstantValueTypeDiscriminator.UInt64 + }; + } } private BoundExpression LowerLiftedUserDefinedComparisonOperator( diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOptimizedNullableOperators.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOptimizedNullableOperators.cs index 53e966a5b90ad..dff16da5b01e6 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOptimizedNullableOperators.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenOptimizedNullableOperators.cs @@ -3391,10 +3391,59 @@ .locals init (int? V_0, verifier.VerifyIL("C.M", il); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Double_E0([CombinatorialValues("0d", "0.0")] string rhs) + { + var source = $$""" + C.Run(0d); + C.Run(0.0); + C.Run(1.0); + C.Run(1.2); + C.Run(null); + + class C + { + public static void Run(double? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(double? x) => x == {{rhs}}; + } + """; + var output = "11000"; + var il = """ + { + // Code size 31 (0x1f) + .maxstack 2 + .locals init (double? V_0, + double V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.r8 0 + IL_000b: stloc.1 + IL_000c: ldloca.s V_0 + IL_000e: call "double double?.GetValueOrDefault()" + IL_0013: ldloc.1 + IL_0014: ceq + IL_0016: ldloca.s V_0 + IL_0018: call "bool double?.HasValue.get" + IL_001d: and + IL_001e: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] public void NullableConstant_Double_G1() { var source = """ + C.Run(0d); C.Run(0.0); C.Run(1.0); C.Run(1.2); @@ -3409,7 +3458,7 @@ public static void Run(double? x) static bool M(double? x) => x > 1.0; } """; - var output = "0010"; + var output = "00010"; var il = """ { // Code size 31 (0x1f) @@ -3442,6 +3491,7 @@ .locals init (double? V_0, public void NullableConstant_Double_LE1() { var source = """ + C.Run(0d); C.Run(0.0); C.Run(1.0); C.Run(1.2); @@ -3456,7 +3506,7 @@ public static void Run(double? x) static bool M(double? x) => x <= 1.0; } """; - var output = "1100"; + var output = "11100"; var il = """ { // Code size 34 (0x22) @@ -3486,5 +3536,1585 @@ .locals init (double? V_0, verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", il); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Decimal_E0() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => x == 0m; + } + """; + var output = "1100011"; + var il = """ + { + // Code size 32 (0x20) + .maxstack 2 + .locals init (decimal? V_0, + decimal V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: initobj "decimal" + IL_000a: ldloca.s V_0 + IL_000c: call "decimal decimal?.GetValueOrDefault()" + IL_0011: ldloc.1 + IL_0012: call "bool decimal.op_Equality(decimal, decimal)" + IL_0017: ldloca.s V_0 + IL_0019: call "bool decimal?.HasValue.get" + IL_001e: and + IL_001f: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + [WorkItem("https://github.com/dotnet/roslyn/issues/73510")] + public void NullableConstant_Decimal_E00() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => x == 0.0m; + } + """; + var output = "1100011"; + var il = """ + { + // Code size 36 (0x24) + .maxstack 6 + .locals init (decimal? V_0, + decimal V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.0 + IL_0006: ldc.i4.0 + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: call "decimal..ctor(int, int, int, bool, byte)" + IL_000e: ldloca.s V_0 + IL_0010: call "decimal decimal?.GetValueOrDefault()" + IL_0015: ldloc.1 + IL_0016: call "bool decimal.op_Equality(decimal, decimal)" + IL_001b: ldloca.s V_0 + IL_001d: call "bool decimal?.HasValue.get" + IL_0022: and + IL_0023: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + [WorkItem("https://github.com/dotnet/roslyn/issues/73510")] + public void NullableConstant_Decimal_00E() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => 0.0m == x; + } + """; + var output = "1100011"; + var il = """ + { + // Code size 33 (0x21) + .maxstack 5 + .locals init (decimal? V_0) + IL_0000: ldc.i4.0 + IL_0001: ldc.i4.0 + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.1 + IL_0005: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_000a: ldarg.0 + IL_000b: stloc.0 + IL_000c: ldloca.s V_0 + IL_000e: call "decimal decimal?.GetValueOrDefault()" + IL_0013: call "bool decimal.op_Equality(decimal, decimal)" + IL_0018: ldloca.s V_0 + IL_001a: call "bool decimal?.HasValue.get" + IL_001f: and + IL_0020: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + [WorkItem("https://github.com/dotnet/roslyn/issues/73510")] + public void NullableConstant_Decimal_E000() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => x == 0.00m; + } + """; + var output = "1100011"; + var il = """ + { + // Code size 36 (0x24) + .maxstack 6 + .locals init (decimal? V_0, + decimal V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.0 + IL_0006: ldc.i4.0 + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.2 + IL_0009: call "decimal..ctor(int, int, int, bool, byte)" + IL_000e: ldloca.s V_0 + IL_0010: call "decimal decimal?.GetValueOrDefault()" + IL_0015: ldloc.1 + IL_0016: call "bool decimal.op_Equality(decimal, decimal)" + IL_001b: ldloca.s V_0 + IL_001d: call "bool decimal?.HasValue.get" + IL_0022: and + IL_0023: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + [WorkItem("https://github.com/dotnet/roslyn/issues/73510")] + public void NullableConstant_Decimal_000E() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => 0.00m == x; + } + """; + var output = "1100011"; + var il = """ + { + // Code size 33 (0x21) + .maxstack 5 + .locals init (decimal? V_0) + IL_0000: ldc.i4.0 + IL_0001: ldc.i4.0 + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.2 + IL_0005: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_000a: ldarg.0 + IL_000b: stloc.0 + IL_000c: ldloca.s V_0 + IL_000e: call "decimal decimal?.GetValueOrDefault()" + IL_0013: call "bool decimal.op_Equality(decimal, decimal)" + IL_0018: ldloca.s V_0 + IL_001a: call "bool decimal?.HasValue.get" + IL_001f: and + IL_0020: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Decimal_NE00() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => x != 0.0m; + } + """; + var output = "0011100"; + var il = """ + { + // Code size 39 (0x27) + .maxstack 6 + .locals init (decimal? V_0, + decimal V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.0 + IL_0006: ldc.i4.0 + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: call "decimal..ctor(int, int, int, bool, byte)" + IL_000e: ldloca.s V_0 + IL_0010: call "decimal decimal?.GetValueOrDefault()" + IL_0015: ldloc.1 + IL_0016: call "bool decimal.op_Equality(decimal, decimal)" + IL_001b: ldloca.s V_0 + IL_001d: call "bool decimal?.HasValue.get" + IL_0022: and + IL_0023: ldc.i4.0 + IL_0024: ceq + IL_0026: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Decimal_00NE() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => 0.0m != x; + } + """; + var output = "0011100"; + var il = """ + { + // Code size 36 (0x24) + .maxstack 5 + .locals init (decimal? V_0) + IL_0000: ldc.i4.0 + IL_0001: ldc.i4.0 + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.1 + IL_0005: newobj "decimal..ctor(int, int, int, bool, byte)" + IL_000a: ldarg.0 + IL_000b: stloc.0 + IL_000c: ldloca.s V_0 + IL_000e: call "decimal decimal?.GetValueOrDefault()" + IL_0013: call "bool decimal.op_Equality(decimal, decimal)" + IL_0018: ldloca.s V_0 + IL_001a: call "bool decimal?.HasValue.get" + IL_001f: and + IL_0020: ldc.i4.0 + IL_0021: ceq + IL_0023: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Decimal_GE0() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => x >= 0m; + } + """; + var output = "1111011"; + var il = """ + { + // Code size 32 (0x20) + .maxstack 2 + .locals init (decimal? V_0, + decimal V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: initobj "decimal" + IL_000a: ldloca.s V_0 + IL_000c: call "decimal decimal?.GetValueOrDefault()" + IL_0011: ldloc.1 + IL_0012: call "bool decimal.op_GreaterThanOrEqual(decimal, decimal)" + IL_0017: ldloca.s V_0 + IL_0019: call "bool decimal?.HasValue.get" + IL_001e: and + IL_001f: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Decimal_GE00() + { + var source = """ + C.Run(0m); + C.Run(0.0m); + C.Run(1m); + C.Run(2m); + C.Run(null); + C.Run(default(decimal)); + C.Run(decimal.Zero); + + class C + { + public static void Run(decimal? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(decimal? x) => x >= 0.0m; + } + """; + var output = "1111011"; + var il = """ + { + // Code size 36 (0x24) + .maxstack 6 + .locals init (decimal? V_0, + decimal V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.0 + IL_0006: ldc.i4.0 + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: call "decimal..ctor(int, int, int, bool, byte)" + IL_000e: ldloca.s V_0 + IL_0010: call "decimal decimal?.GetValueOrDefault()" + IL_0015: ldloc.1 + IL_0016: call "bool decimal.op_GreaterThanOrEqual(decimal, decimal)" + IL_001b: ldloca.s V_0 + IL_001d: call "bool decimal?.HasValue.get" + IL_0022: and + IL_0023: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Byte_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(byte? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(byte? x) => x == (byte)0; + } + """; + var output = "1000"; + var il = """ + { + // Code size 56 (0x38) + .maxstack 2 + .locals init (int? V_0, + int V_1, + byte? V_2, + int? V_3) + IL_0000: ldarg.0 + IL_0001: stloc.2 + IL_0002: ldloca.s V_2 + IL_0004: call "bool byte?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_3 + IL_000d: initobj "int?" + IL_0013: ldloc.3 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_2 + IL_0018: call "byte byte?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.0 + IL_0023: ldc.i4.0 + IL_0024: stloc.1 + IL_0025: ldloca.s V_0 + IL_0027: call "int int?.GetValueOrDefault()" + IL_002c: ldloc.1 + IL_002d: ceq + IL_002f: ldloca.s V_0 + IL_0031: call "bool int?.HasValue.get" + IL_0036: and + IL_0037: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Byte_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(byte? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(byte? x) => x == 1; + } + """; + var output = "0100"; + var il = """ + { + // Code size 46 (0x2e) + .maxstack 2 + .locals init (byte? V_0, + int? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call "bool byte?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj "int?" + IL_0013: ldloc.1 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_0 + IL_0018: call "byte byte?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.1 + IL_0023: ldloca.s V_1 + IL_0025: call "int int?.GetValueOrDefault()" + IL_002a: ldc.i4.1 + IL_002b: ceq + IL_002d: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Char_E0() + { + var source = """ + C.Run('\0'); + C.Run(default(char)); + C.Run('A'); + C.Run('\x1'); + C.Run(null); + + class C + { + public static void Run(char? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(char? x) => x == '\0'; + } + """; + var output = "11000"; + var il = """ + { + // Code size 56 (0x38) + .maxstack 2 + .locals init (int? V_0, + int V_1, + char? V_2, + int? V_3) + IL_0000: ldarg.0 + IL_0001: stloc.2 + IL_0002: ldloca.s V_2 + IL_0004: call "bool char?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_3 + IL_000d: initobj "int?" + IL_0013: ldloc.3 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_2 + IL_0018: call "char char?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.0 + IL_0023: ldc.i4.0 + IL_0024: stloc.1 + IL_0025: ldloca.s V_0 + IL_0027: call "int int?.GetValueOrDefault()" + IL_002c: ldloc.1 + IL_002d: ceq + IL_002f: ldloca.s V_0 + IL_0031: call "bool int?.HasValue.get" + IL_0036: and + IL_0037: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Char_E1() + { + var source = """ + C.Run('\0'); + C.Run(default(char)); + C.Run('A'); + C.Run('\x1'); + C.Run(null); + + class C + { + public static void Run(char? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(char? x) => x == '\x1'; + } + """; + var output = "00010"; + var il = """ + { + // Code size 56 (0x38) + .maxstack 2 + .locals init (int? V_0, + int V_1, + char? V_2, + int? V_3) + IL_0000: ldarg.0 + IL_0001: stloc.2 + IL_0002: ldloca.s V_2 + IL_0004: call "bool char?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_3 + IL_000d: initobj "int?" + IL_0013: ldloc.3 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_2 + IL_0018: call "char char?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.0 + IL_0023: ldc.i4.1 + IL_0024: stloc.1 + IL_0025: ldloca.s V_0 + IL_0027: call "int int?.GetValueOrDefault()" + IL_002c: ldloc.1 + IL_002d: ceq + IL_002f: ldloca.s V_0 + IL_0031: call "bool int?.HasValue.get" + IL_0036: and + IL_0037: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_UInt_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(uint? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(uint? x) => x == 0U; + } + """; + var output = "1000"; + var il = """ + { + // Code size 23 (0x17) + .maxstack 2 + .locals init (uint? V_0, + uint V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + IL_0004: ldloca.s V_0 + IL_0006: call "uint uint?.GetValueOrDefault()" + IL_000b: ldloc.1 + IL_000c: ceq + IL_000e: ldloca.s V_0 + IL_0010: call "bool uint?.HasValue.get" + IL_0015: and + IL_0016: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_UInt_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(uint? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(uint? x) => x == 1U; + } + """; + var output = "0100"; + var il = """ + { + // Code size 11 (0xb) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: call "uint uint?.GetValueOrDefault()" + IL_0007: ldc.i4.1 + IL_0008: ceq + IL_000a: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Short_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(short? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(short? x) => x == (short)0; + } + """; + var output = "1000"; + var il = """ + { + // Code size 56 (0x38) + .maxstack 2 + .locals init (int? V_0, + int V_1, + short? V_2, + int? V_3) + IL_0000: ldarg.0 + IL_0001: stloc.2 + IL_0002: ldloca.s V_2 + IL_0004: call "bool short?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_3 + IL_000d: initobj "int?" + IL_0013: ldloc.3 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_2 + IL_0018: call "short short?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.0 + IL_0023: ldc.i4.0 + IL_0024: stloc.1 + IL_0025: ldloca.s V_0 + IL_0027: call "int int?.GetValueOrDefault()" + IL_002c: ldloc.1 + IL_002d: ceq + IL_002f: ldloca.s V_0 + IL_0031: call "bool int?.HasValue.get" + IL_0036: and + IL_0037: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Short_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(short? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(short? x) => x == 1; + } + """; + var output = "0100"; + var il = """ + { + // Code size 46 (0x2e) + .maxstack 2 + .locals init (short? V_0, + int? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call "bool short?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj "int?" + IL_0013: ldloc.1 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_0 + IL_0018: call "short short?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.1 + IL_0023: ldloca.s V_1 + IL_0025: call "int int?.GetValueOrDefault()" + IL_002a: ldc.i4.1 + IL_002b: ceq + IL_002d: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_UShort_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(ushort? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(ushort? x) => x == (ushort)0; + } + """; + var output = "1000"; + var il = """ + { + // Code size 56 (0x38) + .maxstack 2 + .locals init (int? V_0, + int V_1, + ushort? V_2, + int? V_3) + IL_0000: ldarg.0 + IL_0001: stloc.2 + IL_0002: ldloca.s V_2 + IL_0004: call "bool ushort?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_3 + IL_000d: initobj "int?" + IL_0013: ldloc.3 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_2 + IL_0018: call "ushort ushort?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.0 + IL_0023: ldc.i4.0 + IL_0024: stloc.1 + IL_0025: ldloca.s V_0 + IL_0027: call "int int?.GetValueOrDefault()" + IL_002c: ldloc.1 + IL_002d: ceq + IL_002f: ldloca.s V_0 + IL_0031: call "bool int?.HasValue.get" + IL_0036: and + IL_0037: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_UShort_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(ushort? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(ushort? x) => x == 1; + } + """; + var output = "0100"; + var il = """ + { + // Code size 46 (0x2e) + .maxstack 2 + .locals init (ushort? V_0, + int? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call "bool ushort?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj "int?" + IL_0013: ldloc.1 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_0 + IL_0018: call "ushort ushort?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.1 + IL_0023: ldloca.s V_1 + IL_0025: call "int int?.GetValueOrDefault()" + IL_002a: ldc.i4.1 + IL_002b: ceq + IL_002d: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Long_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(long? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(long? x) => x == 0L; + } + """; + var output = "1000"; + var il = """ + { + // Code size 24 (0x18) + .maxstack 2 + .locals init (long? V_0, + long V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: conv.i8 + IL_0004: stloc.1 + IL_0005: ldloca.s V_0 + IL_0007: call "long long?.GetValueOrDefault()" + IL_000c: ldloc.1 + IL_000d: ceq + IL_000f: ldloca.s V_0 + IL_0011: call "bool long?.HasValue.get" + IL_0016: and + IL_0017: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Long_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(long? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(long? x) => x == 1L; + } + """; + var output = "0100"; + var il = """ + { + // Code size 12 (0xc) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: call "long long?.GetValueOrDefault()" + IL_0007: ldc.i4.1 + IL_0008: conv.i8 + IL_0009: ceq + IL_000b: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_ULong_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(ulong? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(ulong? x) => x == 0UL; + } + """; + var output = "1000"; + var il = """ + { + // Code size 24 (0x18) + .maxstack 2 + .locals init (ulong? V_0, + ulong V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: conv.i8 + IL_0004: stloc.1 + IL_0005: ldloca.s V_0 + IL_0007: call "ulong ulong?.GetValueOrDefault()" + IL_000c: ldloc.1 + IL_000d: ceq + IL_000f: ldloca.s V_0 + IL_0011: call "bool ulong?.HasValue.get" + IL_0016: and + IL_0017: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_ULong_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(ulong? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(ulong? x) => x == 1UL; + } + """; + var output = "0100"; + var il = """ + { + // Code size 12 (0xc) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: call "ulong ulong?.GetValueOrDefault()" + IL_0007: ldc.i4.1 + IL_0008: conv.i8 + IL_0009: ceq + IL_000b: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_NInt_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(nint? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(nint? x) => x == (nint)0; + } + """; + var output = "1000"; + var il = """ + { + // Code size 24 (0x18) + .maxstack 2 + .locals init (nint? V_0, + System.IntPtr V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: conv.i + IL_0004: stloc.1 + IL_0005: ldloca.s V_0 + IL_0007: call "nint nint?.GetValueOrDefault()" + IL_000c: ldloc.1 + IL_000d: ceq + IL_000f: ldloca.s V_0 + IL_0011: call "bool nint?.HasValue.get" + IL_0016: and + IL_0017: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_NInt_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(nint? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(nint? x) => x == (nint)1; + } + """; + var output = "0100"; + var il = """ + { + // Code size 12 (0xc) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: call "nint nint?.GetValueOrDefault()" + IL_0007: ldc.i4.1 + IL_0008: conv.i + IL_0009: ceq + IL_000b: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_NUInt_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(nuint? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(nuint? x) => x == (nuint)0; + } + """; + var output = "1000"; + var il = """ + { + // Code size 24 (0x18) + .maxstack 2 + .locals init (nuint? V_0, + System.UIntPtr V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: conv.i + IL_0004: stloc.1 + IL_0005: ldloca.s V_0 + IL_0007: call "nuint nuint?.GetValueOrDefault()" + IL_000c: ldloc.1 + IL_000d: ceq + IL_000f: ldloca.s V_0 + IL_0011: call "bool nuint?.HasValue.get" + IL_0016: and + IL_0017: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_NUInt_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(nuint? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(nuint? x) => x == (nuint)1; + } + """; + var output = "0100"; + var il = """ + { + // Code size 12 (0xc) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: call "nuint nuint?.GetValueOrDefault()" + IL_0007: ldc.i4.1 + IL_0008: conv.i + IL_0009: ceq + IL_000b: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_SByte_E0() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(sbyte? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(sbyte? x) => x == (sbyte)0; + } + """; + var output = "1000"; + var il = """ + { + // Code size 56 (0x38) + .maxstack 2 + .locals init (int? V_0, + int V_1, + sbyte? V_2, + int? V_3) + IL_0000: ldarg.0 + IL_0001: stloc.2 + IL_0002: ldloca.s V_2 + IL_0004: call "bool sbyte?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_3 + IL_000d: initobj "int?" + IL_0013: ldloc.3 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_2 + IL_0018: call "sbyte sbyte?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.0 + IL_0023: ldc.i4.0 + IL_0024: stloc.1 + IL_0025: ldloca.s V_0 + IL_0027: call "int int?.GetValueOrDefault()" + IL_002c: ldloc.1 + IL_002d: ceq + IL_002f: ldloca.s V_0 + IL_0031: call "bool int?.HasValue.get" + IL_0036: and + IL_0037: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_SByte_E1() + { + var source = """ + C.Run(0); + C.Run(1); + C.Run(2); + C.Run(null); + + class C + { + public static void Run(sbyte? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(sbyte? x) => x == 1; + } + """; + var output = "0100"; + var il = """ + { + // Code size 46 (0x2e) + .maxstack 2 + .locals init (sbyte? V_0, + int? V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: call "bool sbyte?.HasValue.get" + IL_0009: brtrue.s IL_0016 + IL_000b: ldloca.s V_1 + IL_000d: initobj "int?" + IL_0013: ldloc.1 + IL_0014: br.s IL_0022 + IL_0016: ldloca.s V_0 + IL_0018: call "sbyte sbyte?.GetValueOrDefault()" + IL_001d: newobj "int?..ctor(int)" + IL_0022: stloc.1 + IL_0023: ldloca.s V_1 + IL_0025: call "int int?.GetValueOrDefault()" + IL_002a: ldc.i4.1 + IL_002b: ceq + IL_002d: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Single_E0() + { + var source = """ + C.Run(0f); + C.Run(0.0f); + C.Run(default(float)); + C.Run(1f); + C.Run(2f); + C.Run(null); + + class C + { + public static void Run(float? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(float? x) => x == 0f; + } + """; + var output = "111000"; + var il = """ + { + // Code size 27 (0x1b) + .maxstack 2 + .locals init (float? V_0, + float V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.r4 0 + IL_0007: stloc.1 + IL_0008: ldloca.s V_0 + IL_000a: call "float float?.GetValueOrDefault()" + IL_000f: ldloc.1 + IL_0010: ceq + IL_0012: ldloca.s V_0 + IL_0014: call "bool float?.HasValue.get" + IL_0019: and + IL_001a: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52629")] + public void NullableConstant_Single_E1() + { + var source = """ + C.Run(0f); + C.Run(0.0f); + C.Run(default(float)); + C.Run(1f); + C.Run(2f); + C.Run(null); + + class C + { + public static void Run(float? x) + { + System.Console.Write(M(x) ? 1 : 0); + } + static bool M(float? x) => x == 1f; + } + """; + var output = "000100"; + var il = """ + { + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: call "float float?.GetValueOrDefault()" + IL_0007: ldc.r4 1 + IL_000c: ceq + IL_000e: ret + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: output); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", il); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenStructsAndEnum.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenStructsAndEnum.cs index 63e3e41ef313a..41f0fcafaf00d 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenStructsAndEnum.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenStructsAndEnum.cs @@ -2288,7 +2288,7 @@ public static void Eval(object obj1, object obj2) compilation.VerifyIL("NullableTest.EqualEqual", @" { - // Code size 101 (0x65) + // Code size 112 (0x70) .maxstack 2 .locals init (decimal? V_0) IL_0000: ldc.i4.0 @@ -2297,27 +2297,32 @@ .locals init (decimal? V_0) IL_0007: box ""bool"" IL_000c: call ""void Test.Eval(object, object)"" IL_0011: ldsfld ""decimal decimal.One"" - IL_0016: ldsflda ""decimal? NullableTest.NULL"" - IL_001b: call ""decimal decimal?.GetValueOrDefault()"" - IL_0020: call ""bool decimal.op_Equality(decimal, decimal)"" - IL_0025: box ""bool"" - IL_002a: ldc.i4.0 - IL_002b: box ""bool"" - IL_0030: call ""void Test.Eval(object, object)"" - IL_0035: ldsfld ""decimal decimal.Zero"" - IL_003a: ldsfld ""decimal? NullableTest.NULL"" - IL_003f: stloc.0 - IL_0040: ldloca.s V_0 - IL_0042: call ""decimal decimal?.GetValueOrDefault()"" - IL_0047: call ""bool decimal.op_Equality(decimal, decimal)"" - IL_004c: ldloca.s V_0 - IL_004e: call ""bool decimal?.HasValue.get"" - IL_0053: and - IL_0054: box ""bool"" - IL_0059: ldc.i4.0 - IL_005a: box ""bool"" - IL_005f: call ""void Test.Eval(object, object)"" - IL_0064: ret + IL_0016: ldsfld ""decimal? NullableTest.NULL"" + IL_001b: stloc.0 + IL_001c: ldloca.s V_0 + IL_001e: call ""decimal decimal?.GetValueOrDefault()"" + IL_0023: call ""bool decimal.op_Equality(decimal, decimal)"" + IL_0028: ldloca.s V_0 + IL_002a: call ""bool decimal?.HasValue.get"" + IL_002f: and + IL_0030: box ""bool"" + IL_0035: ldc.i4.0 + IL_0036: box ""bool"" + IL_003b: call ""void Test.Eval(object, object)"" + IL_0040: ldsfld ""decimal decimal.Zero"" + IL_0045: ldsfld ""decimal? NullableTest.NULL"" + IL_004a: stloc.0 + IL_004b: ldloca.s V_0 + IL_004d: call ""decimal decimal?.GetValueOrDefault()"" + IL_0052: call ""bool decimal.op_Equality(decimal, decimal)"" + IL_0057: ldloca.s V_0 + IL_0059: call ""bool decimal?.HasValue.get"" + IL_005e: and + IL_005f: box ""bool"" + IL_0064: ldc.i4.0 + IL_0065: box ""bool"" + IL_006a: call ""void Test.Eval(object, object)"" + IL_006f: ret } "); }