Skip to content

Commit c618928

Browse files
committed
Avoid junk produced by Constraint#replace.
There were two instances where a constraint undergoing a replace would still refer to poly params that are no longer bound after the replace. 1. In an ordering the replaced parameters was n ot removed from the bounds of the others. 2. When a parameter refers to the replaced parameter in a type, (not a TypeBounds), the replaced parameter was not replaced. We now have checking in place that in globally committable typer states, TypeVars are not instantiated to PolyParams and (configurable) that constraints of such typer states are always closed. Fixes scala#670.
1 parent 478be16 commit c618928

File tree

6 files changed

+46
-11
lines changed

6 files changed

+46
-11
lines changed

src/dotty/tools/dotc/config/Config.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ object Config {
3232
*/
3333
final val checkConstraintsPropagated = false
3434

35+
/** Check that constraints of globally committable typer states are closed */
36+
final val checkConstraintsClosed = true
37+
3538
/** Check that no type appearing as the info of a SymDenotation contains
3639
* skolem types.
3740
*/

src/dotty/tools/dotc/core/Constraint.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,7 @@ abstract class Constraint extends Showable {
146146

147147
/** Check that no constrained parameter contains itself as a bound */
148148
def checkNonCyclic()(implicit ctx: Context): Unit
149+
150+
/** Check that constraint only refers to PolyParams bound by itself */
151+
def checkClosed()(implicit ctx: Context): Unit
149152
}

src/dotty/tools/dotc/core/OrderingConstraint.scala

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
399399
def removeParam(ps: List[PolyParam]) =
400400
ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx)
401401

402-
def replaceParam(tp: Type, atPoly: PolyType, atIdx: Int) = tp match {
402+
def replaceParam(tp: Type, atPoly: PolyType, atIdx: Int): Type = tp match {
403403
case bounds @ TypeBounds(lo, hi) =>
404404

405405
def recombine(andor: AndOrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = {
@@ -424,7 +424,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
424424
}
425425

426426
bounds.derivedTypeBounds(replaceIn(lo, isUpper = false), replaceIn(hi, isUpper = true))
427-
case _ => tp
427+
case _ =>
428+
tp.substParam(param, replacement)
428429
}
429430

430431
var current =
@@ -438,8 +439,16 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
438439
}
439440
}
440441

441-
def remove(pt: PolyType)(implicit ctx: Context): This =
442-
newConstraint(boundsMap.remove(pt), lowerMap.remove(pt), upperMap.remove(pt))
442+
def remove(pt: PolyType)(implicit ctx: Context): This = {
443+
def removeFromOrdering(po: ParamOrdering) = {
444+
def removeFromBoundss(key: PolyType, bndss: Array[List[PolyParam]]): Array[List[PolyParam]] = {
445+
val bndss1 = bndss.map(bnds => bnds.filterConserve(_.binder ne pt))
446+
if ((0 until bndss.length).forall(i => bndss(i) eq bndss1(i))) bndss else bndss1
447+
}
448+
po.remove(pt).mapValues(removeFromBoundss)
449+
}
450+
newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap))
451+
}
443452

444453
def isRemovable(pt: PolyType, removedParam: Int = -1): Boolean = {
445454
val entries = boundsMap(pt)
@@ -491,6 +500,19 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
491500
}
492501
}
493502

503+
override def checkClosed()(implicit ctx: Context): Unit = {
504+
def isFreePolyParam(tp: Type) = tp match {
505+
case PolyParam(binder, _) => !contains(binder)
506+
case _ => false
507+
}
508+
def checkClosedType(tp: Type, where: String) =
509+
if (tp != null)
510+
assert(!tp.existsPart(isFreePolyParam), i"unclosed constraint: $this refers to $tp in $where")
511+
boundsMap.foreachBinding((_, tps) => tps.foreach(checkClosedType(_, "bounds")))
512+
lowerMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "lower"))))
513+
upperMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "upper"))))
514+
}
515+
494516
private var myUninstVars: mutable.ArrayBuffer[TypeVar] = _
495517

496518
/** The uninstantiated typevars of this constraint */

src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import util.{SimpleMap, DotClass}
99
import reporting._
1010
import printing.{Showable, Printer}
1111
import printing.Texts._
12+
import config.Config
1213
import collection.mutable
1314

1415
class TyperState(r: Reporter) extends DotClass with Showable {
@@ -19,7 +20,7 @@ class TyperState(r: Reporter) extends DotClass with Showable {
1920
/** The current constraint set */
2021
def constraint: Constraint =
2122
new OrderingConstraint(SimpleMap.Empty, SimpleMap.Empty, SimpleMap.Empty)
22-
def constraint_=(c: Constraint): Unit = {}
23+
def constraint_=(c: Constraint)(implicit ctx: Context): Unit = {}
2324

2425
/** The uninstantiated variables */
2526
def uninstVars = constraint.uninstVars
@@ -85,7 +86,10 @@ extends TyperState(r) {
8586
private var myConstraint: Constraint = previous.constraint
8687

8788
override def constraint = myConstraint
88-
override def constraint_=(c: Constraint) = myConstraint = c
89+
override def constraint_=(c: Constraint)(implicit ctx: Context) = {
90+
if (Config.checkConstraintsClosed && isGlobalCommittable) c.checkClosed()
91+
myConstraint = c
92+
}
8993

9094
private var myEphemeral: Boolean = previous.ephemeral
9195

src/dotty/tools/dotc/core/Types.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2458,6 +2458,9 @@ object Types {
24582458
if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound))
24592459
inst = inst.approximateUnion
24602460

2461+
if (ctx.typerState.isGlobalCommittable)
2462+
assert(!inst.isInstanceOf[PolyParam], i"bad inst $this := $inst, constr = ${ctx.typerState.constraint}")
2463+
24612464
instantiateWith(inst)
24622465
}
24632466

tests/pending/pos/t8230a.scala renamed to tests/pos/t8230a.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ object Test {
1515
object Okay {
1616
Arr("1")
1717

18-
import I.{ arrToTrav, longArrToTrav }
19-
foo(Arr("2"))
20-
}
18+
import I.{ arrToTrav, longArrToTrav }
19+
val x = foo(Arr("2"))
20+
}
2121

2222
object Fail {
23-
import I.arrToTrav
24-
foo(Arr("3")) // found String, expected Long
23+
// import I.arrToTrav
24+
// foo(Arr("3")) // found String, expected Long
2525
}
2626
}

0 commit comments

Comments
 (0)