@@ -410,91 +410,109 @@ object TypeOps:
410410 }
411411 }
412412
413- /** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`.
413+ /** An approximating map that drops NamedTypes matching `toAvoid` and wildcard types. */
414+ abstract class AvoidMap (using Context ) extends AvoidWildcardsMap :
415+ @ threadUnsafe lazy val localParamRefs = util.HashSet [Type ]()
416+
417+ def toAvoid (tp : NamedType ): Boolean
418+
419+ /** True iff all NamedTypes on this prefix are static */
420+ override def isStaticPrefix (pre : Type )(using Context ): Boolean = pre match
421+ case pre : NamedType =>
422+ val sym = pre.currentSymbol
423+ sym.is(Package ) || sym.isStatic && isStaticPrefix(pre.prefix)
424+ case _ => true
425+
426+ override def apply (tp : Type ): Type = tp match
427+ case tp : TermRef
428+ if toAvoid(tp) =>
429+ tp.info.widenExpr.dealias match {
430+ case info : SingletonType => apply(info)
431+ case info => range(defn.NothingType , apply(info))
432+ }
433+ case tp : TypeRef if toAvoid(tp) =>
434+ tp.info match {
435+ case info : AliasingBounds =>
436+ apply(info.alias)
437+ case TypeBounds (lo, hi) =>
438+ range(atVariance(- variance)(apply(lo)), apply(hi))
439+ case info : ClassInfo =>
440+ range(defn.NothingType , apply(classBound(info)))
441+ case _ =>
442+ emptyRange // should happen only in error cases
443+ }
444+ case tp : ThisType =>
445+ // ThisType is only used inside a class.
446+ // Therefore, either they don't appear in the type to be avoided, or
447+ // it must be a class that encloses the block whose type is to be avoided.
448+ tp
449+ case tp : LazyRef =>
450+ if localParamRefs.contains(tp.ref) then tp
451+ else if isExpandingBounds then emptyRange
452+ else mapOver(tp)
453+ case tl : HKTypeLambda =>
454+ localParamRefs ++= tl.paramRefs
455+ mapOver(tl)
456+ case _ =>
457+ super .apply(tp)
458+ end apply
459+
460+ /** Three deviations from standard derivedSelect:
461+ * 1. We first try a widening conversion to the type's info with
462+ * the original prefix. Since the original prefix is known to
463+ * be a subtype of the returned prefix, this can improve results.
464+ * 2. Then, if the approximation result is a singleton reference C#x.type, we
465+ * replace by the widened type, which is usually more natural.
466+ * 3. Finally, we need to handle the case where the prefix type does not have a member
467+ * named `tp.name` anymmore. In that case, we need to fall back to Bot..Top.
468+ */
469+ override def derivedSelect (tp : NamedType , pre : Type ) =
470+ if (pre eq tp.prefix)
471+ tp
472+ else tryWiden(tp, tp.prefix).orElse {
473+ if (tp.isTerm && variance > 0 && ! pre.isSingleton)
474+ apply(tp.info.widenExpr)
475+ else if (upper(pre).member(tp.name).exists)
476+ super .derivedSelect(tp, pre)
477+ else
478+ range(defn.NothingType , defn.AnyType )
479+ }
480+ end AvoidMap
481+
482+ /** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`
483+ * and does not contain any WildcardType.
414484 * We need to approximate with ranges:
415485 *
416486 * term references to symbols in `symsToAvoid`,
417487 * term references that have a widened type of which some part refers
418488 * to a symbol in `symsToAvoid`,
419489 * type references to symbols in `symsToAvoid`,
420- * this types of classes in `symsToAvoid`.
421490 *
422491 * Type variables that would be interpolated to a type that
423492 * needs to be widened are replaced by the widened interpolation instance.
493+ *
494+ * TODO: Could we replace some or all usages of this method by
495+ * `LevelAvoidMap` instead? It would be good to investigate this in details
496+ * but when I tried it, avoidance for inlined trees broke because `TreeMap`
497+ * does not update `ctx.nestingLevel` when entering a block so I'm leaving
498+ * this as Future Work™.
424499 */
425500 def avoid (tp : Type , symsToAvoid : => List [Symbol ])(using Context ): Type = {
426- val widenMap = new ApproximatingTypeMap {
501+ val widenMap = new AvoidMap {
427502 @ threadUnsafe lazy val forbidden = symsToAvoid.toSet
428- @ threadUnsafe lazy val localParamRefs = util.HashSet [Type ]()
429- def toAvoid (sym : Symbol ) = ! sym.isStatic && forbidden.contains(sym)
430- def partsToAvoid = new NamedPartsAccumulator (tp => toAvoid(tp.symbol))
431-
432- /** True iff all NamedTypes on this prefix are static */
433- override def isStaticPrefix (pre : Type )(using Context ): Boolean = pre match
434- case pre : NamedType =>
435- val sym = pre.currentSymbol
436- sym.is(Package ) || sym.isStatic && isStaticPrefix(pre.prefix)
437- case _ => true
438-
439- def apply (tp : Type ): Type = tp match
440- case tp : TermRef
441- if toAvoid(tp.symbol) || partsToAvoid(Nil , tp.info).nonEmpty =>
442- tp.info.widenExpr.dealias match {
443- case info : SingletonType => apply(info)
444- case info => range(defn.NothingType , apply(info))
445- }
446- case tp : TypeRef if toAvoid(tp.symbol) =>
447- tp.info match {
448- case info : AliasingBounds =>
449- apply(info.alias)
450- case TypeBounds (lo, hi) =>
451- range(atVariance(- variance)(apply(lo)), apply(hi))
452- case info : ClassInfo =>
453- range(defn.NothingType , apply(classBound(info)))
454- case _ =>
455- emptyRange // should happen only in error cases
456- }
457- case tp : ThisType =>
458- // ThisType is only used inside a class.
459- // Therefore, either they don't appear in the type to be avoided, or
460- // it must be a class that encloses the block whose type is to be avoided.
461- tp
503+ def toAvoid (tp : NamedType ) =
504+ val sym = tp.symbol
505+ ! sym.isStatic && forbidden.contains(sym)
506+
507+ override def apply (tp : Type ): Type = tp match
462508 case tp : TypeVar if mapCtx.typerState.constraint.contains(tp) =>
463509 val lo = TypeComparer .instanceType(
464510 tp.origin, fromBelow = variance > 0 || variance == 0 && tp.hasLowerBound)(using mapCtx)
465511 val lo1 = apply(lo)
466512 if (lo1 ne lo) lo1 else tp
467- case tp : LazyRef =>
468- if localParamRefs.contains(tp.ref) then tp
469- else if isExpandingBounds then emptyRange
470- else mapOver(tp)
471- case tl : HKTypeLambda =>
472- localParamRefs ++= tl.paramRefs
473- mapOver(tl)
474513 case _ =>
475- mapOver (tp)
514+ super .apply (tp)
476515 end apply
477-
478- /** Three deviations from standard derivedSelect:
479- * 1. We first try a widening conversion to the type's info with
480- * the original prefix. Since the original prefix is known to
481- * be a subtype of the returned prefix, this can improve results.
482- * 2. Then, if the approximation result is a singleton reference C#x.type, we
483- * replace by the widened type, which is usually more natural.
484- * 3. Finally, we need to handle the case where the prefix type does not have a member
485- * named `tp.name` anymmore. In that case, we need to fall back to Bot..Top.
486- */
487- override def derivedSelect (tp : NamedType , pre : Type ) =
488- if (pre eq tp.prefix)
489- tp
490- else tryWiden(tp, tp.prefix).orElse {
491- if (tp.isTerm && variance > 0 && ! pre.isSingleton)
492- apply(tp.info.widenExpr)
493- else if (upper(pre).member(tp.name).exists)
494- super .derivedSelect(tp, pre)
495- else
496- range(defn.NothingType , defn.AnyType )
497- }
498516 }
499517
500518 widenMap(tp)
0 commit comments