Skip to content

Commit 18b574a

Browse files
committed
Add comments and some simplifications
1 parent d866916 commit 18b574a

File tree

6 files changed

+131
-115
lines changed

6 files changed

+131
-115
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ object Config {
189189
/** If set, prints a trace of all symbol completions */
190190
inline val showCompletions = false
191191

192-
/** If set, show variable/variable reverse dependencoes when printing constraints. */
192+
/** If set, show variable/variable reverse dependencies when printing constraints. */
193193
inline val showConstraintDeps = true
194194

195195
/** If set, method results that are context functions are flattened by adding

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

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,9 @@ abstract class Constraint extends Showable {
172172
*/
173173
def occursAtToplevel(param: TypeParamRef, tp: Type)(using Context): Boolean
174174

175-
/** A map that associates type parameters of this constraint with all other type
176-
* parameters that refer to them in their bounds covariantly, such that, if the
177-
* type parameter is instantiated to a larger type, the constraint would be narrowed.
175+
/** A string that shows the reverse dependencies maintained by this constraint
176+
* (coDeps and contraDeps for OrderingConstraints).
178177
*/
179-
def coDeps: Constraint.ReverseDeps
180-
181-
/** A map that associates type parameters of this constraint with all other type
182-
* parameters that refer to them in their bounds covariantly, such that, if the
183-
* type parameter is instantiated to a smaller type, the constraint would be narrowed.
184-
*/
185-
def contraDeps: Constraint.ReverseDeps
186-
187-
/** A string showing the `coDeps` and `contraDeps` maps */
188178
def depsToString(using Context): String
189179

190180
/** Does the constraint restricted to variables outside `except` depend on `tv`
@@ -194,7 +184,12 @@ abstract class Constraint extends Showable {
194184
*/
195185
def dependsOn(tv: TypeVar, except: TypeVars, co: Boolean)(using Context): Boolean
196186

197-
/** Check that no constrained parameter contains itself as a bound */
187+
/** Depending on Config settngs:
188+
* - Under `checkConstraintsNonCyclic`, check that no constrained
189+
* parameter contains itself as a bound.
190+
* - Under `checkConstraintDeps`, check hat reverse dependencies in
191+
* constraints are correct and complete.
192+
*/
198193
def checkWellFormed()(using Context): this.type
199194

200195
/** Check that constraint only refers to TypeParamRefs bound by itself */
@@ -206,9 +201,6 @@ abstract class Constraint extends Showable {
206201
def checkConsistentVars()(using Context): Unit
207202
}
208203

209-
object Constraint:
210-
type ReverseDeps = SimpleIdentityMap[TypeParamRef, SimpleIdentitySet[TypeParamRef]]
211-
212204
/** When calling `Constraint#addLess(p1, p2, ...)`, the caller might end up
213205
* unifying one parameter with the other, this enum lets `addLess` know which
214206
* direction the unification will take.

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

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import printing.Texts._
1010
import config.Config
1111
import config.Printers.constr
1212
import reflect.ClassTag
13-
import Constraint.ReverseDeps
14-
import Substituters.SubstParamMap
1513
import annotation.tailrec
1614
import annotation.internal.sharable
1715
import cc.{CapturingType, derivedCapturingType}
@@ -25,20 +23,27 @@ object OrderingConstraint {
2523
* same P is added as a bound variable to the constraint, a backwards link would
2624
* then become necessary at this point but is missing. This causes two CB projects
2725
* to fail when reverse dependencies are checked (parboiled2 and perspective).
28-
* In these rare cases `replace` would behave differently when optimized.
26+
* In these rare cases `replace` could behave differently when optimized. However,
27+
* no deviation was found in the two projects. It is not clear what the "right"
28+
* behavior of `replace` should be in these cases. Normally, PolyTypes added
29+
* to constraints are supposed to be fresh, so that would mean that the behavior
30+
* with optimizeReplace = true would be correct. But the previous behavior without
31+
* reverse dependency checking corresponds to `optimizeReplace = false`. This behavior
32+
* makes sense if we assume that the added polytype was simply added too late, so we
33+
* want to establish the link between newly bound variable and pre-existing reference.
2934
*/
30-
final val optimizeReplace = true
35+
private final val optimizeReplace = true
3136

32-
type ArrayValuedMap[T] = SimpleIdentityMap[TypeLambda, Array[T]]
37+
private type ArrayValuedMap[T] = SimpleIdentityMap[TypeLambda, Array[T]]
3338

3439
/** The type of `OrderingConstraint#boundsMap` */
35-
type ParamBounds = ArrayValuedMap[Type]
40+
private type ParamBounds = ArrayValuedMap[Type]
3641

3742
/** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */
38-
type ParamOrdering = ArrayValuedMap[List[TypeParamRef]]
43+
private type ParamOrdering = ArrayValuedMap[List[TypeParamRef]]
3944

4045
/** A lens for updating a single entry array in one of the three constraint maps */
41-
abstract class ConstraintLens[T <: AnyRef: ClassTag] {
46+
private abstract class ConstraintLens[T <: AnyRef: ClassTag] {
4247
def entries(c: OrderingConstraint, poly: TypeLambda): Array[T] | Null
4348
def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[T])(using Context): OrderingConstraint
4449
def initial: T
@@ -91,23 +96,23 @@ object OrderingConstraint {
9196
map(prev, current, param.binder, param.paramNum, f)
9297
}
9398

94-
val boundsLens: ConstraintLens[Type] = new ConstraintLens[Type] {
99+
private val boundsLens: ConstraintLens[Type] = new ConstraintLens[Type] {
95100
def entries(c: OrderingConstraint, poly: TypeLambda): Array[Type] | Null =
96101
c.boundsMap(poly)
97102
def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[Type])(using Context): OrderingConstraint =
98103
c.newConstraint(boundsMap = c.boundsMap.updated(poly, entries))
99104
def initial = NoType
100105
}
101106

102-
val lowerLens: ConstraintLens[List[TypeParamRef]] = new ConstraintLens[List[TypeParamRef]] {
107+
private val lowerLens: ConstraintLens[List[TypeParamRef]] = new ConstraintLens[List[TypeParamRef]] {
103108
def entries(c: OrderingConstraint, poly: TypeLambda): Array[List[TypeParamRef]] | Null =
104109
c.lowerMap(poly)
105110
def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[List[TypeParamRef]])(using Context): OrderingConstraint =
106111
c.newConstraint(lowerMap = c.lowerMap.updated(poly, entries))
107112
def initial = Nil
108113
}
109114

110-
val upperLens: ConstraintLens[List[TypeParamRef]] = new ConstraintLens[List[TypeParamRef]] {
115+
private val upperLens: ConstraintLens[List[TypeParamRef]] = new ConstraintLens[List[TypeParamRef]] {
111116
def entries(c: OrderingConstraint, poly: TypeLambda): Array[List[TypeParamRef]] | Null =
112117
c.upperMap(poly)
113118
def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[List[TypeParamRef]])(using Context): OrderingConstraint =
@@ -237,13 +242,30 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
237242

238243
// ------------- Type parameter dependencies ----------------------------------------
239244

240-
var coDeps, contraDeps: ReverseDeps = SimpleIdentityMap.empty
245+
private type ReverseDeps = SimpleIdentityMap[TypeParamRef, SimpleIdentitySet[TypeParamRef]]
241246

242-
extension (deps: ReverseDeps) def at (param: TypeParamRef): SimpleIdentitySet[TypeParamRef] =
247+
/** A map that associates type parameters of this constraint with all other type
248+
* parameters that refer to them in their bounds covariantly, such that, if the
249+
* type parameter is instantiated to a larger type, the constraint would be narrowed
250+
* (i.e. solution set changes other than simply being made larger).
251+
*/
252+
private var coDeps: ReverseDeps = SimpleIdentityMap.empty
253+
254+
/** A map that associates type parameters of this constraint with all other type
255+
* parameters that refer to them in their bounds covariantly, such that, if the
256+
* type parameter is instantiated to a smaller type, the constraint would be narrowed.
257+
* (i.e. solution set changes other than simply being made larger).
258+
*/
259+
private var contraDeps: ReverseDeps = SimpleIdentityMap.empty
260+
261+
/** Null-safe indexing */
262+
extension (deps: ReverseDeps) def at(param: TypeParamRef): SimpleIdentitySet[TypeParamRef] =
243263
val result = deps(param)
244-
if null == result then SimpleIdentitySet.empty else result
264+
if null == result // swapped operand order important since `==` is overloaded in `SimpleIdentitySet`
265+
then SimpleIdentitySet.empty
266+
else result
245267

246-
def dependsOn(tv: TypeVar, except: TypeVars, co: Boolean)(using Context): Boolean =
268+
override def dependsOn(tv: TypeVar, except: TypeVars, co: Boolean)(using Context): Boolean =
247269
def origin(tv: TypeVar) =
248270
assert(!tv.isInstantiated)
249271
tv.origin
@@ -253,7 +275,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
253275
def test(deps: ReverseDeps, lens: ConstraintLens[List[TypeParamRef]]) =
254276
deps.at(param).exists(qualifies)
255277
|| lens(this, tv.origin.binder, tv.origin.paramNum).exists(qualifies)
256-
//.showing(i"outer depends on $tv with ${tvdeps.toList}%, % = $result")
257278
if co then test(coDeps, upperLens) else test(contraDeps, lowerLens)
258279

259280
/** Modify traversals in two respects:
@@ -271,10 +292,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
271292
*
272293
* - When typing a prefx, don't avoid negative variances. This matters only for the
273294
* corner case where a parameter is instantiated to Nothing (see comment in
274-
* TypeAccumulator#applyToPrefix). When determining instantiation directions
275-
* (which is what dependency variances are for), it can be ignored.
295+
* TypeAccumulator#applyToPrefix). When determining instantiation directions in
296+
* interpolations (which is what dependency variances are for), it can be ignored.
276297
*/
277298
private trait ConstraintAwareTraversal[T] extends TypeAccumulator[T]:
299+
278300
override def tyconTypeParams(tp: AppliedType)(using Context): List[ParamInfo] =
279301
def tparams(tycon: Type): List[ParamInfo] = tycon match
280302
case tycon: TypeVar if !tycon.isInstantiated => tparams(tycon.origin)
@@ -285,14 +307,16 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
285307
case _ => tp.tyconTypeParams
286308
case _ => tp.tyconTypeParams
287309
tparams(tp.tycon)
310+
288311
override def applyToPrefix(x: T, tp: NamedType): T =
289312
this(x, tp.prefix)
313+
end ConstraintAwareTraversal
290314

291315
private class Adjuster(srcParam: TypeParamRef)(using Context)
292316
extends TypeTraverser, ConstraintAwareTraversal[Unit]:
293317

294318
var add: Boolean = compiletime.uninitialized
295-
val seen = util.HashSet[LazyRef]()
319+
private val seen = util.HashSet[LazyRef]()
296320

297321
def update(deps: ReverseDeps, referenced: TypeParamRef): ReverseDeps =
298322
val prev = deps.at(referenced)
@@ -316,7 +340,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
316340
end Adjuster
317341

318342
/** Adjust dependencies to account for the delta of previous entry `prevEntry`
319-
* and new bound `entry` for the type parameter `srcParam`.
343+
* and the new bound `entry` for the type parameter `srcParam`.
320344
*/
321345
def adjustDeps(entry: Type | Null, prevEntry: Type | Null, srcParam: TypeParamRef)(using Context): this.type =
322346
val adjuster = new Adjuster(srcParam)
@@ -332,8 +356,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
332356

333357
/** Use an optimized strategy to adjust dependencies to account for the delta
334358
* of previous bound `prevBound` and new bound `bound`: If `prevBound` is some
335-
* and/or prefix of `bound`, just add the new parts of `bound`.
359+
* and/or prefix of `bound`, and `baseCase` is true, just add the new parts of `bound`.
336360
* @param isLower `bound` and `prevBound` are lower bounds
361+
* @return true iff the delta strategy succeeded, false if it failed in which case
362+
* the constraint is left unchanged.
337363
*/
338364
def adjustDelta(bound: Type, prevBound: Type, isLower: Boolean, baseCase: => Boolean): Boolean =
339365
if bound eq prevBound then
@@ -346,9 +372,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
346372
}
347373
case _ => false
348374

349-
/** Adjust dependencies to account for the delta of previous bounds `prevBounds`
350-
* and new bounds `bounds`.
351-
* @param add true if the bounds are added, false if they are removed
375+
/** Add or remove depenencies referenced in `bounds`.
376+
* @param add if true, dependecies are added, otherwise they are removed
352377
*/
353378
def adjustBounds(bounds: TypeBounds, add: Boolean) =
354379
adjustReferenced(bounds.lo, isLower = true, add)
@@ -370,7 +395,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
370395
case prevEntry: TypeBounds =>
371396
adjustBounds(prevEntry, add = false)
372397
case _ =>
373-
dropDeps(srcParam)
398+
dropDeps(srcParam) // srcParam is instantiated, so its dependencies can be dropped
374399
this
375400
end adjustDeps
376401

@@ -390,7 +415,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
390415
coDeps = coDeps.remove(param)
391416
contraDeps = contraDeps.remove(param)
392417

393-
/** A string representing the two depenecy maps */
418+
/** A string representing the two dependency maps */
394419
def depsToString(using Context): String =
395420
def depsStr(deps: ReverseDeps): String =
396421
def depStr(param: TypeParamRef) = i"$param --> ${deps.at(param).toList}%, %"
@@ -457,7 +482,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
457482
}
458483

459484
def add(poly: TypeLambda, tvars: List[TypeVar])(using Context): This = {
460-
checkWellFormed() // TODO: drop
461485
assert(!contains(poly))
462486
val nparams = poly.paramNames.length
463487
val entries1 = new Array[Type](nparams * 2)
@@ -609,13 +633,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
609633
case _ =>
610634
Nil
611635

612-
private def updateEntryNoOrdering(current: This, param: TypeParamRef, newEntry: Type, oldEntry: Type)(using Context): This =
613-
boundsLens.update(this, current, param, newEntry).adjustDeps(newEntry, oldEntry, param)
614-
615636
private def updateEntry(current: This, param: TypeParamRef, newEntry: Type)(using Context): This = {
616-
//println(i"update $param to $newEntry in $current")
617637
if Config.checkNoWildcardsInConstraint then assert(!newEntry.containsWildcardTypes)
618-
var current1 = updateEntryNoOrdering(current, param, newEntry, current.entry(param))
638+
val oldEntry = current.entry(param)
639+
var current1 = boundsLens.update(this, current, param, newEntry)
640+
.adjustDeps(newEntry, oldEntry, param)
619641
newEntry match {
620642
case TypeBounds(lo, hi) =>
621643
for p <- dependentParams(lo, isUpper = false) do
@@ -647,7 +669,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
647669
assert(replacement.isValueTypeOrLambda)
648670

649671
val replacedTypeVar = typeVarOfParam(param)
650-
//println(i"replace $param, $replacedTypeVar with $replacement in $this")
672+
//println(i"replace $param with $replacement in $this")
651673

652674
def mapReplacedTypeVarTo(to: Type) = new TypeMap:
653675
override def apply(t: Type): Type =
@@ -673,13 +695,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
673695
case tvar: TypeVar =>
674696
if tvar.isInstantiated
675697
then
676-
// replace is called from TypeVar's instantiateWith,
677-
// forget about instantiation for old dependencies
698+
// That's the case if replace is called from TypeVar's instantiateWith.
699+
// Forget about instantiation for old dependencies.
678700
oldDepEntry = mapReplacedTypeVarTo(param)(oldDepEntry)
679701
else
680-
// replace is called from unify,
681-
// assume parameter has been replaced for new dependencies
682-
// (the actual replacement is done below)
702+
// That's the case if replace is called from unify.
703+
// Assume parameter has been replaced for new dependencies
704+
// (the actual replacement is done below).
683705
newDepEntry = mapReplacedTypeVarTo(replacement)(newDepEntry)
684706
case _ =>
685707
if oldDepEntry ne newDepEntry then
@@ -864,9 +886,6 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
864886

865887
// ---------- Checking -----------------------------------------------
866888

867-
/** Depending on Config settngs, check that there are no cycles and that
868-
* reverse depenecies are correct.
869-
*/
870889
def checkWellFormed()(using Context): this.type =
871890

872891
/** Check that each dependency A -> B in coDeps and contraDeps corresponds to

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ object Substituters:
193193
def apply(tp: Type): Type = substRecThis(tp, from, to, this)(using mapCtx)
194194
}
195195

196-
class SubstParamMap(from: ParamRef, to: Type)(using Context) extends DeepTypeMap, IdempotentCaptRefMap {
196+
final class SubstParamMap(from: ParamRef, to: Type)(using Context) extends DeepTypeMap, IdempotentCaptRefMap {
197197
def apply(tp: Type): Type = substParam(tp, from, to, this)(using mapCtx)
198198
}
199199

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5497,6 +5497,11 @@ object Types {
54975497
|| stop == StopAt.Package && tp.currentSymbol.is(Package)
54985498
}
54995499

5500+
/** The type parameters of the constructor of this applied type.
5501+
* Overridden in OrderingConstraint's ConstraintAwareTraversal to take account
5502+
* of instantiations in the constraint that are not yet propagated to the
5503+
* instance types of type variables.
5504+
*/
55005505
protected def tyconTypeParams(tp: AppliedType)(using Context): List[ParamInfo] =
55015506
tp.tyconTypeParams
55025507
end VariantTraversal
@@ -6065,11 +6070,11 @@ object Types {
60656070
* By contrast, covariance does translate to the prefix, since we have that
60666071
* if `p <: q` then `p.A <: q.A`, and well-formedness requires that `A` is a member
60676072
* of `p`'s upper bound.
6068-
* Overridden in traversers that compute or check reverse dependencies in OrderingConstraint,
6069-
* where we use a more relaxed scheme.
6073+
* Overridden in OrderingConstraint's ConstraintAwareTraversal, where a
6074+
* more relaxed scheme is used.
60706075
*/
60716076
protected def applyToPrefix(x: T, tp: NamedType): T =
6072-
atVariance(variance max 0)(this(x, tp.prefix)) // see remark on NamedType case in TypeMap
6077+
atVariance(variance max 0)(this(x, tp.prefix))
60736078

60746079
def foldOver(x: T, tp: Type): T = {
60756080
record(s"foldOver $getClass")

0 commit comments

Comments
 (0)