@@ -10,6 +10,7 @@ import Flags._
10
10
import config .Config
11
11
import config .Printers .typr
12
12
import reporting .trace
13
+ import StdNames .tpnme
13
14
14
15
/** Methods for adding constraints and solving them.
15
16
*
@@ -243,48 +244,87 @@ trait ConstraintHandling {
243
244
* @return the instantiating type
244
245
* @pre `param` is in the constraint's domain.
245
246
*/
246
- final def approximation (param : TypeParamRef , fromBelow : Boolean )(using Context ): Type = {
247
- val replaceWildcards = new TypeMap {
247
+ 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 :
248
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
+
249
294
def apply (tp : Type ) = mapOver {
250
- tp match {
295
+ tp match
251
296
case tp : WildcardType =>
252
- val bounds = tp.optBounds.orElse(TypeBounds .empty).bounds
253
- // Try to instantiate the wildcard to a type that is known to conform to it.
254
- // This means:
255
- // If fromBelow is true, we minimize the type overall
256
- // Hence, if variance < 0, pick the maximal safe type: bounds.lo
257
- // (i.e. the whole bounds range is over the type)
258
- // if variance > 0, pick the minimal safe type: bounds.hi
259
- // (i.e. the whole bounds range is under the type)
260
- // if variance == 0, pick bounds.lo anyway (this is arbitrary but in line with
261
- // the principle that we pick the smaller type when in doubt).
262
- // If fromBelow is false, we maximize the type overall and reverse the bounds
263
- // if variance != 0. For variance == 0, we still minimize.
264
- // In summary we pick the bound given by this table:
265
- //
266
- // variance | -1 0 1
267
- // ------------------------
268
- // from below | lo lo hi
269
- // from above | hi lo lo
270
- //
271
- if (variance == 0 || fromBelow == (variance < 0 )) bounds.lo else bounds.hi
297
+ pickOneBound(tp.bounds)
298
+ case tp : TypeParamRef if substWildcards.trackedPolis.contains(tp.binder) =>
299
+ pickOneBound(fullBounds(tp))
272
300
case _ => tp
273
- }
274
301
}
275
- }
276
- constraint.entry(param) match {
302
+ end replaceWildcards
303
+
304
+ constraint.entry(param) match
277
305
case entry : TypeBounds =>
278
306
val useLowerBound = fromBelow || param.occursIn(entry.hi)
279
- val bound = if (useLowerBound) fullLowerBound(param) else fullUpperBound(param)
280
- val inst = replaceWildcards(bound)
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
281
322
typr.println(s " approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}" )
282
323
inst
283
324
case inst =>
284
325
assert(inst.exists, i " param = $param\n constraint = $constraint" )
285
326
inst
286
- }
287
- }
327
+ end approximation
288
328
289
329
/** If `tp` is an intersection such that some operands are transparent trait instances
290
330
* and others are not, replace as many transparent trait instances as possible with Any
0 commit comments