diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 32 | ||||
-rw-r--r-- | test/files/run/repl-existentials.check | 63 | ||||
-rw-r--r-- | test/files/run/repl-existentials.scala | 31 | ||||
-rw-r--r-- | test/files/run/t4171.check | 3 | ||||
-rw-r--r-- | test/files/run/t4171.scala | 11 |
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) + } +} |