1
votes

The following code compiles:

class X[U, T <: U]

object X {
  implicit def genericX[U, T <: U]: X[U, T] = new X[U, T]
}

implicitly[X[String, String]] // compiles

When we make T covariant (class X[U, +T <: U]) it compile as well:

class X[U, +T <: U]

object X {
  implicit def genericX[U, T <: U]: X[U, T] = new X[U, T]
}

implicitly[X[String, String]] // compiles

When we make T contravariant (class X[U, -T <: U]), compiler fails to materilize implicitly[X[String, String]]. Strangely enough, it is able to materialize implicitly[X[Any, Any]]:

class X[U, -T <: U]

object X {
  implicit def genericX[U, T <: U]: X[U, T] = new X[U, T]
}

implicitly[X[Any, Any]] // compiles
implicitly[X[String, String]] // error: could not find an implicit value 

I suspect that despite an explicit type annotation, type T, being in a contravariant position, is prematurely fixed to Any. Is this the expected behaviour?

Bonus point: contravariant T does work if we fix U:

class X[-T <: String]

object X {
  implicit def genericX[T <: String]: X[T] = new X[T]
}

implicitly[X[String]] // compiles

UPDATE1: Update after response from Dmytro Mitin:

  class X[U, -T] // same behaviour for invariant T

  object X {
    implicit def genericX[U, T](implicit ev: T <:< U): X[U, T] = new X[U, T]
  }

  implicitly[X[AnyRef, AnyRef]] // compiles

  def check[T](implicit ev: X[AnyRef, T]): Unit = {}

  check[String] // compiles
  check // does not compile
In Dotty 0.24.0-RC1 the code compiles scastie.scala-lang.org/yPy9PxcvTHqbI4bbmxyuhQ This can be considered as a bug. Bugs should be reported here: github.com/scala/bug/issuesDmytro Mitin
It's interesting that the code starts to compile if we add constraint implicit def genericX[U, T <: U](implicit ev: T <:< U): X[U, T] = new X[U, T]Dmytro Mitin
I updated the question - added example with implicit ev: T <:< U.Kamil Kloch