When I compile this simple C# code:
public interface ITestable {
public abstract static void Test();
}
public class MyTester<T> where T : ITestable {
public void RunTest() {
T.Test();
}
}
I get the following three IL opcodes for MyTester<T>.RunTest():
IL_0000: constrained. !T
IL_0006: call void ITestable::Test()
IL_000b: ret
This doesn't seem to align with what the documentation says about the constrained prefix, stating it can only appear before a callvirt instruction, but here it's before a call. You can see this on SharpLab.
This .constrained prefix seems to translate in the jitted assembly too. Using SharpLab's JitGeneric to compile MyTester for a given generic argument (see here), the assembly seems to be performing a check before it calls the Test function. I was under the impression that static abstract method calls should compile to a simple call because the compiler can tell exactly what function is being called, and I don't understand why it is more complex than that.
You should note one thing - for reference types the generics are compiled only once(compared to the value types, where for every one passed as generic parameter the separate implementation will be compiled) to prevent code bloat (see this or this) and the same compiled code is used for all reference types used as generic type parameter, so compiler does not actually know exactly which function being called.
If you update your second snippet with several value and reference types (note that implementations are a bit different so the difference becomes more clear):
You will see that for all reference types the same
MyTester`1[[System.__Canon, System.Private.CoreLib]].RunTest()is emmited while for value types you will have separate ones:Code @sharplab.io
As for the question in title - my guess is that docs were not updated to support the introduction of the static abstract methods in the interfaces (note - the docs are not always correct, even I have bumped into several such cases - for example github PR 1, github PR 2, github PR 3, or even for the IL
OpCodes- github issue for OpCodes.Ldind_I8)Created a new issue for docs @github.
UPD
As written in the answer by @Charlieface there is another piece of documentation which explains the usage of the
constrainedin this case.