locked
Variance Ambiguity

    Question

  • Hi,

    I have a question about semantics of CLR's variance support.
    Is the behaviour of below code well defined?

    Thanks,

    public interface ICountable<out T>  
    {
        int Count{ get; }
    }
    public class MyCollection : ICountable<string>, ICountable<FileStream>
    {  
        int ICountable<string>.Count
        {
            get { return 1; }
        }
        int ICountable<FileStream>.Count
        {
            get { return 2; }
        }
    }
    public static class Program
    {  
        static void Main(string[] args)
        {
            var col = new MyCollection();
            var countable = (ICountable<object>)(object)col;
            Console.WriteLine(countable.Count);
        }
    }
    Tuesday, October 28, 2008 2:08 AM

Answers

  • Q: Are there any plans to update the CLI spec?
    A: Note: I distinguish between "CLI" (the ECMA specification) and "CLR" (the Microsoft implementation). The issue with ambiguity has prompted us to work with Andrew Kennedy on a formally verified model of the CLI in Coq, a theorem-prover. But this is long-term work and I don't expect to see results for at least a year, if then. Our hope is to avoid similar situations in the future rather than fix this situation now.

    Q: Are there any plans to change the CLR behavior?
    A: No. We felt this ambiguity was such an edge-case that it wasn't worth the coding or testing effort. Also it'd be unfortunate to have the Microsoft implementation diverge from the ECMA spec.

    Q: Are there plans to update the CLR verifier?
    A: No, as above.

    Q: Are there any plans to update the VB or C# compiler to reduce this decidability?
    A: The VB compiler gives warnings when you declare a class that might lead to ambiguity, and also when you perform a cast that leads to ambiguity. I'm the guy who implemented these warnings, and it was a heck of a lot of work! We settled on warnings rather than errors because, well, it's now CLR behavior and other compilers might allow ambiguous declarations. Just to give you a flavor of the sort of situations that can give rise to ambiguity warnings: if we have an enumeration E backed by Integer, and a variance interface I(Of Out T, Out U), then the following two classes are ambiguous:

    Class C : Implements I(Of E(), Integer()) : Implements I(Of E(), E()) : End Class
    Class D : Implements I(Of E(), UInteger()) : Implements I(Of E(), E()) : End Class

    Why? Because in looking for CLR-produced variance ambiguity, we have to take into account ALL conversions that the CLR might do (including Enum<->Integer<->UInteger) rather than just those allowed by VB or C#. See also below...

    I started writing about these issues here, http://blogs.msdn.com/lucian, and I have several more variance-related posts in store.


    Q: Other issues?
    A: Eric's blog also has an infinite recursion case, below. The CLI is actually underspecified in this case: it doesn't say whether the cast should or should not succeed. Current CLR behavior in this case is to throw a StackOverflowException. Current VB behavior, if you do a direct cast, is to report it as a "narrowing" (i.e. a cast that might or might not succeed at runtime).

    Interface N(Of In X) : End Interface

    Class T1 : Implements N(Of N(Of T1)) : End Class

    Module Test1

        Sub Test()

            Dim t As New T1

            Dim v As N(Of T1) = CType(t, N(Of T1))  ' throws a StackOverflowException

        End Sub

    End Module

    • Marked as answer by NyaRuRu Wednesday, November 05, 2008 1:49 PM
    Tuesday, November 04, 2008 7:11 PM

All replies

  • I would vote for a runtime exception, unless a compile-time exception could be derived (ie, cannot do a variance cast of an interface that appears more than once in the type signature).  But then, perhaps the language design group has some magic?



    Keith J. Farmer [Idea Entity]
    • Edited by Keith Farmer Tuesday, October 28, 2008 4:45 AM Update signature
    Tuesday, October 28, 2008 4:44 AM
  •  Generic variance has been a feature of CLR since 2.0, it's only new in C# 4.0. So, I've tried the above code on 3.5 SP1 (compiled it as C# program without "out", then ildasm, added the covariance marker, and recompiled with ilasm). Well, what do you know - it uses whatever is first in the list of implemented interfaces. In your case, that'll be ICountable<string>.

    Of course, there's no guarantee that 4.0 runtime won't do it differently. But, given that they've already done some major runtime refactoring in 3.5 SP1, I don't think there will be any major changes. At least, none of the new language features seem to require any runtime additions.
    Tuesday, October 28, 2008 11:23 AM
  • Thanks to keith and int19h,

    I found a blog post about this ambiguity.
    Please see Eric Lippert's "Covariance and Contravariance in C#, Part Ten: Dealing With Ambiguity" and his demo code in his comment.

    interface IFoo<+T> { T Get { get; } }  
    class Bar : IFoo<Giraffe>, IFoo<Turtle> {   
        IFoo<Giraffe>.Get{ get { return new Giraffe(); } }  
        IFoo<Turtle>.Get{ get { return new Turtle(); } }  
    }  
    ...  
    IFoo<Animal> f = new Bar();  
    Console.WriteLine(f.Get().GetType().ToString()); 

    Does anyone know the current status of this issue?
    • Edited by NyaRuRu Tuesday, October 28, 2008 3:03 PM
    Tuesday, October 28, 2008 2:52 PM
  • I've just tried it on VS2010 CTP, and it exhibits the same behavior as I've described earlier: in case of ambiguity, the first matching interface in the list of base classes/interfaces list is picked, in declaration order. Whether this is by design, or they just didn't get to it yet, is an interesting question.
    Tuesday, October 28, 2008 6:29 PM
  • I finally reached an Andrew Kennedy's paper, "On Decidability of Nominal Subtyping with Variance", from Eric's 11th post of "variance" series. I think our situations are well-described in this paper.

    So my remaining question is:
    "Is there any plan to update CLR spec., CLR verifier or C# compiler to reduce this decidability in the era of .NET 4.0?"
    • Edited by NyaRuRu Tuesday, October 28, 2008 7:56 PM
    Tuesday, October 28, 2008 7:45 PM
  • Q: Are there any plans to update the CLI spec?
    A: Note: I distinguish between "CLI" (the ECMA specification) and "CLR" (the Microsoft implementation). The issue with ambiguity has prompted us to work with Andrew Kennedy on a formally verified model of the CLI in Coq, a theorem-prover. But this is long-term work and I don't expect to see results for at least a year, if then. Our hope is to avoid similar situations in the future rather than fix this situation now.

    Q: Are there any plans to change the CLR behavior?
    A: No. We felt this ambiguity was such an edge-case that it wasn't worth the coding or testing effort. Also it'd be unfortunate to have the Microsoft implementation diverge from the ECMA spec.

    Q: Are there plans to update the CLR verifier?
    A: No, as above.

    Q: Are there any plans to update the VB or C# compiler to reduce this decidability?
    A: The VB compiler gives warnings when you declare a class that might lead to ambiguity, and also when you perform a cast that leads to ambiguity. I'm the guy who implemented these warnings, and it was a heck of a lot of work! We settled on warnings rather than errors because, well, it's now CLR behavior and other compilers might allow ambiguous declarations. Just to give you a flavor of the sort of situations that can give rise to ambiguity warnings: if we have an enumeration E backed by Integer, and a variance interface I(Of Out T, Out U), then the following two classes are ambiguous:

    Class C : Implements I(Of E(), Integer()) : Implements I(Of E(), E()) : End Class
    Class D : Implements I(Of E(), UInteger()) : Implements I(Of E(), E()) : End Class

    Why? Because in looking for CLR-produced variance ambiguity, we have to take into account ALL conversions that the CLR might do (including Enum<->Integer<->UInteger) rather than just those allowed by VB or C#. See also below...

    I started writing about these issues here, http://blogs.msdn.com/lucian, and I have several more variance-related posts in store.


    Q: Other issues?
    A: Eric's blog also has an infinite recursion case, below. The CLI is actually underspecified in this case: it doesn't say whether the cast should or should not succeed. Current CLR behavior in this case is to throw a StackOverflowException. Current VB behavior, if you do a direct cast, is to report it as a "narrowing" (i.e. a cast that might or might not succeed at runtime).

    Interface N(Of In X) : End Interface

    Class T1 : Implements N(Of N(Of T1)) : End Class

    Module Test1

        Sub Test()

            Dim t As New T1

            Dim v As N(Of T1) = CType(t, N(Of T1))  ' throws a StackOverflowException

        End Sub

    End Module

    • Marked as answer by NyaRuRu Wednesday, November 05, 2008 1:49 PM
    Tuesday, November 04, 2008 7:11 PM
  • Thank you, Lucian.  I don't suppose you could ask the folks down the hallway if C# will also produce a warning in this case?
    Keith J. Farmer [Idea Entity]
    Wednesday, November 05, 2008 3:38 PM
  • Thanks Keith. I've made sure that the "powers that be" have seen your suggestion about generating a warning. I'm going to let them reply, however, as it is their bailiwick.

    - Charlie
    CSharp Community Program Manager
    Thursday, November 06, 2008 7:52 AM
    Moderator
  • I have worked closely with Lucian and the CLR team on this issue. As he mentions, it turned out to be a far deeper and more difficult problem than we anticipated. We considered all kinds of possibilities before ultimately deciding to allow the strange behaviour.

    Though I recognize that any time the language behaviour is underspecified at compile time and essentially random at runtime, that's badness, this is also a "corner case". No code that does that today works, so this would have to be new code written to take advantage of variance.

    I would like very much to steal Lucian's warning code out of the VB compiler and put it into the C# compiler; however, we have many other priorities higher than that, so no guarantees that we'll get this in.
    Thursday, November 06, 2008 2:43 PM