@@ -10,6 +10,7 @@ import Flags._
1010import config .Config
1111import config .Printers .typr
1212import reporting .trace
13+ import typer .ProtoTypes .newTypeVar
1314import StdNames .tpnme
1415
1516/** Methods for adding constraints and solving them.
@@ -78,22 +79,29 @@ trait ConstraintHandling {
7879 def fullBounds (param : TypeParamRef )(using Context ): TypeBounds =
7980 nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param))
8081
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 =
8288 if ! constraint.contains(param) then true
83- else if ! isUpper && param.occursIn(bound ) then
89+ else if ! isUpper && param.occursIn(rawBound ) then
8490 // We don't allow recursive lower bounds when defining a type,
8591 // so we shouldn't allow them as constraints either.
8692 false
8793 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)
88100 val oldBounds @ TypeBounds (lo, hi) = constraint.nonParamBounds(param)
89101 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,
94104 // 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`)
97105 constraint = constraint.replace(param, bound)
98106 true
99107 else
@@ -245,81 +253,11 @@ trait ConstraintHandling {
245253 * @pre `param` is in the constraint's domain.
246254 */
247255 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-
304256 constraint.entry(param) match
305257 case entry : TypeBounds =>
306258 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}" )
323261 inst
324262 case inst =>
325263 assert(inst.exists, i " param = $param\n constraint = $constraint" )
0 commit comments