summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-02-10 03:11:49 -0800
committerPaul Phillips <paulp@improving.org>2012-02-10 17:58:00 -0800
commitc478eb770ddf27de64d55426f0fdd3fd81d11f22 (patch)
tree0f1a3b19d2c829e951f1c48a41d4e7785499f3a5
parenta209868820942c1f5b6333d2a0604c7a63b7d0d1 (diff)
downloadscala-c478eb770ddf27de64d55426f0fdd3fd81d11f22.tar.gz
scala-c478eb770ddf27de64d55426f0fdd3fd81d11f22.tar.bz2
scala-c478eb770ddf27de64d55426f0fdd3fd81d11f22.zip
Overcame long-maddening existential issue.
When performing an existential transform, the bounds produced by the hidden symbols may contain references to the hidden symbols, but these references were not accounted for. This was at the root of some bad behavior with existentials. If you've ever seen a message like this: <console>:8: error: type mismatch; found : B(in value res0) where type B(in value res0) <: A with ScalaObject required: B(in value res0) forSome { type B(in value res0) <: A with ScalaObject; type A <: Object with ScalaObject } ...then you know what I mean. Closes SI-4171, not quite yet on SI-1195, SI-1201.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala32
-rw-r--r--test/files/run/repl-existentials.check63
-rw-r--r--test/files/run/repl-existentials.scala31
-rw-r--r--test/files/run/t4171.check3
-rw-r--r--test/files/run/t4171.scala11
5 files changed, 138 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index bc8a8a31b5..6f6edc62c7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2844,7 +2844,34 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type?
sym.isTypeParameter && sym.owner.isJavaDefined
+
+ /** If we map a set of hidden symbols to their existential bounds, we
+ * have a problem: the bounds may themselves contain references to the
+ * hidden symbols. So this recursively calls existentialBound until
+ * the typeSymbol is not amongst the symbols being hidden.
+ */
+ def existentialBoundsExcludingHidden(hidden: List[Symbol]): Map[Symbol, Type] = {
+ def safeBound(t: Type): Type =
+ if (hidden contains t.typeSymbol) safeBound(t.typeSymbol.existentialBound.bounds.hi) else t
+
+ def hiBound(s: Symbol): Type = safeBound(s.existentialBound.bounds.hi) match {
+ case tp @ RefinedType(parents, decls) =>
+ val parents1 = parents mapConserve safeBound
+ if (parents eq parents1) tp
+ else copyRefinedType(tp, parents1, decls)
+ case tp => tp
+ }
+ (hidden map { s =>
+ // Hanging onto lower bound in case anything interesting
+ // happens with it.
+ (s, s.existentialBound match {
+ case TypeBounds(lo, hi) => TypeBounds(lo, hiBound(s))
+ case _ => hiBound(s)
+ })
+ }).toMap
+ }
+
/** Given a set `rawSyms` of term- and type-symbols, and a type
* `tp`, produce a set of fresh type parameters and a type so that
* it can be abstracted to an existential type. Every type symbol
@@ -2862,12 +2889,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
* only the type of the Ident is changed.
*/
protected def existentialTransform[T](rawSyms: List[Symbol], tp: Type)(creator: (List[Symbol], Type) => T): T = {
+ val allBounds = existentialBoundsExcludingHidden(rawSyms)
val typeParams: List[Symbol] = rawSyms map { sym =>
val name = sym.name match {
case x: TypeName => x
- case x => newTypeName(x + ".type")
+ case x => nme.singletonName(x)
}
- val bound = sym.existentialBound
+ val bound = allBounds(sym)
val sowner = if (isRawParameter(sym)) context.owner else sym.owner
val quantified = sowner.newExistential(name, sym.pos)
diff --git a/test/files/run/repl-existentials.check b/test/files/run/repl-existentials.check
new file mode 100644
index 0000000000..7093b428e8
--- /dev/null
+++ b/test/files/run/repl-existentials.check
@@ -0,0 +1,63 @@
+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
new file mode 100644
index 0000000000..31034b49a0
--- /dev/null
+++ b/test/files/run/repl-existentials.scala
@@ -0,0 +1,31 @@
+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/t4171.check b/test/files/run/t4171.check
new file mode 100644
index 0000000000..d72391a1c4
--- /dev/null
+++ b/test/files/run/t4171.check
@@ -0,0 +1,3 @@
+1
+5
+class Test$B$1
diff --git a/test/files/run/t4171.scala b/test/files/run/t4171.scala
new file mode 100644
index 0000000000..fba2fb5ed6
--- /dev/null
+++ b/test/files/run/t4171.scala
@@ -0,0 +1,11 @@
+object Test {
+ val c = { class C; new C { def foo = 1 } }
+ val a = { class B { def bar = 5 }; class C extends B; new C }
+ val e = { class A; class B extends A; classOf[B] }
+
+ def main(args: Array[String]): Unit = {
+ println(c.foo)
+ println(a.bar)
+ println(e)
+ }
+}