Aladdin - Scala Bugtracking
[#412] project: compiler priority: medium category: bug
submitter assigned to status date submitted
Philippe Martin fixed 2005-04-01 17:13:52.0
subject Compound Types and null values make Scala unsafe
code
object Magic {

  abstract class A[T1,T2]() {
    trait C            { type T; }
    trait C1 extends C { type T = T1; }
    trait C2 extends C { type T <: T2; }

    type CX;
    val c: CX with C2 = null;

    def castA(x: c.T): T2 = x;
  }

  class B[T1,T2] extends A[T1,T2]() {
    type CX = C1;

    def castB(x: T1): T2 = castA(x);
  }

  def cast[T1,T2](v: T1): T2 =
    new B[T1,T2]().castB(v)

}

object Test {

  def main(args: Array[String]): Unit = {
    Magic.cast[String,Exception]("xyz").printStackTrace();
  }

}
what happened
The program compiles. At runtime, it raises the following error:

Exception in thread "main" java.lang.ClassCastException
	at Test$.main(test.scala:28)
	at Test.main(test.scala:27)
what expected

The method Magic.cast can be used to turn a value of any type T1 into a value of any other type T2. Note that this is done without calling the method asInstanceOf.

The cast from T1 to T2 is obtained by using the type T of the value c declared in the class Magic.A. This value has the type CX with C2. It implies that c.T is a subtype of T2 as T is bounded by T2 in class C2. Therefore castA is well-typed. In class B, the type CX is refined to C1. It implies that c.T is equal to T1 as T is equal to T1 in class C1. Therefore, castB is well-typed. The method cast is trivially well-typed as it simply creates an instance of class B and invokes castB.

To sum up, everything relies on the fact that in class B, the field c has the type C1 with C2 which implies that c.T is at the same time equal to T1 and bounded by T2. It is impossible to create a subclass of C such that its member type T is both equal to T1 and bounded by T2. This means that the type C1 with C2 is empty. This should make it impossible to initialize the field c in class B which should prevent the creation of instances of B and thus prevent any call to the method castB. Unfortunately, this isn't true because in Scala null is a value of any type even if that type is empty.

Without using null, it would still be possible to initialize c by using a non-terminating function:

def f: CX with C2 = f;
val c: CX with C2 = f;
but this prevents the creation of instances of B as their initialization would never terminate.

[back to overview]
Changes of this bug report
Martin  edited on  2006-10-26 16:54:07.0
You now get:
bug412.scala:9 error: type mismatch;
 found   : scala.Null(null)
 required: A.this.CX with A.this.C2
    val c: CX with C2 = null;
                        ^
one error found
Generally, intersections with abstract types are no longer compatible with Null.