Type override in a trait with self-type

Why does the trait B in the following example compile with a self-type but does not compile without it? Is there a simple explanation for this compiler behaviour? Thanks!

Welcome to Scala 2.13.3 (OpenJDK 64-Bit Server VM, Java 14.0.1).
Type in expressions for evaluation. Or try :help.

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait A { type T = Nothing }
trait B extends A { override type T = String }

// Exiting paste mode, now interpreting.

trait B extends A { override type T = String }
                              ^
<pastie>:2: error: incompatible type in overriding
type T = Nothing (defined in trait A)
(Equivalent type required when overriding a type alias.)

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait A {
self: AnyRef =>
type T = Nothing
}

trait B extends A {
self: AnyRef =>
override type T = String
}

// Exiting paste mode, now interpreting.

trait A
trait B
1 Like

Because this must be some kind of bug.

When B is a trait it may still be somewhat sound because apparently those overrides are still checked afterwards:

scala> trait A {
     |   self: AnyRef =>
     |   type T = Int
     |   def foo: T = 42
     | }
     | 
     | trait B extends A {
     |   self: AnyRef =>
     |   override type T = String
     |   override def foo: T = "foo"
     | }
trait A
trait B

scala> val a: A = new B{}
                      ^
       error: incompatible type in overriding
       type T = Int (defined in trait A)
         with override type T = String (defined in trait B) (Equivalent type required when overriding a type alias.);
        other members with override errors are: foo

However when B is a class it’s clear that this is a bug:

scala> trait A {
     |   self: AnyRef =>
     |   type T = Int
     |   def foo: T = 42
     | }
     | 
     | class B extends A {
     |   self: AnyRef =>
     |   override type T = String
     |   override def foo: T = "foo"
     | }
trait A
class B

scala> val a: A = new B()
val a: A = B@57bd5942

scala> a.foo
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
  at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:99)
  at B.foo(<console>:6)
  ... 40 elided
1 Like

Thank you very much for the second example that demonstrates that this is a bug. I almost got bitten by it in my code. :frowning:

I’m amazed that compiles. Geez.

Adding “somewhat sound” to my repertoire!

2 Likes

Fear not for Dotty will be here to help us :slight_smile:
See https://scastie.scala-lang.org/dDUNLi5aS5qPCHmWcHmXwg

1 Like