@@ -6,6 +6,7 @@ import Types._, Contexts._, Symbols._
6
6
import Decorators ._
7
7
import config .Config
8
8
import config .Printers ._
9
+ import collection .mutable
9
10
10
11
/** Methods for adding constraints and solving them.
11
12
*
@@ -234,8 +235,11 @@ trait ConstraintHandling {
234
235
// checkPropagated(s"adding $description")(true) // DEBUG in case following fails
235
236
checkPropagated(s " added $description" ) {
236
237
addConstraintInvocations += 1
238
+ val related = new mutable.ListBuffer [PolyParam ]()
237
239
238
- /** Drop all constrained parameters that occur at the toplevel in bound.
240
+ /** Drop all constrained parameters that occur at the toplevel in bound
241
+ * and add them to `related`, which means they will be handled by
242
+ * `addLess` calls.
239
243
* The preconditions make sure that such parameters occur only
240
244
* in one of two ways:
241
245
*
@@ -251,12 +255,18 @@ trait ConstraintHandling {
251
255
* Tsi = T1 | ... | Tn (n >= 0)
252
256
* Some of the Ti are constrained parameters
253
257
*
254
- * In each case we cannot record the relationship as an isLess, because
255
- * of the outer |/&. But we should not leave it in the constraint either
256
- * because that would risk making a parameter a subtype or supertype of a bound
257
- * where the parameter occurs again at toplevel, which leads to cycles
258
- * in the subtyping test. So we intentionally loosen the constraint in order
259
- * to keep it safe. A test case that demonstrates the problem is i864.scala.
258
+ * In each case we cannot leave the parameter in place,
259
+ * because that would risk making a parameter later a subtype or supertype
260
+ * of a bound where the parameter occurs again at toplevel, which leads to cycles
261
+ * in the subtyping test. So we intentionally narrow the constraint by
262
+ * recording an isLess relationship instead (even though this is not implied
263
+ * by the bound).
264
+ *
265
+ * Narrowing a constraint is better than widening it, because narrowing leads
266
+ * to incompleteness (which we face anyway, see for instance eitherIsSubType)
267
+ * but widening leads to unsoundness.
268
+ *
269
+ * A test case that demonstrates the problem is i864.scala.
260
270
* Turn Config.checkConstraintsSeparated on to get an accurate diagnostic
261
271
* of the cycle when it is created.
262
272
*/
@@ -266,15 +276,19 @@ trait ConstraintHandling {
266
276
case bound : TypeVar if constraint contains bound.origin =>
267
277
prune(bound.underlying)
268
278
case bound : PolyParam if constraint contains bound =>
279
+ related += bound
269
280
if (fromBelow) defn.NothingType else defn.AnyType
270
281
case _ =>
271
282
bound
272
283
}
284
+ def addParamBound (bound : PolyParam ) =
285
+ if (fromBelow) addLess(bound, param) else addLess(param, bound)
273
286
try bound match {
274
287
case bound : PolyParam if constraint contains bound =>
275
- if (fromBelow) addLess(bound, param) else addLess(param, bound)
288
+ addParamBound( bound)
276
289
case _ =>
277
290
val pbound = prune(bound)
291
+ related.foreach(addParamBound)
278
292
if (fromBelow) addLowerBound(param, pbound) else addUpperBound(param, pbound)
279
293
}
280
294
finally addConstraintInvocations -= 1
0 commit comments