Closed
Description
Minimized code
@main def test: Unit =
trait Foo:
def name: String
class Bar
trait C[+A](val a: A)
trait D[+B] extends C[B]
final class F extends D[Foo] with C[Bar](new Bar)
def get[X](c: C[X]): X = c match
case f: F =>
// Inferred: X >: Foo & Bar but f.a: Bar
f.a
val `:(`: String = get[Foo & Bar](new F).name // ClassCastException: Bar cannot be cast to Foo
Output
java.lang.ClassCastException: main$package$Bar$1 cannot be cast to main$package$Foo$1
at main$package$.test(main.scala:15)
at test.main(main.scala:1)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sbt.Run.invokeMain(Run.scala:115)
at sbt.Run.execute$1(Run.scala:79)
at sbt.Run.$anonfun$runWithLoader$4(Run.scala:92)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
at sbt.util.InterfaceUtil$$anon$1.get(InterfaceUtil.scala:10)
at sbt.TrapExit$App.run(TrapExit.scala:257)
at java.lang.Thread.run(Thread.java:748)
Expectation
The compiler should reject the code.
I do not think the problem lies within GADT reasoning since we indeed have F <: C[Foo & Bar]
, but rather in how trait parameters are handled (hence the issue title; I couldn't come up with a better title :p). Since we have A >: Foo & Bar
, F
should pass an argument of type Foo & Bar
to C
.
If we were to replace the trait parameter a
with a method, we are enforced to override a
with the correct type Foo & Bar
:
@main def test: Unit =
trait Foo:
def name: String
class Bar
trait C[+A]:
def a: A
trait D[+B] extends C[B]
final class F extends D[Foo] with C[Bar]:
override def a: Foo & Bar =
class FooBar(val name: String) extends Bar with Foo
FooBar("hello")
def get[X](c: C[X]): X = c match
case f: F => f.a
val `:)`: String = get[Foo & Bar](new F).name // "hello"