Skip to content

Type variables in implicit result are narrowed too eagerly #6385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
neko-kai opened this issue Apr 26, 2019 · 1 comment
Closed

Type variables in implicit result are narrowed too eagerly #6385

neko-kai opened this issue Apr 26, 2019 · 1 comment

Comments

@neko-kai
Copy link
Contributor

Example:

trait Tc1[A]
trait Tc2[A] extends Tc1[A]

class PinTypeTo[K[_]]
object PinTypeTo {
  implicit val pinType: PinTypeTo[Tc2] = new PinTypeTo[Tc2]
}

class X
object X {
  implicit def Tc2Instance[F[x] >: Tc2[x]: PinTypeTo]: F[X] = new Tc2[X] {}
}

object app extends App {
  implicitly[Tc2[X]] // ok
  implicitly[Tc1[X]] // fails
}

Expected:
Search for Tc1 should succeed, since F[X] always matches Tc1[X] (Scala 2 behavior)

Actual:
Search fails with

//[error] 17 |  implicitly[Tc1[X]] // fails
//[error]    |                    ^
//[error]    |no implicit argument of type Tc1[X] was found for parameter ev of method implicitly in object DottyPredef.
//[error]    |I found:
//[error]    |
//[error]    |    X.Tc2Instance[F](/* missing */implicitly[PinTypeTo[Tc1]])
//[error]    |
//[error]    |But no implicit values were found that match type PinTypeTo[Tc1].

Seems like F is narrowed to target type Tc1 overeagerly, with all implicit summons then following that assumptions, although it should probably only be constrained to a subtype of Tc1 instead.

@odersky
Copy link
Contributor

odersky commented May 6, 2019

This is a tough one. Here's Dotty's algorithm for type checking

   F[A] <: C[B]

where F is an instantiatable type parameter:

  • try to instantiate F to C.
  • If this succeeds, continue with A <: B, A = B, or A >: B, depending on variance of C's type parameter.

This is indeed incomplete since it ignores the possibility that F might be a true subtype of C. But if we admit this possibility, and just decompose to F <: C, A <: B, we become unsound. Indeed, consider:

  class C[X]
  class D[X] extends C[Int]

  F[X] <: C[String]

If we decompose this to F <: C, X <: String and later instantiate F to D we violate the original constraint, i.e. after instantiation we get D[String] <: C[String] which is false. So this is a source of unsoundness. It would be great if somebody could make the effort to construct a test case along these lines. I don't have the time right now to do it.

I am not sure how else to decompose this subtyping judgment /cc @adriaanm.

EDIT: No, it's actually quite clear what should be done: Add the constraint F <:< [X] => C[X].
F cannot be instantiated to D after this, since D == [X] => D[X] !<: [X] => C[X] even though D[_] <: C[_].

odersky added a commit to dotty-staging/dotty that referenced this issue May 6, 2019
odersky added a commit to dotty-staging/dotty that referenced this issue May 6, 2019
The issue scala#6385 contains more explanations.
smarter pushed a commit to smarter/dotty that referenced this issue May 7, 2019
smarter pushed a commit to smarter/dotty that referenced this issue May 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants