-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Missing check for consistency of arguments of parameterized superclasses leads to soundness bug #11018
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Thanks for reporting. It looks like the parent constructor call to the parametrized trait |
@smarter is it possible to recheck the constructor applications of parametrized traits after unification of class F extends D[Foo & Bar] with C[Foo & Bar](new Bar) |
First, we can reproduce the problem with a parameterized class instead of a trait, which means we can see if it compiles with Scala 2: trait Foo {
def name: String
}
class Bar
class C[+A](val a: A)
trait D[+B] extends C[B]
final class F extends C[Bar](new Bar) with D[Foo]
object Test {
def main(args: Array[String]): Unit = {
def get[X](c: C[X]): X = c match {
case f: F =>
// Inferred: X >: Foo & Bar but f.a: Bar
f.a
}
val x: String = get[Foo with Bar](new F).name // ClassCastException: Bar cannot be cast to Foo
}
} Turns out it doesn't: i11018.scala:8: error: illegal inheritance;
class F inherits different type instances of class C:
C[Foo] and C[Bar]
final class F extends C[Bar](new Bar) with D[Foo]
^
1 error So it looks like all we have to do is port this check: https://github.com/scala/scala/blob/9bb659e62a9239c01aec14c171f8598bb1a576fe/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala#L834-L883 |
We already have related code in our own RefChecks phase, but I guess it's not complete enough: https://github.com/lampepfl/dotty/blob/a7fccd41461e749024d6d1a61de01af5cab5d8ea/compiler/src/dotty/tools/dotc/typer/RefChecks.scala#L763-L807 |
Thanks @smarter, I can take a look at it. |
Attempting to fix this issue, I stumbled upon #3989, which is roughly related -- there we have the following example: trait A[+X](val x: X)
class B[+X](val y: X) extends A[X](y)
class C extends B(5) with A[String] // error: illegal inheritance
class D extends B(5) with A[Any] // ok where the member |
I implemented a check that compares the type of the parameter at the base with the type of the parameter as seen from the inheriting subclass. For // compare B[Int](5).x.type <:< D.x.type
Int <:< Int & Any // OK
// compare B[Any](5).x.type <:< D.x.type
Any <:< Int & Any // ERROR So my additional check reports an error here, while the test expects it to be ok. @smarter, do you think it is reasonable to reject the example on these grounds? 26 | class D extends B(5) with A[Any] // ok
| ^
| illegal parameter: The types of value x do not match.
|
| value x in trait A has type: Any
| but class D expects value x to have type: Int |
In order to fix a soundness issue with parametrized classes / traits (scala#11018), we port the validation of base types from Scala 2 to Scala 3. The original implementation of the check can be found here: https://github.com/scala/scala/blob/9bb659e62a9239c01aec14c171f8598bb1a576fe/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala#L841-L882
Fixed by #11059 |
Minimized code
Output
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 haveA >: Foo & Bar
,F
should pass an argument of typeFoo & Bar
toC
.If we were to replace the trait parameter
a
with a method, we are enforced to overridea
with the correct typeFoo & Bar
:The text was updated successfully, but these errors were encountered: