@@ -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
*
@@ -35,6 +36,18 @@ trait ConstraintHandling {
35
36
36
37
private def addOneBound (param : PolyParam , bound : Type , isUpper : Boolean ): Boolean =
37
38
! constraint.contains(param) || {
39
+ def occursIn (bound : Type ): Boolean = {
40
+ val b = bound.dealias
41
+ (b eq param) || {
42
+ b match {
43
+ case b : AndOrType => occursIn(b.tp1) || occursIn(b.tp2)
44
+ case b : TypeVar => occursIn(b.origin)
45
+ case _ => false
46
+ }
47
+ }
48
+ }
49
+ if (Config .checkConstraintsSeparated)
50
+ assert(! occursIn(bound), s " $param occurs in $bound" )
38
51
val c1 = constraint.narrowBound(param, bound, isUpper)
39
52
(c1 eq constraint) || {
40
53
constraint = c1
@@ -210,7 +223,7 @@ trait ConstraintHandling {
210
223
final def canConstrain (param : PolyParam ): Boolean =
211
224
! frozenConstraint && (constraint contains param)
212
225
213
- /** Add constraint `param <: bond ` if `fromBelow` is true , `param >: bound` otherwise.
226
+ /** Add constraint `param <: bound ` if `fromBelow` is false , `param >: bound` otherwise.
214
227
* `bound` is assumed to be in normalized form, as specified in `firstTry` and
215
228
* `secondTry` of `TypeComparer`. In particular, it should not be an alias type,
216
229
* lazy ref, typevar, wildcard type, error type. In addition, upper bounds may
@@ -222,11 +235,67 @@ trait ConstraintHandling {
222
235
// checkPropagated(s"adding $description")(true) // DEBUG in case following fails
223
236
checkPropagated(s " added $description" ) {
224
237
addConstraintInvocations += 1
238
+
239
+ def addParamBound (bound : PolyParam ) =
240
+ if (fromBelow) addLess(bound, param) else addLess(param, bound)
241
+
242
+ /** Drop all constrained parameters that occur at the toplevel in `bound` and
243
+ * handle them by `addLess` calls.
244
+ * The preconditions make sure that such parameters occur only
245
+ * in one of two ways:
246
+ *
247
+ * 1.
248
+ *
249
+ * P <: Ts1 | ... | Tsm (m > 0)
250
+ * Tsi = T1 & ... Tn (n >= 0)
251
+ * Some of the Ti are constrained parameters
252
+ *
253
+ * 2.
254
+ *
255
+ * Ts1 & ... & Tsm <: P (m > 0)
256
+ * Tsi = T1 | ... | Tn (n >= 0)
257
+ * Some of the Ti are constrained parameters
258
+ *
259
+ * In each case we cannot leave the parameter in place,
260
+ * because that would risk making a parameter later a subtype or supertype
261
+ * of a bound where the parameter occurs again at toplevel, which leads to cycles
262
+ * in the subtyping test. So we intentionally narrow the constraint by
263
+ * recording an isLess relationship instead (even though this is not implied
264
+ * by the bound).
265
+ *
266
+ * Narrowing a constraint is better than widening it, because narrowing leads
267
+ * to incompleteness (which we face anyway, see for instance eitherIsSubType)
268
+ * but widening leads to unsoundness.
269
+ *
270
+ * A test case that demonstrates the problem is i864.scala.
271
+ * Turn Config.checkConstraintsSeparated on to get an accurate diagnostic
272
+ * of the cycle when it is created.
273
+ *
274
+ * @return The pruned type if all `addLess` calls succeed, `NoType` otherwise.
275
+ */
276
+ def prune (bound : Type ): Type = bound match {
277
+ case bound : AndOrType =>
278
+ val p1 = prune(bound.tp1)
279
+ val p2 = prune(bound.tp2)
280
+ if (p1.exists && p2.exists) bound.derivedAndOrType(p1, p2)
281
+ else NoType
282
+ case bound : TypeVar if constraint contains bound.origin =>
283
+ prune(bound.underlying)
284
+ case bound : PolyParam if constraint contains bound =>
285
+ if (! addParamBound(bound)) NoType
286
+ else if (fromBelow) defn.NothingType
287
+ else defn.AnyType
288
+ case _ =>
289
+ bound
290
+ }
291
+
225
292
try bound match {
226
293
case bound : PolyParam if constraint contains bound =>
227
- if (fromBelow) addLess(bound, param) else addLess(param, bound)
294
+ addParamBound( bound)
228
295
case _ =>
229
- if (fromBelow) addLowerBound(param, bound) else addUpperBound(param, bound)
296
+ val pbound = prune(bound)
297
+ pbound.exists && (
298
+ if (fromBelow) addLowerBound(param, pbound) else addUpperBound(param, pbound))
230
299
}
231
300
finally addConstraintInvocations -= 1
232
301
}
0 commit comments