@@ -173,100 +173,124 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
173
173
}
174
174
175
175
/** 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`
177
186
*/
178
- def approximateUnion (tp : Type ): Type = {
187
+ def orDominator (tp : Type ): Type = {
188
+
179
189
/** a faster version of cs1 intersect cs2 */
180
190
def intersect (cs1 : List [ClassSymbol ], cs2 : List [ClassSymbol ]): List [ClassSymbol ] = {
181
191
val cs2AsSet = new util.HashSet [ClassSymbol ](100 )
182
192
cs2.foreach(cs2AsSet.addEntry)
183
193
cs1.filter(cs2AsSet.contains)
184
194
}
195
+
185
196
/** The minimal set of classes in `cs` which derive all other classes in `cs` */
186
197
def dominators (cs : List [ClassSymbol ], accu : List [ClassSymbol ]): List [ClassSymbol ] = (cs : @ unchecked) match {
187
198
case c :: rest =>
188
199
val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
189
200
if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
190
201
}
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
+
191
223
def approximateOr (tp1 : Type , tp2 : Type ): Type = {
192
224
def isClassRef (tp : Type ): Boolean = tp match {
193
225
case tp : TypeRef => tp.symbol.isClass
194
226
case tp : RefinedType => isClassRef(tp.parent)
195
227
case _ => false
196
228
}
197
229
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
-
234
230
tp1 match {
235
231
case tp1 : RecType =>
236
232
tp1.rebind(approximateOr(tp1.parent, tp2))
237
233
case tp1 : TypeProxy if ! isClassRef(tp1) =>
238
- approximateUnion (tp1.superType | tp2)
234
+ orDominator (tp1.superType | tp2)
239
235
case _ =>
240
236
tp2 match {
241
237
case tp2 : RecType =>
242
238
tp2.rebind(approximateOr(tp1, tp2.parent))
243
239
case tp2 : TypeProxy if ! isClassRef(tp2) =>
244
- approximateUnion (tp1 | tp2.superType)
240
+ orDominator (tp1 | tp2.superType)
245
241
case _ =>
246
242
val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
247
243
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
+ }
251
250
doms.map(baseTp).reduceLeft(AndType .apply)
252
251
}
253
252
}
254
253
}
255
- if (ctx.featureEnabled(defn. LanguageModuleClass , nme.keepUnions)) tp
256
- else tp match {
254
+
255
+ tp match {
257
256
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)
265
258
case _ =>
266
259
tp
267
260
}
268
261
}
269
262
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
+
270
294
/** Not currently needed:
271
295
*
272
296
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.
493
517
*/
494
518
def featureEnabled (owner : ClassSymbol , feature : TermName ): Boolean = {
495
519
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 )) " "
497
521
else toPrefix(sym.owner) + sym.name + " ."
498
522
def featureName = toPrefix(owner) + feature
499
523
def hasImport (implicit ctx : Context ): Boolean = {
@@ -512,13 +536,13 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
512
536
513
537
/** Is auto-tupling enabled? */
514
538
def canAutoTuple =
515
- ! featureEnabled(defn.Scala2LanguageModuleClass , nme.noAutoTupling)
539
+ ! featureEnabled(defn.LanguageModuleClass , nme.noAutoTupling)
516
540
517
541
def scala2Mode =
518
542
featureEnabled(defn.LanguageModuleClass , nme.Scala2 )
519
543
520
544
def dynamicsEnabled =
521
- featureEnabled(defn.Scala2LanguageModuleClass , nme.dynamics)
545
+ featureEnabled(defn.LanguageModuleClass , nme.dynamics)
522
546
523
547
def testScala2Mode (msg : String , pos : Position ) = {
524
548
if (scala2Mode) migrationWarning(msg, pos)
0 commit comments