Skip to content

Allowing to override an abstract type member with equality may be unsound in some cases #11130

Closed
@mario-bucev

Description

@mario-bucev

Minimized code

@main def test: Unit = {
  class Foo
  class Bar extends Foo

  trait S[-A] {
    type T >: A
  }
  trait PFoo extends S[Foo]
  trait PBar extends S[Bar] {
    override type T = Bar
  }
  class PFooBar extends PBar with PFoo {
    override type T >: Bar
  }

  def patmat[A](s: S[A]): s.T = s match {
    case p: (PFoo & s.type) => (new Foo): p.T
  }
  
  // ClassCastException: Foo cannot be cast to class Bar
  val x: Bar = patmat(new PFooBar: PBar)
}

Output

Click to expand
java.lang.ClassCastException: main$package$Foo$1 cannot be cast to main$package$Bar$1
	at main$package$.test(main.scala:21)
	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 code should be rejected, but to me, it is not clear where the problem lies. I think the issue is allowing PBar to assign Bar to T. I also find it rather strange that PFooBar#T is allowed to be a supertype of Bar, it should be rejected and we should instead have PFooBar#T >: Bar | Foo (= Foo)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions