@@ -131,7 +131,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
131
131
* class A extends C[A] with D
132
132
* class B extends C[B] with D with E
133
133
*
134
- * we approximate `A | B` by `C[A | B] with D`
134
+ * we approximate `A | B` by `C[A | B] with D`.
135
135
*/
136
136
def orDominator (tp : Type ): Type = {
137
137
@@ -188,29 +188,68 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
188
188
case _ => false
189
189
}
190
190
191
+ // Step 1: Get RecTypes and ErrorTypes out of the way,
191
192
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
198
195
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
- }
213
196
}
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)
214
253
}
215
254
216
255
tp match {
0 commit comments