@@ -10,6 +10,7 @@ import Flags._
1010import config .Config
1111import config .Printers .typr
1212import reporting .trace
13+ import StdNames .tpnme
1314
1415/** Methods for adding constraints and solving them.
1516 *
@@ -243,48 +244,87 @@ trait ConstraintHandling {
243244 * @return the instantiating type
244245 * @pre `param` is in the constraint's domain.
245246 */
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 :
248269 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+
249294 def apply (tp : Type ) = mapOver {
250- tp match {
295+ tp match
251296 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))
272300 case _ => tp
273- }
274301 }
275- }
276- constraint.entry(param) match {
302+ end replaceWildcards
303+
304+ constraint.entry(param) match
277305 case entry : TypeBounds =>
278306 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
281322 typr.println(s " approx ${param.show}, from below = $fromBelow, bound = ${bound.show}, inst = ${inst.show}" )
282323 inst
283324 case inst =>
284325 assert(inst.exists, i " param = $param\n constraint = $constraint" )
285326 inst
286- }
287- }
327+ end approximation
288328
289329 /** If `tp` is an intersection such that some operands are transparent trait instances
290330 * and others are not, replace as many transparent trait instances as possible with Any
0 commit comments