-
Notifications
You must be signed in to change notification settings - Fork 847
Description
Add following code in a new F# console application project and run it:
type A() = class end
type B() = inherit A()
type C() = inherit A()
let toName (x: obj) =
match x with
| :? A when false -> "not possible"
| :? B -> "B"
| :? C -> "C"
| _ -> "unknown"
printfn "%A" (toName (C()))Expected behavior
This should print "C", as first case should never be matched, and we are passing in a value of type C. This works as expected with dotnet SDK up to 6.0.203.
Actual behavior
In Visual Studio 17.2.1, a warning appears on the line | :? C -> "C" : FS0026 This rule will never be matched.
This seems incorrect, and indeed it's another sign of the match expression analysis on type checks failing to recognize that B and C are two different subclasses of A.
Running the program in either VS2022 Version 17.2.1, or via dotnet SDK 6.0.300 or 7.0.100 preview 4 prints "B" .
In the example, all the classes are empty, but the issue persists if they have members, so this problem can lead to unexpected runtime null or type cast exceptions.
The issue also persists if the first case does not have when false but some other when guard that can be false. The only relevant thing to produce error is to have a type check for the base class of B.
In ILSpy, it's visible that the case returning "C" are optimized away - incorrectly:
public static string toName(object x)
{
if (x is A)
{
return "B";
}
return "unknown";
}Known workarounds
Both Visual Studio 2022 17.1.x and dotnet SDK 6.0.203 work as expected.
On affected SDK, you can separate the match expression into multiple ones, making sure that a check for a base type with a when guard then multiple checks for subtypes does not happen in a single match.
Related information
I have tested in Windows x64 only, project targeting .NET 6.0.