aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/TypeOps.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-10-01 21:35:58 +0200
committerGuillaume Martres <smarter@ubuntu.com>2016-10-11 19:21:02 +0200
commit98a69d90b16f0cc997255f097d3d5d9f2a60301b (patch)
treec3640fb82f53f4f6e250de59d3536f638b63f1aa /src/dotty/tools/dotc/core/TypeOps.scala
parentf6291e42082708e164ebc3456d84e69f4f29cf59 (diff)
downloaddotty-98a69d90b16f0cc997255f097d3d5d9f2a60301b.tar.gz
dotty-98a69d90b16f0cc997255f097d3d5d9f2a60301b.tar.bz2
dotty-98a69d90b16f0cc997255f097d3d5d9f2a60301b.zip
Keep or types
Don't replace them by their dominators, unless one of the following holds: - language:Scala2 mode is on - we are at the point of findMember selection - we compare with a higher-kinded application This means approximateUnion is now split into harmonizeUnion and orDominator which each implement one of the former's two functionalities.
Diffstat (limited to 'src/dotty/tools/dotc/core/TypeOps.scala')
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala128
1 files changed, 76 insertions, 52 deletions
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index bee69ae69..5edd61c70 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -173,21 +173,53 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
}
/** Approximate union type by intersection of its dominators.
- * See Type#approximateUnion for an explanation.
+ * That is, replace a union type Tn | ... | Tn
+ * by the smallest intersection type of base-class instances of T1,...,Tn.
+ * Example: Given
+ *
+ * trait C[+T]
+ * trait D
+ * class A extends C[A] with D
+ * class B extends C[B] with D with E
+ *
+ * we approximate `A | B` by `C[A | B] with D`
*/
- def approximateUnion(tp: Type): Type = {
+ def orDominator(tp: Type): Type = {
+
/** a faster version of cs1 intersect cs2 */
def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = {
val cs2AsSet = new util.HashSet[ClassSymbol](100)
cs2.foreach(cs2AsSet.addEntry)
cs1.filter(cs2AsSet.contains)
}
+
/** The minimal set of classes in `cs` which derive all other classes in `cs` */
def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match {
case c :: rest =>
val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
}
+
+ def mergeRefined(tp1: Type, tp2: Type): Type = {
+ def fail = throw new AssertionError(i"Failure to join alternatives $tp1 and $tp2")
+ tp1 match {
+ case tp1 @ RefinedType(parent1, name1, rinfo1) =>
+ tp2 match {
+ case RefinedType(parent2, `name1`, rinfo2) =>
+ tp1.derivedRefinedType(
+ mergeRefined(parent1, parent2), name1, rinfo1 | rinfo2)
+ case _ => fail
+ }
+ case tp1 @ TypeRef(pre1, name1) =>
+ tp2 match {
+ case tp2 @ TypeRef(pre2, `name1`) =>
+ tp1.derivedSelect(pre1 | pre2)
+ case _ => fail
+ }
+ case _ => fail
+ }
+ }
+
def approximateOr(tp1: Type, tp2: Type): Type = {
def isClassRef(tp: Type): Boolean = tp match {
case tp: TypeRef => tp.symbol.isClass
@@ -195,78 +227,70 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
case _ => false
}
- /** If `tp1` and `tp2` are typebounds, try to make one fit into the other
- * or to make them equal, by instantiating uninstantiated type variables.
- */
- def homogenizedUnion(tp1: Type, tp2: Type): Type = {
- tp1 match {
- case tp1: TypeBounds =>
- tp2 match {
- case tp2: TypeBounds =>
- def fitInto(tp1: TypeBounds, tp2: TypeBounds): Unit = {
- val nestedCtx = ctx.fresh.setNewTyperState
- if (tp2.boundsInterval.contains(tp1.boundsInterval)(nestedCtx))
- nestedCtx.typerState.commit()
- }
- fitInto(tp1, tp2)
- fitInto(tp2, tp1)
- case _ =>
- }
- case _ =>
- }
- tp1 | tp2
- }
-
- tp1 match {
- case tp1: RefinedType =>
- tp2 match {
- case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
- return tp1.derivedRefinedType(
- approximateUnion(OrType(tp1.parent, tp2.parent)),
- tp1.refinedName,
- homogenizedUnion(tp1.refinedInfo, tp2.refinedInfo))
- //.ensuring { x => println(i"approx or $tp1 | $tp2 = $x\n constr = ${ctx.typerState.constraint}"); true } // DEBUG
- case _ =>
- }
- case _ =>
- }
-
tp1 match {
case tp1: RecType =>
tp1.rebind(approximateOr(tp1.parent, tp2))
case tp1: TypeProxy if !isClassRef(tp1) =>
- approximateUnion(tp1.superType | tp2)
+ orDominator(tp1.superType | tp2)
case _ =>
tp2 match {
case tp2: RecType =>
tp2.rebind(approximateOr(tp1, tp2.parent))
case tp2: TypeProxy if !isClassRef(tp2) =>
- approximateUnion(tp1 | tp2.superType)
+ orDominator(tp1 | tp2.superType)
case _ =>
val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
val doms = dominators(commonBaseClasses, Nil)
- def baseTp(cls: ClassSymbol): Type =
- if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls)
- else tp.baseTypeWithArgs(cls)
+ def baseTp(cls: ClassSymbol): Type = {
+ val base =
+ if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls)
+ else tp.baseTypeWithArgs(cls)
+ base.mapReduceOr(identity)(mergeRefined)
+ }
doms.map(baseTp).reduceLeft(AndType.apply)
}
}
}
- if (ctx.featureEnabled(defn.LanguageModuleClass, nme.keepUnions)) tp
- else tp match {
+
+ tp match {
case tp: OrType =>
- approximateOr(tp.tp1, tp.tp2) // Maybe refactor using liftToRec?
- case tp @ AndType(tp1, tp2) =>
- tp derived_& (approximateUnion(tp1), approximateUnion(tp2))
- case tp: RefinedType =>
- tp.derivedRefinedType(approximateUnion(tp.parent), tp.refinedName, tp.refinedInfo)
- case tp: RecType =>
- tp.rebind(approximateUnion(tp.parent))
+ approximateOr(tp.tp1, tp.tp2)
case _ =>
tp
}
}
+ /** Given a disjunction T1 | ... | Tn of types with potentially embedded
+ * type variables, constrain type variables further if this eliminates
+ * some of the branches of the disjunction. Do this also for disjunctions
+ * embedded in intersections, as parents in refinements, and in recursive types.
+ *
+ * For instance, if `A` is an unconstrained type variable, then
+ *
+ * ArrayBuffer[Int] | ArrayBuffer[A]
+ *
+ * is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]`
+ * instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
+ */
+ def harmonizeUnion(tp: Type): Type = tp match {
+ case tp: OrType =>
+ joinIfScala2(typeComparer.fluidly(tp.tp1 | tp.tp2))
+ case tp @ AndType(tp1, tp2) =>
+ tp derived_& (harmonizeUnion(tp1), harmonizeUnion(tp2))
+ case tp: RefinedType =>
+ tp.derivedRefinedType(harmonizeUnion(tp.parent), tp.refinedName, tp.refinedInfo)
+ case tp: RecType =>
+ tp.rebind(harmonizeUnion(tp.parent))
+ case _ =>
+ tp
+ }
+
+ /** Under -language:Scala2: Replace or-types with their joins */
+ private def joinIfScala2(tp: Type) = tp match {
+ case tp: OrType if scala2Mode => tp.join
+ case _ => tp
+ }
+
/** Not currently needed:
*
def liftToRec(f: (Type, Type) => Type)(tp1: Type, tp2: Type)(implicit ctx: Context) = {