@@ -31,35 +31,40 @@ trait TypeAssigner {
31
31
/** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`.
32
32
* Approximation steps are:
33
33
*
34
- * - follow aliases if the original refers to a forbidden symbol
34
+ * - follow aliases and upper bounds if the original refers to a forbidden symbol
35
35
* - widen termrefs that refer to a forbidden symbol
36
36
* - replace ClassInfos of forbidden classes by the intersection of their parents, refined by all
37
37
* non-private fields, methods, and type members.
38
+ * - if the prefix of a class refers to a forbidden symbol, first try to replace the prefix,
39
+ * if this is not possible, replace the ClassInfo as above.
38
40
* - drop refinements referring to a forbidden symbol.
39
41
*/
40
42
def avoid (tp : Type , symsToAvoid : => List [Symbol ])(implicit ctx : Context ): Type = {
41
43
val widenMap = new TypeMap {
42
44
lazy val forbidden = symsToAvoid.toSet
43
- def toAvoid (tp : Type ): Boolean = tp match {
44
- case tp : TermRef =>
45
- val sym = tp.symbol
46
- sym.exists && (
47
- sym.owner.isTerm && (forbidden contains sym)
48
- || ! (sym.owner is Package ) && toAvoid(tp.prefix)
49
- )
50
- case tp : TypeRef =>
51
- forbidden contains tp.symbol
52
- case _ =>
53
- false
54
- }
55
- def apply (tp : Type ) = tp match {
45
+ def toAvoid (tp : Type ): Boolean =
46
+ // TODO: measure the cost of using `existsPart`, and if necessary replace it
47
+ // by a `TypeAccumulator` where we have set `stopAtStatic = true`.
48
+ tp existsPart {
49
+ case tp : NamedType =>
50
+ forbidden contains tp.symbol
51
+ case _ =>
52
+ false
53
+ }
54
+ def apply (tp : Type ): Type = tp match {
56
55
case tp : TermRef if toAvoid(tp) && variance > 0 =>
57
56
apply(tp.info.widenExpr)
58
- case tp : TypeRef if (forbidden contains tp.symbol) || toAvoid(tp.prefix ) =>
57
+ case tp : TypeRef if toAvoid(tp) =>
59
58
tp.info match {
60
59
case TypeAlias (ref) =>
61
60
apply(ref)
62
61
case info : ClassInfo if variance > 0 =>
62
+ if (! (forbidden contains tp.symbol)) {
63
+ val prefix = apply(tp.prefix)
64
+ val tp1 = tp.derivedSelect(prefix)
65
+ if (tp1.typeSymbol.exists)
66
+ return tp1
67
+ }
63
68
val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _))
64
69
def addRefinement (parent : Type , decl : Symbol ) = {
65
70
val inherited =
@@ -82,13 +87,28 @@ trait TypeAssigner {
82
87
case _ =>
83
88
mapOver(tp)
84
89
}
85
- case tp : RefinedType =>
86
- val tp1 @ RefinedType (parent1, _) = mapOver(tp)
87
- if (tp1.refinedInfo.existsPart(toAvoid) && variance > 0 ) {
88
- typr.println(s " dropping refinement from $tp1" )
89
- parent1
90
+ case tp @ RefinedType (parent, name) if variance > 0 =>
91
+ // The naive approach here would be to first approximate the parent,
92
+ // but if the base type of the approximated parent is different from
93
+ // the current base type, then the current refinement won't be valid
94
+ // if it's a type parameter refinement.
95
+ // Therefore we first approximate the base type, then use `baseArgInfos`
96
+ // to get correct refinements for the approximated base type, then
97
+ // recursively approximate the resulting type.
98
+ val base = tp.unrefine
99
+ if (toAvoid(base)) {
100
+ val base1 = apply(base)
101
+ apply(base1.appliedTo(tp.baseArgInfos(base1.typeSymbol)))
102
+ } else {
103
+ val parent1 = apply(tp.parent)
104
+ val refinedInfo1 = apply(tp.refinedInfo)
105
+ if (toAvoid(refinedInfo1)) {
106
+ typr.println(s " dropping refinement from $tp" )
107
+ parent1
108
+ } else {
109
+ tp.derivedRefinedType(parent1, name, refinedInfo1)
110
+ }
90
111
}
91
- else tp1
92
112
case tp : TypeVar if ctx.typerState.constraint.contains(tp) =>
93
113
val lo = ctx.typerState.constraint.fullLowerBound(tp.origin)
94
114
val lo1 = avoid(lo, symsToAvoid)
0 commit comments