summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-02-11 13:16:06 -0800
committerPaul Phillips <paulp@improving.org>2012-02-11 23:05:22 -0800
commit7a6fa80937dec6c60efe53c915dfa3ba76b3af87 (patch)
tree76fbd019dfcb0caf284ba8f95bc2dfe962896506
parentc478eb770ddf27de64d55426f0fdd3fd81d11f22 (diff)
downloadscala-7a6fa80937dec6c60efe53c915dfa3ba76b3af87.tar.gz
scala-7a6fa80937dec6c60efe53c915dfa3ba76b3af87.tar.bz2
scala-7a6fa80937dec6c60efe53c915dfa3ba76b3af87.zip
Another existential problem down.
There is a window of danger when multiple related elements are being typed where something which is conceptually one thing can slip into two things, and those two things can be incompatible with one another. Less mysteriously, c478eb770d fixed this: def f = { object Bob ; Bob } ; val g = f But, it did not fix this: def f = { case class Bob() ; Bob } ; val g = f See test case pos/existentials-harmful.scala for an "in the wild" code example fixed by this commit. The root of the problem was that the getter and the field would each independently derive the same existential type to describe Bob, but those existentials were not the same as one another. This has been the most elusive bug I have ever fixed. I want to cry when I think of how much time I've put into it over the past half decade or so. Unfortunately the way the repl works it is particularly good at eliciting those grotesque found/required error messages and so I was never able to let the thing go. There is still a cosmetic issue (from the last commit really) where compound types wind up with repeated parents. Closes SI-1195, SI-1201.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala43
-rw-r--r--test/files/neg/t935.check5
-rw-r--r--test/files/pos/existentials-harmful.scala (renamed from test/pending/pos/existentials-harmful.scala)0
-rw-r--r--test/files/pos/existentials.scala9
-rw-r--r--test/files/run/existentials3.check22
-rw-r--r--test/files/run/existentials3.scala73
-rw-r--r--test/files/run/repl-existentials.check63
-rw-r--r--test/files/run/repl-existentials.scala31
-rw-r--r--test/files/run/t1195.check6
-rw-r--r--test/files/run/t1195.scala26
10 files changed, 168 insertions, 110 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 0c32ff32c0..915d7a98db 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -326,22 +326,35 @@ trait MethodSynthesis {
super.validate()
}
- // keep type tree of original abstract field
- private def fixTypeTree(dd: DefDef): DefDef = {
- dd.tpt match {
- case tt: TypeTree if dd.rhs == EmptyTree =>
- tt setOriginal tree.tpt
- case tpt =>
- tpt setPos tree.tpt.pos.focus
- }
- dd
- }
override def derivedTree: DefDef = {
- fixTypeTree {
- DefDef(derivedSym,
- if (mods.isDeferred) EmptyTree
- else gen.mkCheckInit(fieldSelection)
- )
+ // For existentials, don't specify a type for the getter, even one derived
+ // from the symbol! This leads to incompatible existentials for the field and
+ // the getter. Let the typer do all the work. You might think "why only for
+ // existentials, why not always," and you would be right, except: a single test
+ // fails, but it looked like some work to deal with it. Test neg/t0606.scala
+ // starts compiling (instead of failing like it's supposed to) because the typer
+ // expects to be able to identify escaping locals in typedDefDef, and fails to
+ // spot that brand of them. In other words it's an artifact of the implementation.
+ val tpt = derivedSym.tpe.finalResultType match {
+ case ExistentialType(_, _) => TypeTree()
+ case tp => TypeTree(tp)
+ }
+ tpt setPos focusPos(derivedSym.pos)
+ // keep type tree of original abstract field
+ if (mods.isDeferred)
+ tpt setOriginal tree.tpt
+
+ // TODO - reconcile this with the DefDef creator in Trees (which
+ // at this writing presented no way to pass a tree in for tpt.)
+ atPos(derivedSym.pos) {
+ DefDef(
+ Modifiers(derivedSym.flags),
+ derivedSym.name.toTermName,
+ Nil,
+ Nil,
+ tpt,
+ if (mods.isDeferred) EmptyTree else gen.mkCheckInit(fieldSelection)
+ ) setSymbol derivedSym
}
}
}
diff --git a/test/files/neg/t935.check b/test/files/neg/t935.check
index af634a2630..8b73700187 100644
--- a/test/files/neg/t935.check
+++ b/test/files/neg/t935.check
@@ -4,4 +4,7 @@ t935.scala:7: error: type arguments [Test3.B] do not conform to class E's type p
t935.scala:13: error: type arguments [Test4.B] do not conform to class E's type parameter bounds [T <: String]
val b: String @E[B](new B) = "hi"
^
-two errors found
+t935.scala:13: error: type arguments [Test4.B] do not conform to class E's type parameter bounds [T <: String]
+ val b: String @E[B](new B) = "hi"
+ ^
+three errors found
diff --git a/test/pending/pos/existentials-harmful.scala b/test/files/pos/existentials-harmful.scala
index 8722852e8a..8722852e8a 100644
--- a/test/pending/pos/existentials-harmful.scala
+++ b/test/files/pos/existentials-harmful.scala
diff --git a/test/files/pos/existentials.scala b/test/files/pos/existentials.scala
new file mode 100644
index 0000000000..c51f60b546
--- /dev/null
+++ b/test/files/pos/existentials.scala
@@ -0,0 +1,9 @@
+class A {
+ def f() = { case class Bob(); Bob }
+
+ val quux0 = f()
+ def quux1 = f()
+
+ val bippy0 = f _
+ def bippy1 = f _
+}
diff --git a/test/files/run/existentials3.check b/test/files/run/existentials3.check
new file mode 100644
index 0000000000..41dc1f767c
--- /dev/null
+++ b/test/files/run/existentials3.check
@@ -0,0 +1,22 @@
+_ <: scala.runtime.AbstractFunction0[_ <: Object with Test$ToS with scala.ScalaObject with scala.Product with scala.Serializable] with scala.ScalaObject with scala.Serializable with java.lang.Object
+_ <: Object with Test$ToS with scala.ScalaObject with scala.Product with scala.Serializable
+Object with Test$ToS with scala.ScalaObject
+Object with Test$ToS with scala.ScalaObject
+Object with Test$ToS with scala.ScalaObject
+scala.Function0[Object with Test$ToS with scala.ScalaObject]
+scala.Function0[Object with Test$ToS with scala.ScalaObject]
+_ <: Object with _ <: Object with Object with Test$ToS with scala.ScalaObject
+_ <: Object with _ <: Object with _ <: Object with Test$ToS with scala.ScalaObject
+scala.collection.immutable.List[Object with scala.collection.Seq[Int] with scala.ScalaObject]
+scala.collection.immutable.List[Object with scala.collection.Seq[_ <: Int] with scala.ScalaObject]
+_ <: scala.runtime.AbstractFunction0[_ <: Object with Test$ToS with scala.ScalaObject with scala.Product with scala.Serializable] with scala.ScalaObject with scala.Serializable with java.lang.Object
+_ <: Object with Test$ToS with scala.ScalaObject with scala.Product with scala.Serializable
+Object with Test$ToS with scala.ScalaObject
+Object with Test$ToS with scala.ScalaObject
+Object with Test$ToS with scala.ScalaObject
+scala.Function0[Object with Test$ToS with scala.ScalaObject]
+scala.Function0[Object with Test$ToS with scala.ScalaObject]
+_ <: Object with _ <: Object with Object with Test$ToS with scala.ScalaObject
+_ <: Object with _ <: Object with _ <: Object with Test$ToS with scala.ScalaObject
+scala.collection.immutable.List[Object with scala.collection.Seq[Int] with scala.ScalaObject]
+scala.collection.immutable.List[Object with scala.collection.Seq[_ <: Int] with scala.ScalaObject]
diff --git a/test/files/run/existentials3.scala b/test/files/run/existentials3.scala
new file mode 100644
index 0000000000..bb80d366cc
--- /dev/null
+++ b/test/files/run/existentials3.scala
@@ -0,0 +1,73 @@
+object Test {
+ trait ToS { final override def toString = getClass.getName }
+
+ def f1 = { case class Bar() extends ToS; Bar }
+ def f2 = { case class Bar() extends ToS; Bar() }
+ def f3 = { class Bar() extends ToS; object Bar extends ToS; Bar }
+ def f4 = { class Bar() extends ToS; new Bar() }
+ def f5 = { object Bar extends ToS; Bar }
+ def f6 = { () => { object Bar extends ToS ; Bar } }
+ def f7 = { val f = { () => { object Bar extends ToS ; Bar } } ; f }
+
+ def f8 = { trait A ; trait B extends A ; class C extends B with ToS; new C { } }
+ def f9 = { trait A ; trait B ; class C extends B with A with ToS; new C { } }
+
+ def f10 = { class A { type T1 } ; List[A#T1]() }
+ def f11 = { abstract class A extends Seq[Int] ; List[A]() }
+ def f12 = { abstract class A extends Seq[U forSome { type U <: Int }] ; List[A]() }
+
+ val g1 = { case class Bar() extends ToS; Bar }
+ val g2 = { case class Bar() extends ToS; Bar() }
+ val g3 = { class Bar() extends ToS; object Bar extends ToS; Bar }
+ val g4 = { class Bar() extends ToS; new Bar() }
+ val g5 = { object Bar extends ToS; Bar }
+ val g6 = { () => { object Bar extends ToS ; Bar } }
+ val g7 = { val f = { () => { object Bar extends ToS ; Bar } } ; f }
+
+ val g8 = { trait A ; trait B extends A ; class C extends B with ToS; new C { } }
+ val g9 = { trait A ; trait B ; class C extends B with A with ToS; new C { } }
+
+ val g10 = { class A { type T1 } ; List[A#T1]() }
+ val g11 = { abstract class A extends Seq[Int] ; List[A]() }
+ val g12 = { abstract class A extends Seq[U forSome { type U <: Int }] ; List[A]() }
+
+ def m[T: Manifest](x: T) = println(manifest[T])
+
+ // manifests don't work for f10/g10
+ def main(args: Array[String]): Unit = {
+ m(f1)
+ m(f2)
+ m(f3)
+ m(f4)
+ m(f5)
+ m(f6)
+ m(f7)
+ m(f8)
+ m(f9)
+ // m(f10)
+ m(f11)
+ m(f12)
+ m(g1)
+ m(g2)
+ m(g3)
+ m(g4)
+ m(g5)
+ m(g6)
+ m(g7)
+ m(g8)
+ m(g9)
+ // m(g10)
+ m(g11)
+ m(g12)
+ }
+}
+
+object Misc {
+ trait Bippy { def bippy = "I'm Bippy!" }
+ object o1 {
+ def f1 = { trait A extends Seq[U forSome { type U <: Bippy }] ; abstract class B extends A ; trait C extends B ; (null: C) }
+ def f2 = f1.head.bippy
+ }
+ def g1 = o1.f1 _
+ def g2 = o1.f2 _
+}
diff --git a/test/files/run/repl-existentials.check b/test/files/run/repl-existentials.check
deleted file mode 100644
index 7093b428e8..0000000000
--- a/test/files/run/repl-existentials.check
+++ /dev/null
@@ -1,63 +0,0 @@
-Type in expressions to have them evaluated.
-Type :help for more information.
-
-scala> trait ToS { final override def toString = getClass.getName }
-defined trait ToS
-
-scala>
-
-scala> // def f1 = { case class Bar() extends ToS; Bar }
-
-scala> def f2 = { case class Bar() extends ToS; Bar() }
-f2: Bar forSome { type Bar <: Object with ToS with ScalaObject with Product with Serializable{def copy(): Bar} }
-
-scala> def f3 = { class Bar() extends ToS; object Bar extends ToS; Bar }
-f3: Object with ToS with ScalaObject
-
-scala> def f4 = { class Bar() extends ToS; new Bar() }
-f4: Object with ToS with ScalaObject
-
-scala> def f5 = { object Bar extends ToS; Bar }
-f5: Object with ToS with ScalaObject
-
-scala> def f6 = { () => { object Bar extends ToS ; Bar } }
-f6: () => Object with ToS with ScalaObject
-
-scala> def f7 = { val f = { () => { object Bar extends ToS ; Bar } } ; f }
-f7: () => Object with ToS with ScalaObject
-
-scala>
-
-scala> // def f8 = { trait A ; trait B extends A ; class C extends B with ToS; new C { } }
-
-scala> // def f9 = { trait A ; trait B ; class C extends B with A with ToS; new C { } }
-
-scala>
-
-scala> def f10 = { class A { type T1 } ; List[A#T1]() }
-f10: List[Object with ScalaObject{type T1}#T1]
-
-scala> def f11 = { abstract class A extends Seq[Int] ; List[A]() }
-f11: List[Object with Seq[Int] with ScalaObject]
-
-scala> def f12 = { abstract class A extends Seq[U forSome { type U <: Int }] ; List[A]() }
-f12: List[Object with Seq[U forSome { type U <: Int }] with ScalaObject]
-
-scala>
-
-scala> trait Bippy { def bippy = "I'm Bippy!" }
-defined trait Bippy
-
-scala> object o1 {
- def f1 = { trait A extends Seq[U forSome { type U <: Bippy }] ; abstract class B extends A ; trait C extends B ; (null: C) }
- def f2 = f1.head.bippy
-}
-defined module o1
-
-scala> o1.f1 _
-res0: () => C forSome { type C <: Object with A with ScalaObject; type A <: Object with Seq[U forSome { type U <: Bippy }] } = <function0>
-
-scala> o1.f2 _
-res1: () => String = <function0>
-
-scala>
diff --git a/test/files/run/repl-existentials.scala b/test/files/run/repl-existentials.scala
deleted file mode 100644
index 31034b49a0..0000000000
--- a/test/files/run/repl-existentials.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-import scala.tools.partest.ReplTest
-
-object Test extends ReplTest {
- def code = """
-trait ToS { final override def toString = getClass.getName }
-
-// def f1 = { case class Bar() extends ToS; Bar }
-def f2 = { case class Bar() extends ToS; Bar() }
-def f3 = { class Bar() extends ToS; object Bar extends ToS; Bar }
-def f4 = { class Bar() extends ToS; new Bar() }
-def f5 = { object Bar extends ToS; Bar }
-def f6 = { () => { object Bar extends ToS ; Bar } }
-def f7 = { val f = { () => { object Bar extends ToS ; Bar } } ; f }
-
-// def f8 = { trait A ; trait B extends A ; class C extends B with ToS; new C { } }
-// def f9 = { trait A ; trait B ; class C extends B with A with ToS; new C { } }
-
-def f10 = { class A { type T1 } ; List[A#T1]() }
-def f11 = { abstract class A extends Seq[Int] ; List[A]() }
-def f12 = { abstract class A extends Seq[U forSome { type U <: Int }] ; List[A]() }
-
-trait Bippy { def bippy = "I'm Bippy!" }
-object o1 {
- def f1 = { trait A extends Seq[U forSome { type U <: Bippy }] ; abstract class B extends A ; trait C extends B ; (null: C) }
- def f2 = f1.head.bippy
-}
-o1.f1 _
-o1.f2 _
-
-""".trim
-}
diff --git a/test/files/run/t1195.check b/test/files/run/t1195.check
new file mode 100644
index 0000000000..dc521fb8ca
--- /dev/null
+++ b/test/files/run/t1195.check
@@ -0,0 +1,6 @@
+_ <: scala.runtime.AbstractFunction1[Int, _ <: Object with scala.ScalaObject with scala.Product with scala.Serializable] with scala.ScalaObject with scala.Serializable with java.lang.Object
+_ <: Object with scala.ScalaObject with scala.Product with scala.Serializable
+Object with scala.ScalaObject with scala.Product with scala.Serializable
+_ <: scala.runtime.AbstractFunction1[Int, _ <: Object with scala.ScalaObject with scala.Product with scala.Serializable] with scala.ScalaObject with scala.Serializable with java.lang.Object
+_ <: Object with scala.ScalaObject with scala.Product with scala.Serializable
+Object with scala.ScalaObject with scala.Product with scala.Serializable
diff --git a/test/files/run/t1195.scala b/test/files/run/t1195.scala
new file mode 100644
index 0000000000..81ef5bdb0e
--- /dev/null
+++ b/test/files/run/t1195.scala
@@ -0,0 +1,26 @@
+object Test {
+ def f() = { case class Bar(x: Int); Bar }
+ def g() = { case class Bar(x: Int); Bar(5) }
+ def h() = { case object Bar ; Bar }
+
+ val f1 = f()
+ val g1 = g()
+ val h1 = h()
+
+ def m[T: Manifest](x: T) = println(manifest[T])
+
+ def main(args: Array[String]): Unit = {
+ m(f)
+ m(g)
+ m(h)
+ m(f1)
+ m(g1)
+ m(h1)
+ }
+}
+
+class A1[T] {
+ class B1[U] {
+ def f = { case class D(x: Int) extends A1[String] ; new D(5) }
+ }
+}