The Dotty resolution algorithm was recently ported to scalac by @milessabin in https://github.com/scala/scala/pull/6037#discussion_r133001639 where this came up:
trait Foo[-T]
trait Bar[-T] extends Foo[T]
object Test {
implicit val fa: Foo[Any] = ???
implicit val ba: Bar[Int] = ???
def test: Unit = {
implicitly[Foo[Int]]
}
}
This fails because Bar[Int] is not considered more specific than Foo[Any], because the variance-flipping we do in isAsSpecificValueType is not thorough enough:
[log frontend] ==> isSubType Bar[Int] <:< Foo[Any]?
[log frontend] ==> isSubType Bar[Int] <:< Foo?
[log frontend] ==> isSubType Foo <:< Foo?
[log frontend] <== isSubType Foo <:< Foo = true
[log frontend] <== isSubType Bar[Int] <:< Foo = true
[log frontend] ==> hasMatchingMember(Bar[Int] . Foo$$T :? =+ Any), mbr: =- Int?
[log frontend] ==> isSubType =- Int <:< =+ Any?
[log frontend] ==> isSubType Any <:< Int?
[log frontend] <== isSubType Any <:< Int = false
[log frontend] <== isSubType =- Int <:< =+ Any = false
[log frontend] <== hasMatchingMember(Bar[Int] . Foo$$T :? =+ Any), mbr: =- Int = false
[log frontend] <== isSubType Bar[Int] <:< Foo[Any] = false
hasMatchingMember returns the original member T in Bar where the variance is not flipped (=- Int). We could add a special case in hasMatchingMember but then we might miss some other cases.
Personally, I've never really been convinced by the rationale for flipping only at the top-level, so I think the simplest solution would be to flip at all levels, then we could use a global mode.
@odersky I'd appreciate some clarification on this ... it's unlikely that the Scala PR will be merged if the intended semantics in Dotty are unclear.
We've agreed in our internal meeting that the current Dotty behavior is wrong and should be changed so that Bar[Int] is more specific; we've also agreed to keep the current "flip variance only at the top-level" semantics. Of course all of this might still change in the future based on user feedback, etc, which hopefully Miles' implementation will help us get!
Fixed in scalac ;-)
Most helpful comment
We've agreed in our internal meeting that the current Dotty behavior is wrong and should be changed so that
Bar[Int]is more specific; we've also agreed to keep the current "flip variance only at the top-level" semantics. Of course all of this might still change in the future based on user feedback, etc, which hopefully Miles' implementation will help us get!