@@ -35,6 +35,18 @@ trait ConstraintHandling {
35
35
36
36
private def addOneBound (param : PolyParam , bound : Type , isUpper : Boolean ): Boolean =
37
37
! constraint.contains(param) || {
38
+ def occursIn (bound : Type ): Boolean = {
39
+ val b = bound.dealias
40
+ (b eq param) || {
41
+ b match {
42
+ case b : AndOrType => occursIn(b.tp1) || occursIn(b.tp2)
43
+ case b : TypeVar => occursIn(b.origin)
44
+ case _ => false
45
+ }
46
+ }
47
+ }
48
+ if (Config .checkConstraintsNonCyclicTrans)
49
+ assert(! occursIn(bound), s " $param occurs in $bound" )
38
50
val c1 = constraint.narrowBound(param, bound, isUpper)
39
51
(c1 eq constraint) || {
40
52
constraint = c1
@@ -222,11 +234,46 @@ trait ConstraintHandling {
222
234
// checkPropagated(s"adding $description")(true) // DEBUG in case following fails
223
235
checkPropagated(s " added $description" ) {
224
236
addConstraintInvocations += 1
237
+
238
+ /** Drop all constrained parameters that occur at the toplevel in bound.
239
+ * The preconditions make sure that such parameters occur only
240
+ * in one of two ways:
241
+ *
242
+ * 1.
243
+ *
244
+ * P <: Ts1 | ... | Tsm (m > 0)
245
+ * Tsi = T1 & ... Tn (n >= 0)
246
+ * Some of the Ti are constrained parameters
247
+ *
248
+ * 2.
249
+ *
250
+ * Ts1 & ... & Tsm <: P (m > 0)
251
+ * Tsi = T1 | ... | Tn (n >= 0)
252
+ * Some of the Ti are constrained parameters
253
+ *
254
+ * In each case we cannot record the relationship as an isLess, because
255
+ * of he 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.
260
+ */
261
+ def prune (bound : Type ): Type = bound match {
262
+ case bound : AndOrType =>
263
+ bound.derivedAndOrType(prune(bound.tp1), prune(bound.tp2))
264
+ case bound : TypeVar if constraint contains bound.origin =>
265
+ prune(bound.underlying)
266
+ case bound : PolyParam if constraint contains bound =>
267
+ if (fromBelow) defn.NothingType else defn.AnyType
268
+ case _ =>
269
+ bound
270
+ }
225
271
try bound match {
226
272
case bound : PolyParam if constraint contains bound =>
227
273
if (fromBelow) addLess(bound, param) else addLess(param, bound)
228
274
case _ =>
229
- if (fromBelow) addLowerBound(param, bound) else addUpperBound(param, bound)
275
+ val pbound = prune(bound)
276
+ if (fromBelow) addLowerBound(param, pbound) else addUpperBound(param, pbound)
230
277
}
231
278
finally addConstraintInvocations -= 1
232
279
}
0 commit comments