@@ -10,6 +10,7 @@ import Flags._
10
10
import config .Config
11
11
import config .Printers .typr
12
12
import reporting .trace
13
+ import typer .ProtoTypes .newTypeVar
13
14
import StdNames .tpnme
14
15
15
16
/** Methods for adding constraints and solving them.
@@ -78,22 +79,29 @@ trait ConstraintHandling {
78
79
def fullBounds (param : TypeParamRef )(using Context ): TypeBounds =
79
80
nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param))
80
81
81
- protected def addOneBound (param : TypeParamRef , bound : Type , isUpper : Boolean )(using Context ): Boolean =
82
+ /** If true, eliminate wildcards in bounds by avoidance, otherwise replace
83
+ * them by fresh variables.
84
+ */
85
+ protected def approximateWildcards : Boolean = true
86
+
87
+ protected def addOneBound (param : TypeParamRef , rawBound : Type , isUpper : Boolean )(using Context ): Boolean =
82
88
if ! constraint.contains(param) then true
83
- else if ! isUpper && param.occursIn(bound ) then
89
+ else if ! isUpper && param.occursIn(rawBound ) then
84
90
// We don't allow recursive lower bounds when defining a type,
85
91
// so we shouldn't allow them as constraints either.
86
92
false
87
93
else
94
+ val dropWildcards = new AvoidWildcardsMap :
95
+ if ! isUpper then variance = - 1
96
+ override def mapWild (t : WildcardType ) =
97
+ if approximateWildcards then super .mapWild(t)
98
+ else newTypeVar(apply(t.effectiveBounds).toBounds)
99
+ val bound = dropWildcards(rawBound)
88
100
val oldBounds @ TypeBounds (lo, hi) = constraint.nonParamBounds(param)
89
101
val equalBounds = (if isUpper then lo else hi) eq bound
90
- if equalBounds
91
- && ! bound.existsPart(bp => bp.isInstanceOf [WildcardType ] || (bp eq param))
92
- then
93
- // The narrowed bounds are equal and do not contain wildcards,
102
+ if equalBounds && ! bound.existsPart(_ eq param, stopAtStatic = true ) then
103
+ // The narrowed bounds are equal and not recursive,
94
104
// so we can remove `param` from the constraint.
95
- // (Handling wildcards requires choosing a bound, but we don't know which
96
- // bound to choose here, this is handled in `ConstraintHandling#approximation`)
97
105
constraint = constraint.replace(param, bound)
98
106
true
99
107
else
@@ -245,81 +253,11 @@ trait ConstraintHandling {
245
253
* @pre `param` is in the constraint's domain.
246
254
*/
247
255
final def approximation (param : TypeParamRef , fromBelow : Boolean )(using Context ): Type =
248
-
249
- /** Substitute wildcards with fresh TypeParamRefs, to be compared with
250
- * other bound, so that they can be instantiated.
251
- */
252
- object substWildcards extends TypeMap :
253
- override def stopAtStatic = true
254
-
255
- var trackedPolis : List [PolyType ] = Nil
256
- def apply (tp : Type ) = tp match
257
- case tp : WildcardType =>
258
- val poly = PolyType (tpnme.EMPTY :: Nil )(pt => tp.bounds :: Nil , pt => defn.AnyType )
259
- trackedPolis = poly :: trackedPolis
260
- poly.paramRefs.head
261
- case _ =>
262
- mapOver(tp)
263
- end substWildcards
264
-
265
- /** Replace TypeParamRefs substituted for wildcards by `substWildCards`
266
- * and any remaining wildcards by a safe approximation
267
- */
268
- val replaceWildcards = new TypeMap :
269
- override def stopAtStatic = true
270
-
271
- /** Try to instantiate a wildcard or TypeParamRef representing a wildcard
272
- * to a type that is known to conform to it.
273
- * This means:
274
- * If fromBelow is true, we minimize the type overall
275
- * Hence, if variance < 0, pick the maximal safe type: bounds.lo
276
- * (i.e. the whole bounds range is over the type).
277
- * If variance > 0, pick the minimal safe type: bounds.hi
278
- * (i.e. the whole bounds range is under the type).
279
- * If variance == 0, pick bounds.lo anyway (this is arbitrary but in line with
280
- * the principle that we pick the smaller type when in doubt).
281
- * If fromBelow is false, we maximize the type overall and reverse the bounds
282
- * If variance != 0. For variance == 0, we still minimize.
283
- * In summary we pick the bound given by this table:
284
- *
285
- * variance | -1 0 1
286
- * ------------------------
287
- * from below | lo lo hi
288
- * from above | hi lo lo
289
- */
290
- def pickOneBound (bounds : TypeBounds ) =
291
- if variance == 0 || fromBelow == (variance < 0 ) then bounds.lo
292
- else bounds.hi
293
-
294
- def apply (tp : Type ) = mapOver {
295
- tp match
296
- case tp : WildcardType =>
297
- pickOneBound(tp.bounds)
298
- case tp : TypeParamRef if substWildcards.trackedPolis.contains(tp.binder) =>
299
- pickOneBound(fullBounds(tp))
300
- case _ => tp
301
- }
302
- end replaceWildcards
303
-
304
256
constraint.entry(param) match
305
257
case entry : TypeBounds =>
306
258
val useLowerBound = fromBelow || param.occursIn(entry.hi)
307
- val rawBound = if useLowerBound then fullLowerBound(param) else fullUpperBound(param)
308
- val bound = substWildcards(rawBound)
309
- val inst =
310
- if bound eq rawBound then bound
311
- else
312
- // Get rid of wildcards by mapping them to fresh TypeParamRefs
313
- // with constraints derived from comparing both bounds, and then
314
- // instantiating. See pos/i10161.scala for a test where this matters.
315
- val saved = constraint
316
- try
317
- for poly <- substWildcards.trackedPolis do addToConstraint(poly, Nil )
318
- if useLowerBound then bound <:< fullUpperBound(param)
319
- else fullLowerBound(param) <:< bound
320
- replaceWildcards(bound)
321
- finally constraint = saved
322
- typr.println(s " approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}" )
259
+ val inst = if useLowerBound then fullLowerBound(param) else fullUpperBound(param)
260
+ typr.println(s " approx ${param.show}, from below = $fromBelow, inst = ${inst.show}" )
323
261
inst
324
262
case inst =>
325
263
assert(inst.exists, i " param = $param\n constraint = $constraint" )
0 commit comments