summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2014-02-07 11:52:55 +0100
committerAdriaan Moors <adriaan.moors@typesafe.com>2014-02-12 18:05:00 -0800
commitf62e280825422c1e64c2427bc5958869662701ca (patch)
treef61e5c455851ee50f2210a898c15d6437724a3d6
parent9c4a6e3ed7624892f46948c1c0fb57d7d5b3346e (diff)
downloadscala-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.scala7
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala9
-rw-r--r--test/files/neg/t8244.check4
-rw-r--r--test/files/neg/t8244/Raw_1.java4
-rw-r--r--test/files/neg/t8244/Test_2.scala12
-rw-r--r--test/files/neg/t8244b.check4
-rw-r--r--test/files/neg/t8244b.scala18
-rw-r--r--test/files/neg/t8244c.check4
-rw-r--r--test/files/neg/t8244c.scala18
-rw-r--r--test/files/neg/t8244e.check4
-rw-r--r--test/files/neg/t8244e/Raw.java4
-rw-r--r--test/files/neg/t8244e/Test.scala12
-rw-r--r--test/files/pos/t8244d/InodeBase_1.java6
-rw-r--r--test/files/pos/t8244d/Test_2.scala3
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)
+}