Academic: Automatic type deduction of base types when used in covariant generics

1k views Asked by At

I stumbled upon a case in where automatic type deduction of the .NET 4.0 MS-C# compiler failed and I had to specify the type "by hand".

This is not a big problem for me, but enough to get me curious why the compiler can not automatically find the correct types for the call..

I reduced the call down to the following programm:

class Program
{
    interface GenericInterface<T> { }
    interface Covariant<out T> { }

    static void Fn<T, U>(GenericInterface<T> t, U u)
        where T : Covariant<U>
    {
    }

    class Base { }
    class Derived : Base { }

    static void Main(string[] args)
    {
        Base b = null;
        Derived d = null;
        GenericInterface<Covariant<Base>> c = null;

        Fn(c, b);                           // 1
        Fn<Covariant<Base>, Base>(c, d);    // 2
        Fn(c, d);                           // 3
    }
}

The code does not compile because of the last call to Complex, marked "// 3".

The first call is easy and straight-forward - there is no base/subclass involved. The second call just specifies all parameter "by hand".

I would have expected that the compiler automatically choose the parameter used in the second for the third call as well. Sure, the second parameter is actually given as "Derived" but this can be converted into "Base" and the first parameter needs U to be of type "Base". The where-clause should still be possible with "Base" as U because of the covariant type in the interface.

I don't know exactly the rules for generic type parameter deduction in C#, but I always assumed, it works a bit like "If there is exactly one possible assignment for the parameters, use this one. If not, refuse to compile."

Why doesn't the compiler detect the types automatically? Is this one of these "if a compiler could do this, then it also would have to be able to solve Fermat's Last Theorem" - cases? :D

1

There are 1 answers

3
Kyle On

The where-clause should still be possible with "Base" as U because of the covariant type in the interface.

I think this is where you've gone wrong. The compiler does not consider generic constraints to be part of the method signature.

So I think what might be happening in your third case is that the compiler is deducing T = Covariant<Base> and U = Derived. It then proceeds to check the generic constraint on T. Covariant<Base> doesn't implement Covariant<Derived> so the constraint is not satisfied and compilation fails.