@@ -389,7 +389,7 @@ trait Inferencing { this: Typer =>
389
389
if ((ownedVars ne locked) && ! ownedVars.isEmpty) {
390
390
val qualifying = ownedVars -- locked
391
391
if (! qualifying.isEmpty) {
392
- typr.println(i " interpolate $tree: ${tree.tpe.widen} in $state, owned vars = ${state.ownedVars.toList}%, %, previous = ${locked.toList}%, % / ${state.constraint}" )
392
+ typr.println(i " interpolate $tree: ${tree.tpe.widen} in $state, owned vars = ${state.ownedVars.toList}%, %, qualifying = ${qualifying.toList} %, %, previous = ${locked.toList}%, % / ${state.constraint}" )
393
393
val resultAlreadyConstrained =
394
394
tree.isInstanceOf [Apply ] || tree.tpe.isInstanceOf [MethodOrPoly ]
395
395
if (! resultAlreadyConstrained)
@@ -421,22 +421,68 @@ trait Inferencing { this: Typer =>
421
421
// val y: List[List[String]] = List(List(1))
422
422
val hasUnreportedErrors = state.reporter.hasUnreportedErrors
423
423
def constraint = state.constraint
424
+ type InstantiateQueue = mutable.ListBuffer [(TypeVar , Boolean )]
425
+ val toInstantiate = new InstantiateQueue
424
426
for (tvar <- qualifying)
425
- if (! tvar.isInstantiated && state. constraint.contains(tvar)) {
427
+ if (! tvar.isInstantiated && constraint.contains(tvar)) {
426
428
// Needs to be checked again, since previous interpolations could already have
427
429
// instantiated `tvar` through unification.
428
430
val v = vs(tvar)
429
431
if (v == null ) {
430
432
typr.println(i " interpolate non-occurring $tvar in $state in $tree: $tp, fromBelow = ${tvar.hasLowerBound}, $constraint" )
431
- tvar.instantiate(fromBelow = tvar.hasLowerBound)
433
+ toInstantiate += (( tvar, tvar .hasLowerBound) )
432
434
}
433
435
else if (! hasUnreportedErrors)
434
436
if (v.intValue != 0 ) {
435
437
typr.println(i " interpolate $tvar in $state in $tree: $tp, fromBelow = ${v.intValue == 1 }, $constraint" )
436
- tvar.instantiate(fromBelow = v.intValue == 1 )
438
+ toInstantiate += ((tvar, v.intValue == 1 ) )
437
439
}
438
440
else typr.println(i " no interpolation for nonvariant $tvar in $state" )
439
441
}
442
+
443
+ /** Instantiate all type variables in `buf` in the indicated directions.
444
+ * If a type variable A is instantiated from below, and there is another
445
+ * type variable B in `buf` that is known to be smaller than A, wait and
446
+ * instantiate all other type variables before trying to instantiate A again.
447
+ * Dually, wait instantiating a type variable from above as long as it has
448
+ * upper bounds in `buf`.
449
+ *
450
+ * This is done to avoid loss of precision when forming unions. An example
451
+ * is in i7558.scala:
452
+ *
453
+ * type Tr[+V1, +O1 <: V1]
454
+ * def [V2, O2 <: V2](tr: Tr[V2, O2]) sl: Tr[V2, O2] = ???
455
+ * def as[V3, O3 <: V3](tr: Tr[V3, O3]) : Tr[V3, O3] = tr.sl
456
+ *
457
+ * Here we interpolate at some point V2 and O2 given the constraint
458
+ *
459
+ * V2 >: V3, O2 >: O3, O2 <: V2
460
+ *
461
+ * where O3 and V3 are type refs with O3 <: V3.
462
+ * If we interpolate V2 first to V3 | O2, the widenUnion algorithm will
463
+ * instantiate O2 to V3, leading to the final constraint
464
+ *
465
+ * V2 := V3, O2 := V3
466
+ *
467
+ * But if we instantiate O2 first to O3, and V2 next to V3, we get the
468
+ * more flexible instantiation
469
+ *
470
+ * V2 := V3, O2 := O3
471
+ */
472
+ def doInstantiate (buf : InstantiateQueue ): Unit =
473
+ if buf.nonEmpty then
474
+ val suspended = new InstantiateQueue
475
+ while buf.nonEmpty do
476
+ val first @ (tvar, fromBelow) = buf.head
477
+ buf.dropInPlace(1 )
478
+ val suspend = buf.exists{ (following, _) =>
479
+ if fromBelow then constraint.isLess(following.origin, tvar.origin)
480
+ else constraint.isLess(following.origin, tvar.origin)
481
+ }
482
+ if suspend then suspended += first else tvar.instantiate(fromBelow)
483
+ doInstantiate(suspended)
484
+ end doInstantiate
485
+ doInstantiate(toInstantiate)
440
486
}
441
487
}
442
488
tree
0 commit comments