I suspect you'll find it's using call
instead of callvirt
if you look at the IL. The C# compiler always uses callvirt
, even for non-virtual methods, because it forces a nullity check.
Is this a bug? Well, not necessarily. It depends on what the F# language specification states about methods calls on null references. It's perfectly possible that it states that the method will be called (non-virtually) with a null "this" reference, which is exactly what has happened.
C# happens to specify that this sort of dereferencing will throw a NullReferenceException
, but that's a language choice.
I suspect the F# approach may be a little faster, due to the lack of nullity checking involved... and don't forget that null references are less "expected" in F# than in C#... that may explain the different approach taken here. Or it could just be an oversight, of course.
EDIT: I'm not an expert at reading the F# specification, but section 6.9.6 at least suggests to me that it's a bug:
6.9.6 Evaluating Method Applications
For elaborated applications of methods, the elaborated form of the expression will be
either expr.M(args) or M(args).
The (optional) expr and args are evaluated in left-to-right order and the body of the member is evaluated in an environment with formal parameters that are mapped to corresponding argument values.
If expr evaluates to null then NullReferenceException is raised.
If the method is a virtual dispatch slot (that is, a method that is declared abstract) then the body of the member is chosen according to the dispatch maps of the value of expr.
Whether this counts as an elaborated application or not is a little beyond me, I'm afraid... but I hope this has been at least somewhat helpful.