summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala1
-rw-r--r--src/reflect/scala/reflect/internal/BaseTypeSeqs.scala62
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala166
-rw-r--r--src/reflect/scala/reflect/internal/tpe/GlbLubs.scala2
-rw-r--r--test/files/neg/lub-from-hell-2.check7
-rw-r--r--test/files/neg/lub-from-hell-2.scala6
-rw-r--r--test/files/pos/lub-from-hell.scala11
-rw-r--r--test/junit/scala/reflect/internal/TypesTest.scala59
8 files changed, 211 insertions, 103 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
index b7523bbf06..64638ca34d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -142,6 +142,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
elided += r
else
result += r
+ ()
}
result.toList
}
diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala
index 81281b5eb4..0f5b800925 100644
--- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala
+++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala
@@ -56,42 +56,44 @@ trait BaseTypeSeqs {
if(pending contains i) {
pending.clear()
throw CyclicInheritance
- } else
- elems(i) match {
- case rtp @ RefinedType(variants, decls) =>
- // can't assert decls.isEmpty; see t0764
- //if (!decls.isEmpty) abort("computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j))
- //Console.println("compute closure of "+this+" => glb("+variants+")")
- pending += i
- try {
- mergePrefixAndArgs(variants, Variance.Contravariant, lubDepth(variants)) match {
- case NoType => typeError("no common type instance of base types "+(variants mkString ", and ")+" exists.")
- case tp0 =>
- pending(i) = false
- elems(i) = tp0
- tp0
- }
- }
- catch {
- case CyclicInheritance =>
- typeError(
- "computing the common type instance of base types "+(variants mkString ", and ")+" leads to a cycle.")
+ } else {
+ def computeLazyType(rtp: RefinedType): Type = {
+ if (!isIntersectionTypeForLazyBaseType(rtp))
+ abort("unexpected RefinedType in base type seq, lazy BTS elements should be created via intersectionTypeForLazyBaseType: " + rtp)
+ val variants = rtp.parents
+ // can't assert decls.isEmpty; see t0764
+ //if (!decls.isEmpty) abort("computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j))
+ //Console.println("compute closure of "+this+" => glb("+variants+")")
+ pending += i
+ try {
+ mergePrefixAndArgs(variants, Variance.Contravariant, lubDepth(variants)) match {
+ case NoType => typeError("no common type instance of base types " + (variants mkString ", and ") + " exists.")
+ case tp0 =>
+ pending(i) = false
+ elems(i) = tp0
+ tp0
}
+ }
+ catch {
+ case CyclicInheritance =>
+ typeError(
+ "computing the common type instance of base types " + (variants mkString ", and ") + " leads to a cycle.")
+ }
+ }
+ elems(i) match {
+ case rtp@RefinedType(variants, decls) =>
+ computeLazyType(rtp)
+ case et @ ExistentialType(quantified, rtp: RefinedType) =>
+ existentialAbstraction(quantified, computeLazyType(rtp))
case tp =>
tp
}
+ }
def rawElem(i: Int) = elems(i)
- /** The type symbol of the type at i'th position in this sequence;
- * no evaluation needed.
- */
- def typeSymbol(i: Int): Symbol = {
- elems(i) match {
- case RefinedType(v :: vs, _) => v.typeSymbol
- case tp => tp.typeSymbol
- }
- }
+ /** The type symbol of the type at i'th position in this sequence */
+ def typeSymbol(i: Int): Symbol = elems(i).typeSymbol
/** Return all evaluated types in this sequence as a list */
def toList: List[Type] = elems.toList
@@ -215,7 +217,7 @@ trait BaseTypeSeqs {
}
i += 1
}
- buf += intersectionType(minTypes)
+ buf += intersectionTypeForLazyBaseType(minTypes) // TODO this reverses the order. Does this matter? Or should this be minTypes.reverse?
btsSize += 1
}
}
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 7dda805378..523cb968e7 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -174,8 +174,10 @@ trait Types
if (newtp eq underlying) this
// BoundedWildcardTypes reach here during erroneous compilation: neg/t6258
// Higher-kinded exclusion is because [x]CC[x] compares =:= to CC: pos/t3800
+ // Avoid reusing the existing Wrapped(RefinedType) when we've be asked to wrap an =:= RefinementTypeRef, the
+ // distinction is important in base type sequences.
// Otherwise, if newtp =:= underlying, don't rewrap it.
- else if (!newtp.isWildcard && !newtp.isHigherKinded && (newtp =:= underlying)) this
+ else if (!newtp.isWildcard && !newtp.isHigherKinded && !newtp.isInstanceOf[RefinementTypeRef] && (newtp =:= underlying)) this
else rewrap(newtp)
)
protected def rewrap(newtp: Type): Type
@@ -1589,7 +1591,6 @@ trait Types
*/
case class RefinedType(override val parents: List[Type],
override val decls: Scope) extends CompoundType with RefinedTypeApi {
-
override def isHigherKinded = (
parents.nonEmpty &&
(parents forall typeIsHigherKinded) &&
@@ -2704,6 +2705,7 @@ trait Types
def isRepresentableWithWildcards = {
val qset = quantified.toSet
underlying match {
+ case _: RefinementTypeRef => false
case TypeRef(pre, sym, args) =>
def isQuantified(tpe: Type): Boolean = {
(tpe exists (t => qset contains t.typeSymbol)) ||
@@ -3521,7 +3523,9 @@ trait Types
if ((parents eq original.parents) && (decls eq original.decls)) original
else {
val owner = original.typeSymbol.owner
- val result = refinedType(parents, owner)
+ val result =
+ if (isIntersectionTypeForLazyBaseType(original)) intersectionTypeForLazyBaseType(parents)
+ else refinedType(parents, owner)
val syms1 = decls.toList
for (sym <- syms1)
result.decls.enter(sym.cloneSymbol(result.typeSymbol))
@@ -3596,6 +3600,14 @@ trait Types
case tp :: Nil => tp
case _ => refinedType(tps, commonOwner(tps))
}
+ def intersectionTypeForLazyBaseType(tps: List[Type]) = tps match {
+ case tp :: Nil => tp
+ case _ => RefinedType(tps, newScope, tps.head.typeSymbolDirect)
+ }
+ def isIntersectionTypeForLazyBaseType(tp: RefinedType) = tp.parents match {
+ case head :: _ => tp.typeSymbolDirect eq head.typeSymbolDirect
+ case _ => false
+ }
/**** This implementation to merge parents was checked in in commented-out
form and has languished unaltered for five years. I think we should
@@ -4409,83 +4421,93 @@ trait Types
* Return `x` if the computation succeeds with result `x`.
* Return `NoType` if the computation fails.
*/
- def mergePrefixAndArgs(tps: List[Type], variance: Variance, depth: Depth): Type = tps match {
- case tp :: Nil => tp
- case TypeRef(_, sym, _) :: rest =>
- val pres = tps map (_.prefix) // prefix normalizes automatically
+ def mergePrefixAndArgs(tps0: List[Type], variance: Variance, depth: Depth): Type = {
+ var tparams = mutable.ListBuffer[Symbol]()
+ val tps = tps0.flatMap {
+ case rt: RefinedType if isIntersectionTypeForLazyBaseType(rt) => rt.parents
+ case ExistentialType(qs, underlying) =>
+ tparams ++= qs
+ underlying match {
+ case rt: RefinedType if isIntersectionTypeForLazyBaseType(rt) => rt.parents
+ case tp => tp :: Nil
+ }
+ case tp => tp :: Nil
+ }
+
+ val merged = tps match {
+ case tp :: Nil => tp
+ case TypeRef(_, sym, _) :: rest =>
+ val pres = tps map (_.prefix) // prefix normalizes automatically
val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth)
- val argss = tps map (_.normalize.typeArgs) // symbol equality (of the tp in tps) was checked using typeSymbol, which normalizes, so should normalize before retrieving arguments
+ val argss = tps map (_.normalize.typeArgs) // symbol equality (of the tp in tps) was checked using typeSymbol, which normalizes, so should normalize before retrieving arguments
val capturedParams = new ListBuffer[Symbol]
- try {
- if (sym == ArrayClass && phase.erasedTypes) {
- // special treatment for lubs of array types after erasure:
- // if argss contain one value type and some other type, the lub is Object
- // if argss contain several reference types, the lub is an array over lub of argtypes
- if (argss exists typeListIsEmpty) {
- NoType // something is wrong: an array without a type arg.
- }
- else {
- val args = argss map (_.head)
- if (args.tail forall (_ =:= args.head)) typeRef(pre, sym, List(args.head))
- else if (args exists (arg => isPrimitiveValueClass(arg.typeSymbol))) ObjectTpe
- else typeRef(pre, sym, List(lub(args)))
+ try {
+ if (sym == ArrayClass && phase.erasedTypes) {
+ // special treatment for lubs of array types after erasure:
+ // if argss contain one value type and some other type, the lub is Object
+ // if argss contain several reference types, the lub is an array over lub of argtypes
+ if (argss exists typeListIsEmpty) {
+ NoType // something is wrong: an array without a type arg.
+ }
+ else {
+ val args = argss map (_.head)
+ if (args.tail forall (_ =:= args.head)) typeRef(pre, sym, List(args.head))
+ else if (args exists (arg => isPrimitiveValueClass(arg.typeSymbol))) ObjectTpe
+ else typeRef(pre, sym, List(lub(args)))
+ }
}
- }
- else transposeSafe(argss) match {
- case None =>
- // transpose freaked out because of irregular argss
- // catching just in case (shouldn't happen, but also doesn't cost us)
- // [JZ] It happens: see SI-5683.
- debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss")
- NoType
- case Some(argsst) =>
- val args = map2(sym.typeParams, argsst) { (tparam, as0) =>
- val as = as0.distinct
- if (as.size == 1) as.head
- else if (depth.isZero) {
- log("Giving up merging args: can't unify %s under %s".format(as.mkString(", "), tparam.fullLocationString))
- // Don't return "Any" (or "Nothing") when we have to give up due to
- // recursion depth. Return NoType, which prevents us from poisoning
- // lublist's results. It can recognize the recursion and deal with it, but
- // only if we aren't returning invalid types.
- NoType
- }
- else {
- if (tparam.variance == variance) lub(as, depth.decr)
- else if (tparam.variance == variance.flip) glb(as, depth.decr)
+ else transposeSafe(argss) match {
+ case None =>
+ // transpose freaked out because of irregular argss
+ // catching just in case (shouldn't happen, but also doesn't cost us)
+ // [JZ] It happens: see SI-5683.
+ debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss")
+ NoType
+ case Some(argsst) =>
+ val args = map2(sym.typeParams, argsst) { (tparam, as0) =>
+ val as = as0.distinct
+ if (as.size == 1) as.head
+ else if (depth.isZero) {
+ log("Giving up merging args: can't unify %s under %s".format(as.mkString(", "), tparam.fullLocationString))
+ // Don't return "Any" (or "Nothing") when we have to give up due to
+ // recursion depth. Return NoType, which prevents us from poisoning
+ // lublist's results. It can recognize the recursion and deal with it, but
+ // only if we aren't returning invalid types.
+ NoType
+ }
else {
- val l = lub(as, depth.decr)
- val g = glb(as, depth.decr)
- if (l <:< g) l
- else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we
- // just err on the conservative side, i.e. with a bound that is too high.
- // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251
-
- val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l)
- capturedParams += qvar
- qvar.tpe
+ if (tparam.variance == variance) lub(as, depth.decr)
+ else if (tparam.variance == variance.flip) glb(as, depth.decr)
+ else {
+ val l = lub(as, depth.decr)
+ val g = glb(as, depth.decr)
+ if (l <:< g) l
+ else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we
+ // just err on the conservative side, i.e. with a bound that is too high.
+ // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251
+
+ val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l)
+ capturedParams += qvar
+ qvar.tpe
+ }
}
}
}
- }
- if (args contains NoType) NoType
- else existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args))
+ if (args contains NoType) NoType
+ else existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args))
+ }
+ } catch {
+ case ex: MalformedType => NoType
}
- } catch {
- case ex: MalformedType => NoType
- }
- case SingleType(_, sym) :: rest =>
- val pres = tps map (_.prefix)
- val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth)
- try singleType(pre, sym)
- catch { case ex: MalformedType => NoType }
- case ExistentialType(tparams, quantified) :: rest =>
- mergePrefixAndArgs(quantified :: rest, variance, depth) match {
- case NoType => NoType
- case tpe => existentialAbstraction(tparams, tpe)
- }
- case _ =>
- abort(s"mergePrefixAndArgs($tps, $variance, $depth): unsupported tps")
+ case SingleType(_, sym) :: rest =>
+ val pres = tps map (_.prefix)
+ val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth)
+ try singleType(pre, sym)
+ catch { case ex: MalformedType => NoType }
+ case _ =>
+ abort(s"mergePrefixAndArgs($tps, $variance, $depth): unsupported tps")
+ }
+ existentialAbstraction(tparams.toList, merged)
}
def addMember(thistp: Type, tp: Type, sym: Symbol): Unit = addMember(thistp, tp, sym, AnyDepth)
diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala
index 123b44aa05..c997dd30eb 100644
--- a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala
+++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala
@@ -118,7 +118,7 @@ private[internal] trait GlbLubs {
// ts0 is the 1-dimensional frontier of symbols cutting through 2-dimensional tsBts.
// Invariant: all symbols "under" (closer to the first row) the frontier
// are smaller (according to _.isLess) than the ones "on and beyond" the frontier
- val ts0 = tsBts map (_.head)
+ val ts0 = tsBts map (_.head)
// Is the frontier made up of types with the same symbol?
val isUniformFrontier = (ts0: @unchecked) match {
diff --git a/test/files/neg/lub-from-hell-2.check b/test/files/neg/lub-from-hell-2.check
new file mode 100644
index 0000000000..3ef935f93b
--- /dev/null
+++ b/test/files/neg/lub-from-hell-2.check
@@ -0,0 +1,7 @@
+lub-from-hell-2.scala:3: error: type arguments [Any,Iterable[Any] with Int => Any with scala.collection.generic.Subtractable[Any,Iterable[Any] with Int => Any with scala.collection.generic.Subtractable[Any,Iterable[Any] with Int => Any]{def seq: Iterable[Any] with Int => Any}]{def seq: Iterable[Any] with Int => Any{def seq: Iterable[Any] with Int => Any}}] do not conform to trait Subtractable's type parameter bounds [A,+Repr <: scala.collection.generic.Subtractable[A,Repr]]
+ def foo(a: Boolean, b: collection.mutable.Set[Any], c: collection.mutable.ListBuffer[Any]) = if (a) b else c
+ ^
+lub-from-hell-2.scala:4: error: type arguments [Any,scala.collection.mutable.Iterable[Any] with scala.collection.mutable.Cloneable[scala.collection.mutable.Iterable[Any] with scala.collection.mutable.Cloneable[scala.collection.mutable.Iterable[Any] with Cloneable with Int => Any] with Int => Any{def seq: scala.collection.mutable.Iterable[Any] with Cloneable with Int => Any}] with scala.collection.generic.Growable[Any] with Int => Any with scala.collection.generic.Shrinkable[Any] with scala.collection.generic.Subtractable[Any,Iterable[Any] with Int => Any with scala.collection.generic.Subtractable[Any,Iterable[Any] with Int => Any]{def seq: Iterable[Any] with Int => Any}] with scala.collection.script.Scriptable[Any]] do not conform to trait Subtractable's type parameter bounds [A,+Repr <: scala.collection.generic.Subtractable[A,Repr]]
+ def bar(a: Boolean, b: scala.collection.mutable.SetLike[Any,scala.collection.mutable.Set[Any]], c: scala.collection.mutable.Buffer[Any]) = if (a) b else c
+ ^
+two errors found
diff --git a/test/files/neg/lub-from-hell-2.scala b/test/files/neg/lub-from-hell-2.scala
new file mode 100644
index 0000000000..96760c6edf
--- /dev/null
+++ b/test/files/neg/lub-from-hell-2.scala
@@ -0,0 +1,6 @@
+class Test {
+ trait Tree
+ def foo(a: Boolean, b: collection.mutable.Set[Any], c: collection.mutable.ListBuffer[Any]) = if (a) b else c
+ def bar(a: Boolean, b: scala.collection.mutable.SetLike[Any,scala.collection.mutable.Set[Any]], c: scala.collection.mutable.Buffer[Any]) = if (a) b else c
+ // bar produces an ill-bounded LUB in 2.11.8. After this commit, which fixes a bug in existential+refinement lubs, foo also fails.
+}
diff --git a/test/files/pos/lub-from-hell.scala b/test/files/pos/lub-from-hell.scala
new file mode 100644
index 0000000000..a7d2a99b08
--- /dev/null
+++ b/test/files/pos/lub-from-hell.scala
@@ -0,0 +1,11 @@
+class Test {
+ trait Tree
+ def foo(b: Boolean, buf: collection.mutable.ArrayBuffer[Any], acc: StringBuilder) = if (b) buf else acc
+
+ // def bar(b: Boolean,
+ // buf: scala.collection.IndexedSeqLike[Any,Cloneable with Mutable with Equals],
+ // acc: scala.collection.IndexedSeqLike[_18,scala.collection.mutable.IndexedSeq[_18]] with scala.collection.IndexedSeqLike[_18,IndexedSeq[_18]] forSome { type _18 >: String with Char }
+ // ) = if (b) buf else acc
+
+
+}
diff --git a/test/junit/scala/reflect/internal/TypesTest.scala b/test/junit/scala/reflect/internal/TypesTest.scala
index 45a4369396..763fd73c8a 100644
--- a/test/junit/scala/reflect/internal/TypesTest.scala
+++ b/test/junit/scala/reflect/internal/TypesTest.scala
@@ -69,4 +69,63 @@ class TypesTest {
assert(elem0.contains(IntClass))
}
+ @Test
+ def testRefinedLubs(): Unit = {
+ // https://github.com/scala/scala-dev/issues/168
+ assertEquals(typeOf[Option[AnyVal]], lub(typeOf[Option[Int] with Option[Char]] :: typeOf[Option[Boolean] with Option[Short]] :: Nil))
+ assertEquals(typeOf[Option[AnyVal]], lub(typeOf[Option[Int] with Option[Char]] :: typeOf[Option[Boolean]] :: Nil))
+ assertEquals(typeOf[Option[AnyVal]], lub((typeOf[Option[Int] with Option[Char]] :: typeOf[Option[Boolean] with Option[Short]] :: Nil).reverse))
+ assertEquals(typeOf[Option[AnyVal]], lub((typeOf[Option[Int] with Option[Char]] :: typeOf[Option[Boolean]] :: Nil).reverse))
+ }
+
+ @Test
+ def testExistentialRefinement(): Unit = {
+ import rootMirror.EmptyPackageClass
+
+ // class M[A]
+ val MClass = EmptyPackageClass.newClass("M")
+ val A = MClass.newTypeParameter("A").setInfo(TypeBounds.empty)
+ MClass.setInfo(PolyType(A :: Nil, ClassInfoType(ObjectClass.tpeHK :: Nil, newScopeWith(), MClass)))
+
+ // (M[Int] with M[X] { def m: Any }) forSome { type X }
+ val X = NoSymbol.newExistential("X").setInfo(TypeBounds.empty)
+ val T: Type = {
+ val decls = newScopeWith(MClass.newMethod("m").setInfo(NullaryMethodType(AnyClass.tpeHK)))
+ val refined = refinedType(appliedType(MClass, IntClass.tpeHK) :: appliedType(MClass, X.tpeHK) :: Nil, NoSymbol, decls, NoPosition)
+ newExistentialType(X :: Nil, refined)
+ }
+
+ val RefinementClass = T.underlying.typeSymbol
+ assertTrue(RefinementClass.isRefinementClass)
+ TypeRef(NoPrefix, RefinementClass, Nil) match {
+ case rtr : RefinementTypeRef =>
+ // ContainsCollector needs to look inside the info of symbols of RefinementTypeRefs
+ assert(rtr.contains(X))
+ }
+
+ val underlying = T.underlying
+ val baseTypeSeqIndices = T.baseTypeSeq.toList.indices
+ for (i <- baseTypeSeqIndices) {
+ // Elements of the existential type should have the same type symbol as underlying
+ assertEquals(T.baseTypeSeq.typeSymbol(i), underlying.baseTypeSeq.typeSymbol(i))
+ }
+
+ // Type symbols should be distinct
+ def checkDistinctTypeSyms(bts: BaseTypeSeq): Unit = {
+ val syms = baseTypeSeqIndices.map(T.baseTypeSeq.typeSymbol)
+ assertEquals(syms, syms.distinct)
+ }
+ checkDistinctTypeSyms(T.baseTypeSeq)
+ checkDistinctTypeSyms(T.underlying.baseTypeSeq)
+
+ // This is the entry for the refinement class
+ assertTrue(T.baseTypeSeq.typeSymbol(0).isRefinementClass)
+ assertEquals("M[Int] with M[X]{def m: Any} forSome { type X }", T.baseTypeSeq.rawElem(0).toString)
+
+ // This is the entry for M. The raw entry is an existential over a RefinedType which encodes a lazily computed base type
+ assertEquals(T.baseTypeSeq.typeSymbol(1), MClass)
+ assertEquals("M[X] with M[Int] forSome { type X }", T.baseTypeSeq.rawElem(1).toString)
+ // calling `apply` merges the prefix/args of the elements ot the RefinedType and rewraps in the existential
+ assertEquals("M[_1] forSome { type X; type _1 >: X with Int }", T.baseTypeSeq.apply(1).toString)
+ }
}