Skip to content

Missing check for consistency of arguments of parameterized superclasses leads to soundness bug #11018

Closed
@mario-bucev

Description

@mario-bucev

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"

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions