Skip to content

Soundness issue with variance and higher kinded types #2971

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
jpmartin2 opened this issue Aug 14, 2017 · 2 comments
Closed

Soundness issue with variance and higher kinded types #2971

jpmartin2 opened this issue Aug 14, 2017 · 2 comments

Comments

@jpmartin2
Copy link

jpmartin2 commented Aug 14, 2017

While playing around with dotty I encountered what seems to be a issue with the typechecker. Here's a minimal example where I was able to reproduce the issue:

case class Foo[+X[_]](will: X[Int]) {
  def foo[Y[_]](right: Foo[Y]) = Foo.doFoo(this, right)
}

class A[X] { def crash = true }
class B[X]

object Foo {
  def doFoo[X[_]](left: Foo[X], right: Foo[X]): Foo[X] = right

  def main(args: Array[String]): Unit = {
    val fooA = Foo(new A[Int])
    val fooB = Foo(new B[Int])
    // The type for this is inferred correctly to Foo[A|B]
    val fine = doFoo(fooA, fooB)
    // This throws a ClassCastException because fooB isn't a Foo[A]
    val oops: Foo[A] = fooA.foo(fooB)
    println(oops.will.crash)
  }
}

It seems like the type that dotty is inferring for foo is Foo[X], even though it should be Foo[X|Y], and because of this is allowing me to then use the result in ways I shouldn't be able to.

Also, I couldn't find a way to annotate foo to get it to typecheck and work. One variation I tried was this:

case class Foo[+X[_]](will: X[Int]) {
  def foo[Y[_]](right: Foo[Y]): Foo[X|Y] = Foo.foo(this, right)
}

object Foo {
  def foo[X[_],Y[_]](left: Foo[X], right: Foo[Y]): Foo[X|Y] = right
}

But I was met with

-- [E007] Type Mismatch Error: /tmp/scastie6848463800932418099/src/main/scala/main.scala:6:62 
6 |  def foo[X[_],Y[_]](left: Foo[X], right: Foo[Y]): Foo[X|Y] = right
  |                                                              ^^^^^
  |                                                   found:    Foo[Y](right)
  |                                                   required: Foo[X]
  |

I'd expect the required type should be Foo[X|Y] (or to be more precise Foo[[T] => X[T] | Y[T]]) , for which a Foo[Y] should be fine right? Or is there a problem using subtyping with higher kinded types like this?

Either way, the first example definitely seems like an issue of soundness.

@smarter
Copy link
Member

smarter commented Aug 14, 2017

Ouch, nice find! I think I figured it out the issue leading to the ClassCastException and will open a PR shortly. Your second example still fails to compile due to type inference having a hard time with unions in higher-kinded types, but let's take it one at a time.

smarter added a commit to dotty-staging/dotty that referenced this issue Aug 14, 2017
We shouldn't use `HKApply#superType` in `isRef`, because this means
taking the upper bound of abstract type constructors.
smarter added a commit to dotty-staging/dotty that referenced this issue Aug 14, 2017
We shouldn't use `HKApply#superType` in `isRef`, because this means
taking the upper bound of abstract type constructors.
smarter added a commit to dotty-staging/dotty that referenced this issue Aug 14, 2017
We shouldn't use `HKApply#superType` in `isRef`, because this means
taking the upper bound of abstract type constructors.
smarter added a commit that referenced this issue Aug 16, 2017
Fix #2971: Soudness issue with variance and higher kinded types
@smarter
Copy link
Member

smarter commented Aug 16, 2017

Opened #2989 to track a problem related to your second example.

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