summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2016-07-18 11:29:44 +1000
committerAdriaan Moors <adriaan.moors@typesafe.com>2016-08-23 15:51:51 -0700
commit56f23af90732b3770770139b33f92852384c9f04 (patch)
treeb605cff4f28ea9ad1ffa2d2a0e5fad67f6776395 /src/reflect/scala/reflect/internal/BaseTypeSeqs.scala
parenta56afcd6192ae69633df23137576505015d34992 (diff)
downloadscala-56f23af90732b3770770139b33f92852384c9f04.tar.gz
scala-56f23af90732b3770770139b33f92852384c9f04.tar.bz2
scala-56f23af90732b3770770139b33f92852384c9f04.zip
Improved refinement type and existential type handling
Lazy base type seq elements are encoded as a refined type with an empty scope and a list of type refs over some common type symbol that will be merged when `BaseTypeSeq#apply` is called. The first change in this commit is to mark the creation and consumption of such elements with calls to `[is]IntersectionTypeForBaseTypeSeq`. They are distinguished by using the actual type symbol rather than a refinement class symbol, which in turn simplifies the code in `BaseTypeSeq#typeSymbol`. I have also made `lub` aware of this encoding: it is now able to "see through" to the parents of such refined types and merge them with other base types of the same class symbol (even other refined types representing lazy BTS elements.) To make this fix work, I also had to fix a bug in LUBs of multiple with existential types. Because of the way the recursion was structured in `mergePrefixAndArgs`, the order of list of types being merged changed behaviour: quantified varialbles of existential types were being rewrapped around the resultting type, but only if we hadn't encountered the first regular `TypeRef`. This can be seen with the following before/after shot: ``` // 2.11.8 scala> val ts = typeOf[Set[Any]] :: typeOf[Set[X] forSome { type X <: Y; type Y <: Int}] :: Nil; def merge(ts: List[Type]) = mergePrefixAndArgs(ts, Variance.Contravariant, lubDepth(ts)); val merged1 = merge(ts); val merged2 = merge(ts.reverse); (ts.forall(_ <:< merged1), ts.forall(_ <:< merged2)) ts: List[$r.intp.global.Type] = List(Set[Any], Set[_ <: Int]) merge: (ts: List[$r.intp.global.Type])$r.intp.global.Type merged1: $r.intp.global.Type = scala.collection.immutable.Set[_ >: Int] merged2: $r.intp.global.Type = scala.collection.immutable.Set[_53] forSome { type X <: Int; type _53 >: X } res0: (Boolean, Boolean) = (false,true) // HEAD ... merged1: $r.intp.global.Type = scala.collection.immutable.Set[_10] forSome { type X <: Int; type _10 >: X } merged2: $r.intp.global.Type = scala.collection.immutable.Set[_11] forSome { type X <: Int; type _11 >: X } res0: (Boolean, Boolean) = (true,true) ``` Furthermore, I have fixed the computation of the base type sequences of existential types over refinement types, in order to maintain the invariant that each slot of the base type sequence of a existential has the same type symbol as that of its underlying type. Before, what I've now called a `RefinementTypeRef` was transformed into a `RefinedType` during rewrapping in the existential, which led to it being wrongly considered as a lazy element of the base type sequence. The first change above should also be sufficient to avoid the bug, but I felt it was worth cleaning up `maybeRewrap` as an extra line of defence. Finally, I have added another special case to `BaseTypeSeq#apply` to be able to lazily compute elements that have been wrapped in an existential. The unit test cases in `TypesTest` rely on these changes. A subsequent commit will build on this foundation to make a fix to `asSeenFrom`.
Diffstat (limited to 'src/reflect/scala/reflect/internal/BaseTypeSeqs.scala')
-rw-r--r--src/reflect/scala/reflect/internal/BaseTypeSeqs.scala62
1 files changed, 32 insertions, 30 deletions
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
}
}