Skip to content

Matching against a recursive type (RecType) is unsound #11097

Closed
@mario-bucev

Description

@mario-bucev

Minimized code

@main def test: Unit = {
  class C { type T1; type T2 }

  def pmatch(s: C): s.T2 = s match {
    case p: (C { type T1 = Int; type T2 >: T1  } & s.type) =>
      (3: p.T1): p.T2
    case p: (C { type T1 = String; type T2 >: T1  } & s.type) =>
      ("this branch should be matched": p.T1): p.T2
  }

  // ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
  val x = pmatch(new C { type T1 = String; type T2 = String })
}

Output

Click to expand
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
	at main$package$.test(main.scala:12)
	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

We could expect the second branch to be matched, but due to erasure, I do not think it is possible.

I believe issuing a warning specifying that such a runtime check cannot be performed could be a consideration. I also think that (3: p.T1): p.T2 should be rejected since we cannot be certain that we have { type T1 = Int; type T2 >: T1 } (similarly for the second match branch).

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions