summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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)
+ }
+}