C# 9.0 'is not' pattern matching bug when not targetting .NET 5 #5358
-
This small test: object? maybeTest = new object();
if (maybeTest is not { } test) {
Assert.IsNull(maybeTest);
return;
}
Assert.IsNotNull(test); // fails here; test is null fails on the indicate line, when compiling pre .NET 5. The resulting CIL looks like this: IL_0001: newobj instance void [System.Runtime]System.Object::.ctor()
IL_0006: stloc.0 // maybeTest
IL_0007: ldloc.0 // maybeTest
IL_0008: brtrue.s IL_000f
IL_000a: ldloc.0 // maybeTest
IL_000b: stloc.1 // test
IL_000c: ldc.i4.1
IL_000d: br.s IL_0010
IL_000f: ldc.i4.0
IL_0010: stloc.2 // V_2
IL_0011: ldloc.2 // V_2
IL_0012: brfalse.s IL_001e
IL_0014: nop
IL_0015: ldloc.0 // maybeTest
IL_0016: call void [Microsoft.VisualStudio.TestPlatform.TestFramework]Microsoft.VisualStudio.TestTools.UnitTesting.Assert::IsNull(object)
IL_001b: nop
IL_001c: br.s IL_0025
IL_001e: ldloc.1 // test
IL_001f: call void [Microsoft.VisualStudio.TestPlatform.TestFramework]Microsoft.VisualStudio.TestTools.UnitTesting.Assert::IsNotNull(object)
IL_0024: nop
IL_0025: ret This only fails with reference types. Writing the same test with structs does not have a problem. Comparing the CIL, the noticeable difference is the Note that the reference value's truthy-ness should match the truthy-ness of I've tested this at dotnetfiddle.net and get the same problem when choosing "Roslyn 3.8" compiler. It works with .NET 5 compiler and doesn't compile with .NET Framework 4.72 compiler. For comparison here is the struct version which passes: int? maybeTest = 5;
if (maybeTest is not { } test) {
Assert.IsNull(maybeTest);
return;
}
Assert.IsNotNull(test); IL_0001: ldloca.s maybeTest
IL_0003: ldc.i4.5
IL_0004: call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0/*int32*/)
IL_0009: ldloca.s maybeTest
IL_000b: call instance bool valuetype [System.Runtime]System.Nullable`1<int32>::get_HasValue()
IL_0010: brfalse.s IL_0020
IL_0012: ldloca.s maybeTest
IL_0014: call instance !0/*int32*/ valuetype [System.Runtime]System.Nullable`1<int32>::GetValueOrDefault()
IL_0019: stloc.1 // test
IL_001a: ldc.i4.1
IL_001b: ldc.i4.0
IL_001c: ceq
IL_001e: br.s IL_0021
IL_0020: ldc.i4.1
IL_0021: stloc.2 // V_2
IL_0022: ldloc.2 // V_2
IL_0023: brfalse.s IL_0034
IL_0025: nop
IL_0026: ldloc.0 // maybeTest
IL_0027: box valuetype [System.Runtime]System.Nullable`1<int32>
IL_002c: call void [Microsoft.VisualStudio.TestPlatform.TestFramework]Microsoft.VisualStudio.TestTools.UnitTesting.Assert::IsNull(object)
IL_0031: nop
IL_0032: br.s IL_0040
IL_0034: ldloc.1 // test
IL_0035: box [System.Runtime]System.Int32
IL_003a: call void [Microsoft.VisualStudio.TestPlatform.TestFramework]Microsoft.VisualStudio.TestTools.UnitTesting.Assert::IsNotNull(object)
IL_003f: nop
IL_0040: ret |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
Thanks for reporting @DaveCousineau. It looks like this is dotnet/roslyn#49262 that was fixed in 16.9. |
Beta Was this translation helpful? Give feedback.
Thanks for reporting @DaveCousineau. It looks like this is dotnet/roslyn#49262 that was fixed in 16.9.