scalatypestype-boundsvolatilitynon-volatile

Cannot override a type with non-volatile upper bound



I have a compiler error in scala and I don't know what does it refer to:
Assume these declarations:

trait Abstract {
  type MyType
}
trait AInner
trait A extends Abstract{
  type MyType <: AInner
}
trait BInner {
  def bMethod : Int
}
trait B extends Abstract with A{
  override type MyType <: BInner with A#MyType
}
What I'm trying to achieve here(in trait B) is to further restrict the type MyType declared in Abstract, so any value of type MyType must extend all the MyTypes in the mixin tree.

The compiler is giving me this message(as in title): type MyType is a volatile type; cannot override a type with non-volatile upper bound. I understand, that type volatility is happening here because of type conjuction with A#MyType, the part of the error: type with non-volatile upper bound probably refers to the type declaration type MyType <: AInner, where AInner is not an abstract type thus non-volatile.

Why can't I do it? Is there a way, how to achieve my goal?


Solution

  • Removing this check in the compiler lets us shine a light on the potential for unsoundness.

    diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
    index 37a7e3c..78a8959 100644
    --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
    +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
    @@ -5128,8 +5128,7 @@ trait Typers extends Adaptations with Tags {
    
           def typedSelectFromTypeTree(tree: SelectFromTypeTree) = {
             val qual1 = typedType(tree.qualifier, mode)
    -        if (qual1.tpe.isVolatile) TypeSelectionFromVolatileTypeError(tree, qual1)
    -        else typedSelect(tree, qual1, tree.name)
    +        typedSelect(tree, qual1, tree.name)
           }
    
           def typedTypeBoundsTree(tree: TypeBoundsTree) = {
    

    Then, running the code from a compiler test case for illegal type selection for volatile types:

    scala> class A; class B extends A
    defined class A
    defined class B
    
    scala> trait C {
         |   type U
         |   trait D { type T >: B <: A }
         |   val y: (D with U)#T = new B
         | }
    defined trait C
    
    scala> class D extends C {
         |   trait E
         |   trait F { type T = E }
         |   type U = F
         |   def frob(arg : E) : E = arg
         |   frob(y)
         | }
    defined class D
    
    scala> new D
    java.lang.ClassCastException: B cannot be cast to D$E
    

    As I understand it, the issue stems from the fact that Scala doesn't have true intersection types.

    scala> type A = { type T = Int }
    defined type alias A
    
    scala> type B = { type T = String }
    defined type alias B
    
    scala> "": (A with B)#T
    res16: String = ""
    
    scala> 0: (A with B)#T
    <console>:37: error: type mismatch;
     found   : Int(0)
     required: String
                  0: (A with B)#T
                  ^
    

    This might change in the future, if the research into Dependent Object Types (DOT) bears fruit.