@@ -131,7 +131,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
131131 * class A extends C[A] with D
132132 * class B extends C[B] with D with E
133133 *
134- * we approximate `A | B` by `C[A | B] with D`
134+ * we approximate `A | B` by `C[A | B] with D`.
135135 */
136136 def orDominator (tp : Type ): Type = {
137137
@@ -188,29 +188,68 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
188188 case _ => false
189189 }
190190
191+ // Step 1: Get RecTypes and ErrorTypes out of the way,
191192 tp1 match {
192- case tp1 : RecType =>
193- tp1.rebind(approximateOr(tp1.parent, tp2))
194- case tp1 : TypeProxy if ! isClassRef(tp1) =>
195- orDominator(tp1.superType | tp2)
196- case err : ErrorType =>
197- err
193+ case tp1 : RecType => return tp1.rebind(approximateOr(tp1.parent, tp2))
194+ case err : ErrorType => return err
198195 case _ =>
199- tp2 match {
200- case tp2 : RecType =>
201- tp2.rebind(approximateOr(tp1, tp2.parent))
202- case tp2 : TypeProxy if ! isClassRef(tp2) =>
203- orDominator(tp1 | tp2.superType)
204- case err : ErrorType =>
205- err
206- case _ =>
207- val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
208- val doms = dominators(commonBaseClasses, Nil )
209- def baseTp (cls : ClassSymbol ): Type =
210- tp.baseType(cls).mapReduceOr(identity)(mergeRefinedOrApplied)
211- doms.map(baseTp).reduceLeft(AndType .apply)
212- }
213196 }
197+ tp2 match {
198+ case tp2 : RecType => return tp2.rebind(approximateOr(tp1, tp2.parent))
199+ case err : ErrorType => return err
200+ case _ =>
201+ }
202+
203+ // Step 2: Try to widen either side. This is tricky and currently incomplete.
204+ // An illustration is in test pos/padTo.scala: Here we nee to compute the join of
205+ //
206+ // `A | C` under the constraints `B >: A` and `C <: B`
207+ //
208+ // where `A, B, C` are type parameters.
209+ // Widening `A` to its upper bound would give `Any | C`, i.e. `Any`.
210+ // But widening `C` first would give `A | B` and then `B`.
211+ // So we need to widen `C` first. But how to decide this in general?
212+ // In the algorithm below, we widen both sides, and then check whether
213+ // one widened type is a supertype of the other original type, in which
214+ // case we can immediately pick that widened type as the join.
215+ // If that yields no result, we pick the second widened type if it is
216+ // a subtype of the first widened type and the first widened type otherwise.
217+ // At this last step we lose possible solutions, since we have to make an
218+ // arbitrary choice which side to widen. A better solution would look at
219+ // the constituents of each operand (if the operand is an OrType again) and
220+ // try to widen them selectively in turn. But this might lead to a combinatorial
221+ // explosion of possibilities.
222+ //
223+ // Another approach could be to store information contained in lower bounds
224+ // on both sides. So if `B >: A` we'd also record that `A <: B` and therefore
225+ // widening `A` would yield `B` instead of `Any`, so we'd still be on the right track.
226+ // This looks feasible if lower bounds are type parameters, but tricky if they
227+ // are something else. We'd have to extract the strongest possible
228+ // constraint over all type parameters that is implied by a lower bound.
229+ // This looks related to an algorithmic problem arising in GADT matching.
230+ val tp1w = tp1 match {
231+ case tp1 : TypeProxy if ! isClassRef(tp1) => tp1.superType
232+ case _ => tp1
233+ }
234+ val tp2w = tp2 match {
235+ case tp2 : TypeProxy if ! isClassRef(tp2) => tp2.superType
236+ case _ => tp1
237+ }
238+ if ((tp1w ne tp1) || (tp2w ne tp2)) {
239+ return {
240+ if (tp1 frozen_<:< tp2w) tp2w
241+ else if (tp2 frozen_<:< tp1w) tp1w
242+ else if (tp2w frozen_<:< tp1w) orDominator(tp1 | tp2w)
243+ else orDominator(tp1w | tp2)
244+ }
245+ }
246+
247+ // Step 3: Intersect base classes of both sides
248+ val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
249+ val doms = dominators(commonBaseClasses, Nil )
250+ def baseTp (cls : ClassSymbol ): Type =
251+ tp.baseType(cls).mapReduceOr(identity)(mergeRefinedOrApplied)
252+ doms.map(baseTp).reduceLeft(AndType .apply)
214253 }
215254
216255 tp match {
0 commit comments