diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2014-02-07 11:52:55 +0100 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-12 18:05:00 -0800 |
commit | f62e280825422c1e64c2427bc5958869662701ca (patch) | |
tree | f61e5c455851ee50f2210a898c15d6437724a3d6 | |
parent | 9c4a6e3ed7624892f46948c1c0fb57d7d5b3346e (diff) | |
download | scala-f62e280825422c1e64c2427bc5958869662701ca.tar.gz scala-f62e280825422c1e64c2427bc5958869662701ca.tar.bz2 scala-f62e280825422c1e64c2427bc5958869662701ca.zip |
SI-8244 Fix raw type regression under separate compilation
In #1901, handling of raw types encountered in signatures during class
file parsing was changed to work in the same manner as
`classExistentialType`, by using
`existentialAbstraction(cls.tparms, cls.tpe_*)`
But this never creates fresh existential symbols, and just sticks
the class type parameters it `quantified`:
scala> trait T[A <: String]
defined trait T
scala> val cls = typeOf[T[_]].typeSymbol
cls = trait T#101864
scala> cls.typeParams
res0 = List(type A#101865)
scala> cls.tpe_*
res1 = T#101864[A#101865]
scala> classExistentialType(cls)
res3 = T#101864[_ <: String#7209]
scala> val ExistentialType(quantified, result) = res3
List(type A#101865)
In the enclosed test case, this class type parameter was substituted
during `typeOf[X] memberType sym`, which led us unsoundly thinking
that `Raw[_]` was `Raw[X]`.
I've added a TODO comment to review the other usages of
`classExistentialType`.
Test variations include joint and separate compilation, and the
corresponding Scala-only code. All fail with type errors now,
as we expect. I've also added a distillation of a bootstrap
error that failed when I forgot to wrap the `existentialType`.
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 7 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Definitions.scala | 9 | ||||
-rw-r--r-- | test/files/neg/t8244.check | 4 | ||||
-rw-r--r-- | test/files/neg/t8244/Raw_1.java | 4 | ||||
-rw-r--r-- | test/files/neg/t8244/Test_2.scala | 12 | ||||
-rw-r--r-- | test/files/neg/t8244b.check | 4 | ||||
-rw-r--r-- | test/files/neg/t8244b.scala | 18 | ||||
-rw-r--r-- | test/files/neg/t8244c.check | 4 | ||||
-rw-r--r-- | test/files/neg/t8244c.scala | 18 | ||||
-rw-r--r-- | test/files/neg/t8244e.check | 4 | ||||
-rw-r--r-- | test/files/neg/t8244e/Raw.java | 4 | ||||
-rw-r--r-- | test/files/neg/t8244e/Test.scala | 12 | ||||
-rw-r--r-- | test/files/pos/t8244d/InodeBase_1.java | 6 | ||||
-rw-r--r-- | test/files/pos/t8244d/Test_2.scala | 3 |
14 files changed, 104 insertions, 5 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 664645e53e..2f9cc01c0b 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -665,8 +665,11 @@ abstract class ClassfileParser { // so have to check unsafeTypeParams.isEmpty before worrying about raw type case below, // or we'll create a boatload of needless existentials. else if (classSym.isMonomorphicType || classSym.unsafeTypeParams.isEmpty) tp - // raw type - existentially quantify all type parameters - else debuglogResult(s"raw type from $classSym")(unsafeClassExistentialType(classSym)) + else debuglogResult(s"raw type from $classSym"){ + // raw type - existentially quantify all type parameters + val eparams = typeParamsToExistentials(classSym, classSym.unsafeTypeParams) + newExistentialType(eparams, typeRef(pre, classSym, eparams.map(_.tpeHK))) + } case tp => assert(sig.charAt(index) != '<', s"sig=$sig, index=$index, tp=$tp") tp diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 78e639fdff..645d6aa4ff 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -898,12 +898,15 @@ trait Definitions extends api.StandardDefinitions { * * C[E1, ..., En] forSome { E1 >: LB1 <: UB1 ... en >: LBn <: UBn }. */ + // TODO Review the way this is used. I see two potential problems: + // 1. `existentialAbstraction` here doesn't create fresh existential type symbols, it just + // uses the class type parameter symbols directly as the list of quantified symbols. + // See SI-8244 for the trouble that this can cause. + // Compare with callers of `typeParamsToExistentials` (used in Java raw type handling) + // 2. Why don't we require a prefix? Could its omission lead to wrong results in CheckabilityChecker? def classExistentialType(clazz: Symbol): Type = existentialAbstraction(clazz.typeParams, clazz.tpe_*) - def unsafeClassExistentialType(clazz: Symbol): Type = - existentialAbstraction(clazz.unsafeTypeParams, clazz.tpe_*) - // members of class scala.Any // TODO these aren't final! They are now overriden in AnyRef/Object. Prior to the fix diff --git a/test/files/neg/t8244.check b/test/files/neg/t8244.check new file mode 100644 index 0000000000..90b2bf6f46 --- /dev/null +++ b/test/files/neg/t8244.check @@ -0,0 +1,4 @@ +Test_2.scala:9: error: value exxx is not a member of ?0 + raw.t.exxx // java.lang.ClassCastException: java.lang.String cannot be cast to X + ^ +one error found diff --git a/test/files/neg/t8244/Raw_1.java b/test/files/neg/t8244/Raw_1.java new file mode 100644 index 0000000000..0c667f1106 --- /dev/null +++ b/test/files/neg/t8244/Raw_1.java @@ -0,0 +1,4 @@ +public abstract class Raw_1<T>{ + public Raw_1 raw() { return new Raw_1<String>() { public String t() { return ""; } }; } + public abstract T t(); +} diff --git a/test/files/neg/t8244/Test_2.scala b/test/files/neg/t8244/Test_2.scala new file mode 100644 index 0000000000..152bb0b870 --- /dev/null +++ b/test/files/neg/t8244/Test_2.scala @@ -0,0 +1,12 @@ +class X extends Raw_1[X] { + override def t = this + def exxx = 0 +} + +object Test extends App { + def c(s: X) = { + val raw = s.raw + raw.t.exxx // java.lang.ClassCastException: java.lang.String cannot be cast to X + } + c(new X()) +} diff --git a/test/files/neg/t8244b.check b/test/files/neg/t8244b.check new file mode 100644 index 0000000000..f6cbf99eb5 --- /dev/null +++ b/test/files/neg/t8244b.check @@ -0,0 +1,4 @@ +t8244b.scala:15: error: value exxx is not a member of _$1 + raw.t.exxx + ^ +one error found diff --git a/test/files/neg/t8244b.scala b/test/files/neg/t8244b.scala new file mode 100644 index 0000000000..2fb4f451a1 --- /dev/null +++ b/test/files/neg/t8244b.scala @@ -0,0 +1,18 @@ +class Raw_1[T]{ + def raw(): Raw_1[_] = { new Raw_1[String] { def t() = "" } } + def t(): T +} + + +class X extends Raw_1[X] { + override def t = this + def exxx = 0 +} + +object Test extends App { + def c(s: X) = { + val raw = s.raw + raw.t.exxx + } + c(new X()) +} diff --git a/test/files/neg/t8244c.check b/test/files/neg/t8244c.check new file mode 100644 index 0000000000..fd58a5847c --- /dev/null +++ b/test/files/neg/t8244c.check @@ -0,0 +1,4 @@ +t8244c.scala:15: error: value exxx is not a member of _$1 + raw.t.exxx + ^ +one error found diff --git a/test/files/neg/t8244c.scala b/test/files/neg/t8244c.scala new file mode 100644 index 0000000000..2fb4f451a1 --- /dev/null +++ b/test/files/neg/t8244c.scala @@ -0,0 +1,18 @@ +class Raw_1[T]{ + def raw(): Raw_1[_] = { new Raw_1[String] { def t() = "" } } + def t(): T +} + + +class X extends Raw_1[X] { + override def t = this + def exxx = 0 +} + +object Test extends App { + def c(s: X) = { + val raw = s.raw + raw.t.exxx + } + c(new X()) +} diff --git a/test/files/neg/t8244e.check b/test/files/neg/t8244e.check new file mode 100644 index 0000000000..ebd74036e5 --- /dev/null +++ b/test/files/neg/t8244e.check @@ -0,0 +1,4 @@ +Test.scala:9: error: value exxx is not a member of ?0 + raw.t.exxx // java.lang.ClassCastException: java.lang.String cannot be cast to X + ^ +one error found diff --git a/test/files/neg/t8244e/Raw.java b/test/files/neg/t8244e/Raw.java new file mode 100644 index 0000000000..53202e319d --- /dev/null +++ b/test/files/neg/t8244e/Raw.java @@ -0,0 +1,4 @@ +public abstract class Raw<T>{ + public Raw raw() { return new Raw<String>() { public String t() { return ""; } }; } + public abstract T t(); +} diff --git a/test/files/neg/t8244e/Test.scala b/test/files/neg/t8244e/Test.scala new file mode 100644 index 0000000000..ca2a90583f --- /dev/null +++ b/test/files/neg/t8244e/Test.scala @@ -0,0 +1,12 @@ +class X extends Raw[X] { + override def t = this + def exxx = 0 +} + +object Test extends App { + def c(s: X) = { + val raw = s.raw + raw.t.exxx // java.lang.ClassCastException: java.lang.String cannot be cast to X + } + c(new X()) +} diff --git a/test/files/pos/t8244d/InodeBase_1.java b/test/files/pos/t8244d/InodeBase_1.java new file mode 100644 index 0000000000..36c2123418 --- /dev/null +++ b/test/files/pos/t8244d/InodeBase_1.java @@ -0,0 +1,6 @@ +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; + +abstract class INodeBase_1<K, V> { + @SuppressWarnings("rawtypes") + public static final AtomicReferenceFieldUpdater<INodeBase_1, Object> updater = null; +} diff --git a/test/files/pos/t8244d/Test_2.scala b/test/files/pos/t8244d/Test_2.scala new file mode 100644 index 0000000000..cb39c9692c --- /dev/null +++ b/test/files/pos/t8244d/Test_2.scala @@ -0,0 +1,3 @@ +class INodeX[K, V] extends INodeBase_1[K, V] { + INodeBase_1.updater.set(this, null) +} |