@@ -10,6 +10,55 @@ import printing.Texts._
10
10
import config .Config
11
11
import config .Printers ._
12
12
13
+ object Constraint {
14
+
15
+ /** The type of `Constraint#myMap` */
16
+ type ParamInfo = SimpleMap [PolyType , Array [Type ]]
17
+
18
+ /** The type of `Constraint#dependents */
19
+ type DependentMap = SimpleMap [PolyType , Array [Set [PolyParam ]]]
20
+
21
+ /** The type of functions that include or exclude a `PolyParam` in or from a set*/
22
+ private type DepDelta = (Set [PolyParam ], PolyParam ) => Set [PolyParam ]
23
+
24
+ private val addDep : DepDelta = (_ + _)
25
+ private val removeDep : DepDelta = (_ - _)
26
+
27
+ private val NoTypeBounds = new TypeBounds (WildcardType , WildcardType ){}
28
+
29
+ /** An accumulator that changes dependencies on `param`.
30
+ * @param param The parameter to which changed dependencies refer.
31
+ * @param ofVariance Include `PolyParams` occurring at this variance in the dependencies.
32
+ * @param delta The dependency change to perform (add or remove).
33
+ */
34
+ private class ChangeDependencies (param : PolyParam , ofVariance : Int , delta : DepDelta )(implicit ctx : Context )
35
+ extends TypeAccumulator [DependentMap ] {
36
+ def apply (deps : DependentMap , tp : Type ): DependentMap = tp match {
37
+ case tp @ PolyParam (pt, n) if
38
+ this .variance == 0 || this .variance == ofVariance =>
39
+ val oldDeps = deps(pt)
40
+ val original = safeSelect(oldDeps, n)
41
+ val changed = delta(original, param)
42
+ if (original eq changed) deps
43
+ else {
44
+ val newDeps =
45
+ if (oldDeps == null ) new Array [Set [PolyParam ]](pt.paramBounds.length)
46
+ else oldDeps.clone
47
+ newDeps(n) = changed
48
+ deps.updated(pt, newDeps)
49
+ }
50
+ case _ => foldOver(deps, tp)
51
+ }
52
+ }
53
+
54
+ /** `deps(n)`, except that `Set()` is returned if `deps` or `deps(n)` are null */
55
+ private def safeSelect (deps : Array [Set [PolyParam ]], n : Int ) : Set [PolyParam ] =
56
+ if (deps == null || deps(n) == null ) Set ()
57
+ else deps(n)
58
+ }
59
+
60
+ import Constraint ._
61
+
13
62
/** Constraint over undetermined type parameters
14
63
* @param myMap a map from PolyType to arrays.
15
64
* Each array contains twice the number of entries as there a type parameters
@@ -18,8 +67,20 @@ import config.Printers._
18
67
* track the corresponding parameters, or is left empty (filled with nulls).
19
68
* An instantiated type parameter is represented by having its instance type in
20
69
* the corresponding array entry.
70
+ * @param dependents a map from PolyTypes to arrays of Sets of PolyParams.
71
+ * The i'th set in an array corresponding to polytype `pt` contains
72
+ * those dependent `PolyParam`s `dp` that have `PolyParam(pt, i)` in their bounds in
73
+ * significant position. A position is significant if solving the
74
+ * constraint for `(pt, i)` with a type higher than its lower bound
75
+ * would lead to a constraint for `dp` that was not looser than
76
+ * the existing constraint. Specifically, it means that all poly params
77
+ * appearing covariantly in the lower bound and contravariantly in the
78
+ * upper bound, as well as all poly params appearing nonvariantly are
79
+ * significant.
80
+ * The `dependents` map is maintained and queried only of `Config.trackConstrDeps` is set.
21
81
*/
22
- class Constraint (val myMap : SimpleMap [PolyType , Array [Type ]]) extends Showable {
82
+ class Constraint (private val myMap : ParamInfo ,
83
+ private val dependents : DependentMap ) extends Showable {
23
84
24
85
/** Does the constraint's domain contain the type parameters of `pt`? */
25
86
def contains (pt : PolyType ): Boolean = myMap(pt) != null
@@ -66,16 +127,88 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable {
66
127
def typeVarOfParam (param : PolyParam ): Type = {
67
128
val entries = myMap(param.binder)
68
129
if (entries == null ) NoType
69
- else typeVar(entries, param.paramNum)
130
+ else {
131
+ val tvar = typeVar(entries, param.paramNum)
132
+ if (tvar != null ) tvar else NoType
133
+ }
134
+ }
135
+
136
+ /** Change dependencies in map `deps` to reflect new parameter bounds.
137
+ * @param deps The map to change
138
+ * @param pt the polytype that contains the parameters which might have new bounds
139
+ * @param entries the entries for the parameters which might have new bounds
140
+ * @param delta the change operation, one of `addDep` or `removeDep`.
141
+ * @param cmpEntries the comparison entries or `null` if no such entries exist.
142
+ * As an optimization, only bounds that differ between `entries`
143
+ * and `cmpEntries` will record their dependencies.
144
+ */
145
+ def changeDependencies (deps : DependentMap , pt : PolyType , entries : Array [Type ], delta : DepDelta , cmpEntries : Array [Type ])(implicit ctx : Context ): DependentMap = {
146
+ val limit = paramCount(entries)
147
+ def loop (deps : DependentMap , n : Int ): DependentMap = {
148
+ if (n >= limit) deps
149
+ else {
150
+ val newDeps = entries(n) match {
151
+ case bounds @ TypeBounds (lo, hi) =>
152
+ val cmpBounds =
153
+ if (cmpEntries == null ) NoTypeBounds
154
+ else cmpEntries(n) match {
155
+ case bounds : TypeBounds => bounds
156
+ case _ => NoTypeBounds
157
+ }
158
+ if (cmpBounds eq bounds) deps
159
+ else {
160
+ val param = PolyParam (pt, n)
161
+ val deps1 =
162
+ if (cmpBounds.lo eq lo) deps
163
+ else new ChangeDependencies (param, 1 , delta).apply(deps, lo)
164
+ val deps2 =
165
+ if (cmpBounds.hi eq hi) deps1
166
+ else new ChangeDependencies (param, - 1 , delta).apply(deps1, hi)
167
+ deps2
168
+ }
169
+ case _ =>
170
+ deps
171
+ }
172
+ loop(newDeps, n + 1 )
173
+ }
174
+ }
175
+ if (Config .trackConstrDeps) loop(deps, 0 ) else deps
70
176
}
71
177
178
+ /** Change dependencies to reflect all changes between the bounds in `oldMap` and `newMap`.
179
+ */
180
+ def diffDependencies (deps : DependentMap , oldMap : ParamInfo , newMap : ParamInfo )(implicit ctx : Context ): DependentMap =
181
+ if (Config .trackConstrDeps) {
182
+ var d = deps
183
+ oldMap foreachBinding { (poly, entries) =>
184
+ val newEntries = newMap(poly)
185
+ if (newEntries ne entries) d = changeDependencies(d, poly, entries, removeDep, newEntries)
186
+ }
187
+ newMap foreachBinding { (poly, entries) =>
188
+ val oldEntries = oldMap(poly)
189
+ if (oldEntries ne entries) d = changeDependencies(d, poly, entries, addDep, oldEntries)
190
+ }
191
+ d
192
+ } else deps
193
+
194
+ /** The set of parameters that depend directly on `param`
195
+ * according to what's stored in `dependents`.
196
+ */
197
+ def dependentParams (param : PolyParam ): Set [PolyParam ] =
198
+ safeSelect(dependents(param.binder), param.paramNum)
199
+
72
200
/** A new constraint which is derived from this constraint by adding or replacing
73
201
* the entries corresponding to `pt` with `entries`.
74
202
*/
75
203
private def updateEntries (pt : PolyType , entries : Array [Type ])(implicit ctx : Context ) : Constraint = {
76
- val res = new Constraint (myMap.updated(pt, entries))
204
+ val res = new Constraint (
205
+ myMap.updated(pt, entries),
206
+ changeDependencies(dependents, pt, entries, addDep, myMap(pt)))
207
+
208
+ // assert(res.domainPolys.filter(pt =>
209
+ // pt.resultType.resultType.widen.classSymbol.name.toString == "Ensuring").length < 2) //DEBUG
77
210
if (Config .checkConstraintsNonCyclic) checkNonCyclic(pt, entries)
78
- ctx.runInfo.recordConstraintSize(res)
211
+ ctx.runInfo.recordConstraintSize(res, res.myMap.size )
79
212
res
80
213
}
81
214
@@ -114,7 +247,10 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable {
114
247
updateEntries(poly, myMap(poly) map op)
115
248
116
249
/** A new constraint with all entries coming from `pt` removed. */
117
- def remove (pt : PolyType ) = new Constraint (myMap remove pt)
250
+ def remove (pt : PolyType )(implicit ctx : Context ) =
251
+ new Constraint (
252
+ myMap remove pt,
253
+ changeDependencies(dependents, pt, myMap(pt), removeDep, null ))
118
254
119
255
/** Is entry associated with `pt` removable?
120
256
* @param removedParam The index of a parameter which is still present in the
@@ -185,7 +321,10 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable {
185
321
186
322
val pt = param.binder
187
323
val constr1 = if (isRemovable(pt, param.paramNum)) remove(pt) else updated(param, tp)
188
- val result = new Constraint (constr1.myMap mapValues subst)
324
+ val substMap = constr1.myMap mapValues subst
325
+ val result = new Constraint (
326
+ substMap,
327
+ diffDependencies(constr1.dependents, constr1.myMap, substMap))
189
328
if (Config .checkConstraintsNonCyclic) result.checkNonCyclic()
190
329
result
191
330
}
@@ -243,6 +382,16 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable {
243
382
if isBounds(entries(n))
244
383
} yield PolyParam (poly, n)
245
384
385
+ /** Check whether predicate holds for all parameters in constraint
386
+ */
387
+ def forallParams (p : PolyParam => Boolean ): Boolean = {
388
+ myMap.foreachBinding { (poly, entries) =>
389
+ for (i <- 0 until paramCount(entries))
390
+ if (isBounds(entries(i)) && ! p(PolyParam (poly, i))) return false
391
+ }
392
+ true
393
+ }
394
+
246
395
/** Perform operation `op` on all typevars, or only on uninstantiated
247
396
* typevars, depending on whether `uninstOnly` is set or not.
248
397
*/
@@ -299,9 +448,9 @@ class Constraint(val myMap: SimpleMap[PolyType, Array[Type]]) extends Showable {
299
448
trait ConstraintRunInfo { self : RunInfo =>
300
449
private var maxSize = 0
301
450
private var maxConstraint : Constraint = _
302
- def recordConstraintSize (c : Constraint ) =
303
- if (c.myMap. size > maxSize) {
304
- maxSize = c.myMap. size
451
+ def recordConstraintSize (c : Constraint , size : Int ) =
452
+ if (size > maxSize) {
453
+ maxSize = size
305
454
maxConstraint = c
306
455
}
307
456
def printMaxConstraint ()(implicit ctx : Context ) =
0 commit comments