Skip to content

Commit 3fa9819

Browse files
committed
Refinement of dependent parameter handling in OrderingConstraint
Previous code did not recognize some cases of dependent parameters. E.g P1 <: P2 | P3 P2 <: P4 P3 <: P4 In that case it follows that P1 <: P4 but that was not recorded in P's upper set.
1 parent 2514c3b commit 3fa9819

File tree

1 file changed

+59
-8
lines changed

1 file changed

+59
-8
lines changed

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

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ import OrderingConstraint._
113113
* polytype's type parameters. The second half might contain type variables that
114114
* track the corresponding parameters, or is left empty (filled with nulls).
115115
* An instantiated type parameter is represented by having its instance type in
116-
* the corresponding array entry.
116+
* the corresponding array entry. The dual use of arrays for poly params
117+
* and typevars is to save space and hopefully gain some speed.
117118
*
118119
* @param lowerMap a map from PolyTypes to arrays. Each array entry corresponds
119120
* to a parameter P of the polytype; it contains all constrained parameters
@@ -212,9 +213,46 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
212213

213214
// ---------- Adding PolyTypes --------------------------------------------------
214215

215-
/** The bound type `tp` without dependent parameters
216-
* NoType if type consists only of dependent parameters.
217-
* @param seenFromBelow If true, `bound` is an upper bound, else a lower bound.
216+
/** The list of parameters P such that, for a fresh type parameter Q:
217+
*
218+
* Q <: tp implies Q <: P and isUpper = true, or
219+
* tp <: Q implies P <: Q and isUpper = false
220+
*/
221+
def dependentParams(tp: Type, isUpper: Boolean): List[PolyParam] = tp match {
222+
case param: PolyParam if contains(param) =>
223+
param :: (if (isUpper) upper(param) else lower(param))
224+
case tp: AndOrType =>
225+
val ps1 = dependentParams(tp.tp1, isUpper)
226+
val ps2 = dependentParams(tp.tp2, isUpper)
227+
if (isUpper == tp.isAnd) ps1.union(ps2) else ps1.intersect(ps2)
228+
case _ =>
229+
Nil
230+
}
231+
232+
/** The bound type `tp` without constrained parameters which are clearly
233+
* dependent. A parameter in an upper bound is clearly dependent if it appears
234+
* in a hole of a context H given by:
235+
*
236+
* H = []
237+
* H & T
238+
* T & H
239+
*
240+
* (the idea is that a parameter P in a H context is guaranteed to be a supertype of the
241+
* bounded parameter.)
242+
* Analogously, a parameter in a lower bound is clearly dependent if it appears
243+
* in a hole of a context H given by:
244+
*
245+
* L = []
246+
* L | T
247+
* T | L
248+
*
249+
* "Clearly dependent" is not synonymous with "dependent" in the sense
250+
* it is defined in `dependentParams`. Dependent parameters are handled
251+
* in `updateEntry`. The idea of stripping off clearly dependent parameters
252+
* and to handle them separately is for efficiency, so that type expressions
253+
* used as bounds become smaller.
254+
*
255+
* @param isUpper If true, `bound` is an upper bound, else a lower bound.
218256
*/
219257
private def stripParams(tp: Type, paramBuf: mutable.ListBuffer[PolyParam],
220258
isUpper: Boolean)(implicit ctx: Context): Type = tp match {
@@ -232,9 +270,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
232270
tp
233271
}
234272

235-
/** The bound type `tp` without dependent parameters.
273+
/** The bound type `tp` without clearly dependent parameters.
236274
* A top or bottom type if type consists only of dependent parameters.
237-
* @param seenFromBelow If true, `bound` is an upper bound, else a lower bound.
275+
* @param isUpper If true, `bound` is an upper bound, else a lower bound.
238276
*/
239277
private def normalizedType(tp: Type, paramBuf: mutable.ListBuffer[PolyParam],
240278
isUpper: Boolean)(implicit ctx: Context): Type =
@@ -259,7 +297,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
259297
val bounds = nonParamBounds(param)
260298
val lo = normalizedType(bounds.lo, loBuf, isUpper = false)
261299
val hi = normalizedType(bounds.hi, hiBuf, isUpper = true)
262-
current = boundsLens.update(this, current, poly, i, bounds.derivedTypeBounds(lo, hi))
300+
current = updateEntry(current, param, bounds.derivedTypeBounds(lo, hi))
263301
current = (current /: loBuf)(order(_, _, param))
264302
current = (current /: hiBuf)(order(_, param, _))
265303
loBuf.clear()
@@ -289,9 +327,22 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
289327

290328
def addLess(param1: PolyParam, param2: PolyParam)(implicit ctx: Context): This =
291329
order(this, param1, param2)
330+
331+
def updateEntry(current: This, param: PolyParam, tp: Type)(implicit ctx: Context): This = {
332+
var current1 = boundsLens.update(this, current, param, tp)
333+
tp match {
334+
case TypeBounds(lo, hi) =>
335+
for (p <- dependentParams(lo, isUpper = false))
336+
current1 = order(current1, p, param)
337+
for (p <- dependentParams(hi, isUpper = true))
338+
current1 = order(current1, param, p)
339+
case _ =>
340+
}
341+
current1
342+
}
292343

293344
def updateEntry(param: PolyParam, tp: Type)(implicit ctx: Context): This =
294-
boundsLens.update(this, this, param, tp)
345+
updateEntry(this, param, tp)
295346

296347
def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This = {
297348
val p1Bounds = (nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1)

0 commit comments

Comments
 (0)