@@ -173,100 +173,124 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
173173 }
174174
175175 /** Approximate union type by intersection of its dominators.
176- * See Type#approximateUnion for an explanation.
176+ * That is, replace a union type Tn | ... | Tn
177+ * by the smallest intersection type of base-class instances of T1,...,Tn.
178+ * Example: Given
179+ *
180+ * trait C[+T]
181+ * trait D
182+ * class A extends C[A] with D
183+ * class B extends C[B] with D with E
184+ *
185+ * we approximate `A | B` by `C[A | B] with D`
177186 */
178- def approximateUnion (tp : Type ): Type = {
187+ def orDominator (tp : Type ): Type = {
188+
179189 /** a faster version of cs1 intersect cs2 */
180190 def intersect (cs1 : List [ClassSymbol ], cs2 : List [ClassSymbol ]): List [ClassSymbol ] = {
181191 val cs2AsSet = new util.HashSet [ClassSymbol ](100 )
182192 cs2.foreach(cs2AsSet.addEntry)
183193 cs1.filter(cs2AsSet.contains)
184194 }
195+
185196 /** The minimal set of classes in `cs` which derive all other classes in `cs` */
186197 def dominators (cs : List [ClassSymbol ], accu : List [ClassSymbol ]): List [ClassSymbol ] = (cs : @ unchecked) match {
187198 case c :: rest =>
188199 val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
189200 if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
190201 }
202+
203+ def mergeRefined (tp1 : Type , tp2 : Type ): Type = {
204+ def fail = throw new AssertionError (i " Failure to join alternatives $tp1 and $tp2" )
205+ tp1 match {
206+ case tp1 @ RefinedType (parent1, name1, rinfo1) =>
207+ tp2 match {
208+ case RefinedType (parent2, `name1`, rinfo2) =>
209+ tp1.derivedRefinedType(
210+ mergeRefined(parent1, parent2), name1, rinfo1 | rinfo2)
211+ case _ => fail
212+ }
213+ case tp1 @ TypeRef (pre1, name1) =>
214+ tp2 match {
215+ case tp2 @ TypeRef (pre2, `name1`) =>
216+ tp1.derivedSelect(pre1 | pre2)
217+ case _ => fail
218+ }
219+ case _ => fail
220+ }
221+ }
222+
191223 def approximateOr (tp1 : Type , tp2 : Type ): Type = {
192224 def isClassRef (tp : Type ): Boolean = tp match {
193225 case tp : TypeRef => tp.symbol.isClass
194226 case tp : RefinedType => isClassRef(tp.parent)
195227 case _ => false
196228 }
197229
198- /** If `tp1` and `tp2` are typebounds, try to make one fit into the other
199- * or to make them equal, by instantiating uninstantiated type variables.
200- */
201- def homogenizedUnion (tp1 : Type , tp2 : Type ): Type = {
202- tp1 match {
203- case tp1 : TypeBounds =>
204- tp2 match {
205- case tp2 : TypeBounds =>
206- def fitInto (tp1 : TypeBounds , tp2 : TypeBounds ): Unit = {
207- val nestedCtx = ctx.fresh.setNewTyperState
208- if (tp2.boundsInterval.contains(tp1.boundsInterval)(nestedCtx))
209- nestedCtx.typerState.commit()
210- }
211- fitInto(tp1, tp2)
212- fitInto(tp2, tp1)
213- case _ =>
214- }
215- case _ =>
216- }
217- tp1 | tp2
218- }
219-
220- tp1 match {
221- case tp1 : RefinedType =>
222- tp2 match {
223- case tp2 : RefinedType if tp1.refinedName == tp2.refinedName =>
224- return tp1.derivedRefinedType(
225- approximateUnion(OrType (tp1.parent, tp2.parent)),
226- tp1.refinedName,
227- homogenizedUnion(tp1.refinedInfo, tp2.refinedInfo))
228- // .ensuring { x => println(i"approx or $tp1 | $tp2 = $x\n constr = ${ctx.typerState.constraint}"); true } // DEBUG
229- case _ =>
230- }
231- case _ =>
232- }
233-
234230 tp1 match {
235231 case tp1 : RecType =>
236232 tp1.rebind(approximateOr(tp1.parent, tp2))
237233 case tp1 : TypeProxy if ! isClassRef(tp1) =>
238- approximateUnion (tp1.superType | tp2)
234+ orDominator (tp1.superType | tp2)
239235 case _ =>
240236 tp2 match {
241237 case tp2 : RecType =>
242238 tp2.rebind(approximateOr(tp1, tp2.parent))
243239 case tp2 : TypeProxy if ! isClassRef(tp2) =>
244- approximateUnion (tp1 | tp2.superType)
240+ orDominator (tp1 | tp2.superType)
245241 case _ =>
246242 val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
247243 val doms = dominators(commonBaseClasses, Nil )
248- def baseTp (cls : ClassSymbol ): Type =
249- if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls)
250- else tp.baseTypeWithArgs(cls)
244+ def baseTp (cls : ClassSymbol ): Type = {
245+ val base =
246+ if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls)
247+ else tp.baseTypeWithArgs(cls)
248+ base.mapReduceOr(identity)(mergeRefined)
249+ }
251250 doms.map(baseTp).reduceLeft(AndType .apply)
252251 }
253252 }
254253 }
255- if (ctx.featureEnabled(defn. LanguageModuleClass , nme.keepUnions)) tp
256- else tp match {
254+
255+ tp match {
257256 case tp : OrType =>
258- approximateOr(tp.tp1, tp.tp2) // Maybe refactor using liftToRec?
259- case tp @ AndType (tp1, tp2) =>
260- tp derived_& (approximateUnion(tp1), approximateUnion(tp2))
261- case tp : RefinedType =>
262- tp.derivedRefinedType(approximateUnion(tp.parent), tp.refinedName, tp.refinedInfo)
263- case tp : RecType =>
264- tp.rebind(approximateUnion(tp.parent))
257+ approximateOr(tp.tp1, tp.tp2)
265258 case _ =>
266259 tp
267260 }
268261 }
269262
263+ /** Given a disjunction T1 | ... | Tn of types with potentially embedded
264+ * type variables, constrain type variables further if this eliminates
265+ * some of the branches of the disjunction. Do this also for disjunctions
266+ * embedded in intersections, as parents in refinements, and in recursive types.
267+ *
268+ * For instance, if `A` is an unconstrained type variable, then
269+ *
270+ * ArrayBuffer[Int] | ArrayBuffer[A]
271+ *
272+ * is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]`
273+ * instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
274+ */
275+ def harmonizeUnion (tp : Type ): Type = tp match {
276+ case tp : OrType =>
277+ joinIfScala2(typeComparer.fluidly(tp.tp1 | tp.tp2))
278+ case tp @ AndType (tp1, tp2) =>
279+ tp derived_& (harmonizeUnion(tp1), harmonizeUnion(tp2))
280+ case tp : RefinedType =>
281+ tp.derivedRefinedType(harmonizeUnion(tp.parent), tp.refinedName, tp.refinedInfo)
282+ case tp : RecType =>
283+ tp.rebind(harmonizeUnion(tp.parent))
284+ case _ =>
285+ tp
286+ }
287+
288+ /** Under -language:Scala2: Replace or-types with their joins */
289+ private def joinIfScala2 (tp : Type ) = tp match {
290+ case tp : OrType if scala2Mode => tp.join
291+ case _ => tp
292+ }
293+
270294 /** Not currently needed:
271295 *
272296 def liftToRec(f: (Type, Type) => Type)(tp1: Type, tp2: Type)(implicit ctx: Context) = {
@@ -493,7 +517,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
493517 */
494518 def featureEnabled (owner : ClassSymbol , feature : TermName ): Boolean = {
495519 def toPrefix (sym : Symbol ): String =
496- if (! sym.exists || (sym eq defn.LanguageModuleClass ) || (sym eq defn. Scala2LanguageModuleClass ) ) " "
520+ if (! sym.exists || (sym eq defn.LanguageModuleClass )) " "
497521 else toPrefix(sym.owner) + sym.name + " ."
498522 def featureName = toPrefix(owner) + feature
499523 def hasImport (implicit ctx : Context ): Boolean = {
@@ -512,13 +536,13 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
512536
513537 /** Is auto-tupling enabled? */
514538 def canAutoTuple =
515- ! featureEnabled(defn.Scala2LanguageModuleClass , nme.noAutoTupling)
539+ ! featureEnabled(defn.LanguageModuleClass , nme.noAutoTupling)
516540
517541 def scala2Mode =
518542 featureEnabled(defn.LanguageModuleClass , nme.Scala2 )
519543
520544 def dynamicsEnabled =
521- featureEnabled(defn.Scala2LanguageModuleClass , nme.dynamics)
545+ featureEnabled(defn.LanguageModuleClass , nme.dynamics)
522546
523547 def testScala2Mode (msg : String , pos : Position ) = {
524548 if (scala2Mode) migrationWarning(msg, pos)
0 commit comments