Skip to content

Commit 165f714

Browse files
committed
WIP
1 parent c58b23f commit 165f714

File tree

7 files changed

+138
-32
lines changed

7 files changed

+138
-32
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object Config {
1313
/** When updating a constraint bound, check that the constrained parameter
1414
* does not appear at the top-level of either of its bounds.
1515
*/
16-
final val checkConstraintsNonCyclic = false
16+
final val checkConstraintsNonCyclic = true
1717

1818
/** Check that each constraint resulting from a subtype test
1919
* is satisfiable.

compiler/src/dotty/tools/dotc/config/Settings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ object Settings {
3636
}
3737

3838
def update(idx: Int, x: Any): SettingsState =
39-
if (_wasRead)
39+
if _wasRead && false then
4040
new SettingsState(values.toSeq).update(idx, x)
4141
else {
4242
values(idx) = x

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ abstract class Constraint extends Showable {
151151
def & (other: Constraint, otherHasErrors: Boolean)(implicit ctx: Context): Constraint
152152

153153
/** Check that no constrained parameter contains itself as a bound */
154-
def checkNonCyclic()(implicit ctx: Context): Unit
154+
def checkNonCyclic()(implicit ctx: Context): this.type
155155

156156
/** Check that constraint only refers to TypeParamRefs bound by itself */
157157
def checkClosed()(implicit ctx: Context): Unit

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,11 @@ trait ConstraintHandling[AbstractContext] {
163163
if (isUpper) oldBounds.derivedTypeBounds(lo, hi & bound)
164164
else oldBounds.derivedTypeBounds(lo | bound, hi)
165165
finally homogenizeArgs = saved
166-
val c1 = constraint.updateEntry(param, narrowedBounds)
166+
val c1 =
167+
try constraint.updateEntry(param, narrowedBounds)
168+
catch case ex: AssertionError =>
169+
println(i"assertion failed while adding $param $rawBound fromBelow = ${!isUpper}, bound = $bound, old = $oldBounds, narrowed = $narrowedBounds to $constraint")
170+
throw ex
167171
(c1 eq constraint) || {
168172
constraint = c1
169173
val TypeBounds(lo, hi) = constraint.entry(param)
@@ -314,9 +318,35 @@ trait ConstraintHandling[AbstractContext] {
314318
}
315319
}
316320
}
321+
def avoidCycle(tp: Type): Type = tp match
322+
case tp: AndOrType =>
323+
tp.derivedAndOrType(avoidCycle(tp.tp1), avoidCycle(tp.tp2))
324+
case tp: TypeParamRef =>
325+
if tp eq param then
326+
if fromBelow then defn.NothingType else defn.AnyType
327+
else constraint.entry(tp) match // (3)
328+
case NoType => tp
329+
case TypeBounds(lo, hi) => if lo eq hi then avoidCycle(lo) else tp
330+
case inst => avoidCycle(inst)
331+
case _ => tp
332+
317333
constraint.entry(param) match {
318334
case _: TypeBounds =>
319335
val bound = if (fromBelow) fullLowerBound(param) else fullUpperBound(param)
336+
337+
def avoidCycle(tp: Type): Type = tp match
338+
case tp: AndOrType =>
339+
tp.derivedAndOrType(avoidCycle(tp.tp1), avoidCycle(tp.tp2))
340+
case tp: TypeParamRef =>
341+
if tp eq param then
342+
println(i"STRIP $tp from $bound $fromBelow")
343+
if fromBelow then defn.NothingType else defn.AnyType
344+
else constraint.entry(tp) match // (3)
345+
case NoType => tp
346+
case TypeBounds(lo, hi) => if lo eq hi then avoidCycle(lo) else tp
347+
case inst => avoidCycle(inst)
348+
case _ => tp
349+
320350
val inst = avoidParam(bound)
321351
typr_println(s"approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}")
322352
inst

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

Lines changed: 98 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ object OrderingConstraint {
2626
/** A new constraint with given maps */
2727
private def newConstraint(boundsMap: ParamBounds, lowerMap: ParamOrdering, upperMap: ParamOrdering)(implicit ctx: Context) : OrderingConstraint = {
2828
val result = new OrderingConstraint(boundsMap, lowerMap, upperMap)
29-
if (Config.checkConstraintsNonCyclic) result.checkNonCyclic()
3029
ctx.run.recordConstraintSize(result, result.boundsMap.size)
3130
result
3231
}
@@ -179,7 +178,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
179178

180179
def minUpper(param: TypeParamRef): List[TypeParamRef] = {
181180
val all = upper(param)
182-
all.filterNot(p => all.exists(isLess(_, p)))
181+
all//.filterNot(p => all.exists(isLess(_, p)))
183182
}
184183

185184
def exclusiveLower(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] =
@@ -215,8 +214,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
215214
def dependentParams(tp: Type, isUpper: Boolean): List[TypeParamRef] = tp match {
216215
case param: TypeParamRef if contains(param) =>
217216
param :: (if (isUpper) upper(param) else lower(param))
218-
case tp: AndType => dependentParams(tp.tp1, isUpper) | (dependentParams(tp.tp2, isUpper))
219-
case tp: OrType => dependentParams(tp.tp1, isUpper).intersect(dependentParams(tp.tp2, isUpper))
217+
case tp: AndType if isUpper => dependentParams(tp.tp1, isUpper) | (dependentParams(tp.tp2, isUpper))
218+
case tp: OrType if !isUpper => dependentParams(tp.tp1, isUpper).intersect(dependentParams(tp.tp2, isUpper))
220219
case _ =>
221220
Nil
222221
}
@@ -307,8 +306,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
307306
hiBuf.clear()
308307
i += 1
309308
}
310-
if (Config.checkConstraintsNonCyclic) checkNonCyclic()
311-
current
309+
current.checkNonCyclic()
312310
}
313311

314312
// ---------- Updates ------------------------------------------------------------
@@ -329,9 +327,40 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
329327
}
330328

331329
def addLess(param1: TypeParamRef, param2: TypeParamRef)(implicit ctx: Context): This =
332-
order(this, param1, param2)
330+
order(this, param1, param2).checkNonCyclic()
331+
332+
private def ensureNonCyclic(param: TypeParamRef, inst: Type)(using Context): Type =
333+
334+
def recur(tp: Type, fromBelow: Boolean): Type = tp match
335+
case tp: AndOrType =>
336+
val r1 = recur(tp.tp1, fromBelow)
337+
val r2 = recur(tp.tp2, fromBelow)
338+
if (r1 eq tp.tp1) && (r2 eq tp.tp2) then tp
339+
else if tp.isAnd then r1 & r2
340+
else r1 | r2
341+
case tp: TypeParamRef =>
342+
if tp eq param then
343+
if fromBelow then defn.NothingType else defn.AnyType
344+
else entry(tp) match
345+
case NoType => tp
346+
case TypeBounds(lo, hi) => if lo eq hi then recur(lo, fromBelow) else tp
347+
case inst => recur(inst, fromBelow)
348+
case tp: TypeVar if false => // TODO: needed?
349+
val underlying1 = recur(tp.underlying, fromBelow)
350+
if underlying1 ne tp.underlying then underlying1 else tp
351+
case _ => tp
352+
353+
inst match
354+
case bounds: TypeBounds =>
355+
bounds.derivedTypeBounds(
356+
recur(bounds.lo, fromBelow = true),
357+
recur(bounds.hi, fromBelow = false))
358+
case _ =>
359+
inst
360+
end ensureNonCyclic
333361

334-
def updateEntry(current: This, param: TypeParamRef, tp: Type)(implicit ctx: Context): This = {
362+
private def updateEntry(current: This, param: TypeParamRef, tp: Type)(implicit ctx: Context): This = {
363+
val tp0 = tp
335364
var current1 = boundsLens.update(this, current, param, tp)
336365
tp match {
337366
case TypeBounds(lo, hi) =>
@@ -345,11 +374,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
345374
}
346375

347376
def updateEntry(param: TypeParamRef, tp: Type)(implicit ctx: Context): This =
348-
updateEntry(this, param, tp)
377+
updateEntry(this, param, tp).checkNonCyclic()
349378

350379
def unify(p1: TypeParamRef, p2: TypeParamRef)(implicit ctx: Context): This = {
351-
val p1Bounds = (nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1)
352-
updateEntry(p1, p1Bounds).replace(p2, p1)
380+
val p1Bounds = ensureNonCyclic(p1,
381+
(nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1))
382+
updateEntry(this, p1, p1Bounds).replace(p2, p1)
353383
}
354384

355385
// ---------- Removals ------------------------------------------------------------
@@ -390,9 +420,24 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
390420
def removeParam(ps: List[TypeParamRef]) =
391421
ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx)
392422

423+
var current =
424+
if isRemovable(poly) then remove(poly) else updateEntry(this, param, replacement)
425+
val current0 = current
426+
427+
var avoided = List[(Type, Type)]()
428+
393429
def replaceParam(tp: Type, atPoly: TypeLambda, atIdx: Int): Type = tp match {
394430
case bounds @ TypeBounds(lo, hi) =>
395431

432+
/*
433+
tp.substParam(param, replacement) match
434+
case bounds: TypeBounds =>
435+
val finalized = current.avoidCycles(atPoly.paramRefs(atIdx), bounds)
436+
if finalized ne bounds then avoided = ((bounds, finalized)) :: avoided
437+
finalized
438+
case tp => tp
439+
current.avoidCycles() */
440+
396441
def recombineAnd(and: AndType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = {
397442
val tp1 = op(and.tp1, isUpper)
398443
val tp2 = op(and.tp2, isUpper)
@@ -422,19 +467,26 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
422467
case _ => tp.substParam(param, replacement)
423468
}
424469

425-
bounds.derivedTypeBounds(replaceIn(lo, isUpper = false), replaceIn(hi, isUpper = true))
470+
val replaced = bounds.derivedTypeBounds(replaceIn(lo, isUpper = false), replaceIn(hi, isUpper = true))
471+
val finalized = current.ensureNonCyclic(atPoly.paramRefs(atIdx), replaced)
472+
// bounds.derivedTypeBounds(replaceIn(lo, isUpper = false), replaceIn(hi, isUpper = true)))
473+
if finalized ne replaced then avoided = ((replaced, finalized)) :: avoided
474+
finalized
426475
case _ =>
427476
tp.substParam(param, replacement)
428477
}
429478

430-
var current =
431-
if (isRemovable(poly)) remove(poly) else updateEntry(param, replacement)
432-
current.foreachParam {(p, i) =>
433-
current = boundsLens.map(this, current, p, i, replaceParam(_, p, i))
434-
current = lowerLens.map(this, current, p, i, removeParam)
435-
current = upperLens.map(this, current, p, i, removeParam)
436-
}
437-
current
479+
try current.foreachParam { (p, i) =>
480+
current = boundsLens.map(this, current, p, i, replaceParam(_, p, i))
481+
current = lowerLens.map(this, current, p, i, removeParam)
482+
current = upperLens.map(this, current, p, i, removeParam)
483+
}
484+
catch case ex: AssertionError =>
485+
println(i"FAILED wen replacing $param := $tp in $this, initial = $current0")
486+
for (f, t) <- avoided do
487+
println(i"avoided $f --> $t")
488+
throw ex
489+
current.checkNonCyclic()
438490
}
439491
}
440492

@@ -447,6 +499,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
447499
po.remove(pt).mapValuesNow(removeFromBoundss)
448500
}
449501
newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap))
502+
.checkNonCyclic()
450503
}
451504

452505
def isRemovable(pt: TypeLambda): Boolean = {
@@ -566,7 +619,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
566619
if (binder eq tl) tvar.setOrigin(tl1.paramRefs(n))
567620
}
568621
constr.println(i"renamd $this to $current")
569-
current
622+
current.checkNonCyclic()
570623
}
571624

572625
def ensureFresh(tl: TypeLambda)(implicit ctx: Context): TypeLambda =
@@ -614,11 +667,30 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
614667

615668
// ---------- Cyclic checking -------------------------------------------
616669

617-
def checkNonCyclic()(implicit ctx: Context): Unit =
618-
domainParams.foreach(checkNonCyclic)
670+
def checkNonCyclic()(implicit ctx: Context): this.type =
671+
if Config.checkConstraintsNonCyclic then domainParams.foreach(checkNonCyclic)
672+
this
619673

620674
private def checkNonCyclic(param: TypeParamRef)(implicit ctx: Context): Unit =
621-
assert(!isLess(param, param), i"cyclic constraint involving $param in $this")
675+
assert(!isLess(param, param), i"cyclic ordering involving $param in $this, upper = ${upper(param)}")
676+
677+
def recur(tp: Type)(using Context): Unit = tp match
678+
case tp: AndOrType =>
679+
recur(tp.tp1)
680+
recur(tp.tp2)
681+
case tp: TypeParamRef =>
682+
assert(tp ne param, i"cyclic bound for $param: ${entry(param)} in $this")
683+
entry(tp) match
684+
case NoType =>
685+
case TypeBounds(lo, hi) => if lo eq hi then recur(lo)
686+
case inst => recur(inst)
687+
case TypeBounds(lo, hi) =>
688+
recur(lo)
689+
recur(hi)
690+
case _ =>
691+
692+
recur(entry(param))
693+
end checkNonCyclic
622694

623695
// ---------- Invalidation -------------------------------------------
624696

@@ -631,6 +703,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
631703
// ---------- toText -----------------------------------------------------
632704

633705
override def toText(printer: Printer): Text = {
706+
//Printer.debugPrintUnique = true
634707
def entryText(tp: Type) = tp match {
635708
case tp: TypeBounds =>
636709
tp.toText(printer)
@@ -663,6 +736,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
663736
Text(ups.map(_.toText(printer)), ", ")
664737
Text(deps, "\n")
665738
}
739+
//Printer.debugPrintUnique = false
666740
Text.lines(List(header, uninstVarsText, constrainedText, boundsText, orderingText, ")"))
667741
}
668742

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
2626

2727
protected def maxToTextRecursions: Int = 100
2828

29+
protected def showUniqueIds = ctx.settings.uniqid.value || Printer.debugPrintUnique
30+
2931
protected final def limiter: MessageLimiter = ctx.property(MessageLimiter).get
3032

3133
protected def controlled(op: => Text): Text = limiter.controlled(op)
@@ -248,14 +250,14 @@ class PlainPrinter(_ctx: Context) extends Printer {
248250

249251
/** If -uniqid is set, the hashcode of the lambda type, after a # */
250252
protected def lambdaHash(pt: LambdaType): Text =
251-
if (ctx.settings.uniqid.value)
253+
if (showUniqueIds)
252254
try "#" + pt.hashCode
253255
catch { case ex: NullPointerException => "" }
254256
else ""
255257

256258
/** If -uniqid is set, the unique id of symbol, after a # */
257259
protected def idString(sym: Symbol): String =
258-
if (ctx.settings.uniqid.value || Printer.debugPrintUnique) "#" + sym.id else ""
260+
if (showUniqueIds || Printer.debugPrintUnique) "#" + sym.id else ""
259261

260262
def nameString(sym: Symbol): String =
261263
simpleNameString(sym) + idString(sym) // + "<" + (if (sym.exists) sym.owner else "") + ">"

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
213213
case tp: RefinedType if defn.isFunctionType(tp) && !printDebug =>
214214
toTextDependentFunction(tp.refinedInfo.asInstanceOf[MethodType])
215215
case tp: TypeRef =>
216-
if (tp.symbol.isAnonymousClass && !ctx.settings.uniqid.value)
216+
if (tp.symbol.isAnonymousClass && !showUniqueIds)
217217
toText(tp.info)
218218
else if (tp.symbol.is(Param))
219219
tp.prefix match {
@@ -729,7 +729,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
729729
protected def optAscription[T >: Untyped](tpt: Tree[T]): Text = optText(tpt)(": " ~ _)
730730

731731
private def idText(tree: untpd.Tree): Text =
732-
if ((ctx.settings.uniqid.value || Printer.debugPrintUnique) && tree.hasType && tree.symbol.exists) s"#${tree.symbol.id}" else ""
732+
if showUniqueIds && tree.hasType && tree.symbol.exists then s"#${tree.symbol.id}" else ""
733733

734734
private def useSymbol(tree: untpd.Tree) =
735735
tree.hasType && tree.symbol.exists && ctx.settings.YprintSyms.value

0 commit comments

Comments
 (0)