Scala3已经放弃了一般类型投影,因为它不可靠:
编译运行时失败的代码是可能的。
(问题经过编辑以反映评论)
考虑以下scala3代码:scastie
class A:
class X:
def outer : A.this.type = A.this
class B extends A
class C extends A
val b0 = new B
val b1 = b0
val b2 = new B
val c0 = new C
val c1 = c0
val c2 = new C
val b0x : A#X = new b0.X
val pathTypeMatch = b0x match
case _ : c2.X => "c2.X"
case _ : c1.X => "c1.x"
case _ : c0.X => "c0.X"
case _ : b2.X => "b2.X"
case _ : b1.X => "b1.X"
case _ : b0.X => "b0.X"
case _ => "ELSE"
pathTypeMatch // "b1.x"
val projectionTypeMatch = b0x match
case _ : C#X => "C#X"
case _ : B#X => "B#X"
case _ : A#X => "A#X"
case _ => "ELSE"
projectionTypeMatch // "C#X" !!!
val failingTypeMatch = b0x match
case cx : C#X =>
val c : C = cx.outer // Fails at runtime
代码可以编译,但在运行时失败,因为"类B"不能强制转换为"类c"。
编译器认为C#X
和B#X
擦除为A#X
,因此case cx:C#X
分支匹配。从这里,可以合理地认为cx.outer
是C
(这是错误的),因此出现了例外。
未发出警告。不涉及TypeTest。
当查看projectionTypeMatch
的字节码时,所有3个分支的测试都是严格相同的。
在type-patterns:
(类型模式T是…)对类
C
、p.C
或T#C
的引用。此类型模式匹配给定类的任何非空实例。注意,类的前缀(如果存在)与确定类实例。
实际上,当对路径依赖类型进行匹配时,行为是预期的(pathTypeMatch
是b1.x
)。特别是,编译器必须在匹配中考虑X outer。
对于投影类型的匹配应该是一致的,不是吗?
目的是什么?
似乎在scala3中将类型投射限制为具体类型将允许一致的行为。是这样吗?
参见https://github.com/lampepfl/dotty/issues/16728.
修复https://github.com/lampepfl/dotty/pull/17136
观察到的行为不反映意图。scala3编译器已经修复:3.3.1-RC1-bin-20230327-a569057-NIGHTLY
的行为符合预期。
注意,错误行为可能被视为一般类型投影不健全的一种味道,并且不能在scala2中修复。.