Disallow interfaces with static virtual members as type arguments · Issue #5955 · dotnet/csharplang (original) (raw)
This proposal attempts to address a type hole that has been pointed out a number of times with static virtual members, e.g. here.
Static virtual members (Proposal: static-abstracts-in-interfaces.md, tracking issue: #4436) have a restriction disallowing interfaces as type arguments where the constraint contains an interface which has static virtual members.
This is to prevent situations like this:
interface I { static abstract string P { get; } } class C where T : I { void M() { Console.WriteLine(T.P); } } new C().M(); // Error
If this were allowed, the call to C<I>().M()
would try to execute I.P
, which of course doesn't have an implementation! Instead the compiler prevents I
(and interface) from being used as a type argument, because the constraint contains an interface (also I
) that has static virtual members.
However, the situation can still occur in a way that goes undetected by the compiler:
abstract class C { public abstract void M() where U : T; public void M0() { M(); } } interface I { static abstract string P { get; } } class D : C { public override void M() => Console.WriteLine(U.P); } new D().M0(); // Exception
Here I
is not directly used as a constraint, so the compiler does not detect a violation. But it is still indirectly used as a constraint, when substituted for the type parameter T
which is used as a constraint for U
.
The runtime protects against this case by throwing an exception when M0
tries to instantiate M<>
with I
, but it would be better if the compiler could prevent it.
I propose that we change the rule so that:
An interface containing or inheriting static virtual members cannot be used as a type argument
This simple rule protects the assumption that any type used as a type argument satisfies itself as a constraint. That is the assumption behind the language allowing e.g. the M<T>
instantiation above, where the type parameter U
is constrained by T
itself.
It is possible that this restriction would limit some useful scenarios, but it might be better to be restrictive now and then find mitigations in the future if and when we find specific common situations that are overly constrained by it.