Skip to content

Commit c9b30ac

Browse files
committed
Implement avoid in terms of ApproximatingTypeMap
This puts avoid on firmer ground.
1 parent efbb1c6 commit c9b30ac

File tree

1 file changed

+79
-10
lines changed

1 file changed

+79
-10
lines changed

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,84 @@ trait TypeAssigner {
3939
}
4040
}
4141

42+
/** Given a class info, the intersection of its parents, refined by all
43+
* non-private fields, methods, and type members.
44+
*/
45+
def classBound(info: ClassInfo)(implicit ctx: Context): Type = {
46+
val parentType = info.parentsWithArgs.reduceLeft(ctx.typeComparer.andType(_, _))
47+
def addRefinement(parent: Type, decl: Symbol) = {
48+
val inherited =
49+
parentType.findMember(decl.name, info.cls.thisType, Private)
50+
.suchThat(decl.matches(_))
51+
val inheritedInfo = inherited.info
52+
if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) {
53+
val r = RefinedType(parent, decl.name, decl.info)
54+
typr.println(i"add ref $parent $decl --> " + r)
55+
r
56+
}
57+
else
58+
parent
59+
}
60+
val refinableDecls = info.decls.filter(
61+
sym => !(sym.is(TypeParamAccessor | Private) || sym.isConstructor))
62+
val raw = (parentType /: refinableDecls)(addRefinement)
63+
RecType.closeOver(rt => raw.substThis(info.cls, RecThis(rt)))
64+
}
65+
4266
/** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`.
43-
* Approximation steps are:
44-
*
45-
* - follow aliases and upper bounds if the original refers to a forbidden symbol
46-
* - widen termrefs that refer to a forbidden symbol
47-
* - replace ClassInfos of forbidden classes by the intersection of their parents, refined by all
48-
* non-private fields, methods, and type members.
49-
* - if the prefix of a class refers to a forbidden symbol, first try to replace the prefix,
50-
* if this is not possible, replace the ClassInfo as above.
51-
* - drop refinements referring to a forbidden symbol.
5267
*/
5368
def avoid(tp: Type, symsToAvoid: => List[Symbol])(implicit ctx: Context): Type = {
69+
val wmap = new ApproximatingTypeMap {
70+
lazy val forbidden = symsToAvoid.toSet
71+
def toAvoid(sym: Symbol) = !sym.isStatic && forbidden.contains(sym)
72+
def partsToAvoid = new NamedPartsAccumulator(tp => toAvoid(tp.symbol))
73+
def apply(tp: Type): Type = tp match {
74+
case tp: TermRef
75+
if toAvoid(tp.symbol) || partsToAvoid(mutable.Set.empty, tp.info).nonEmpty =>
76+
tp.info.widenExpr match {
77+
case info: SingletonType => apply(info)
78+
case info => range(tp.info.bottomType, apply(info))
79+
}
80+
case tp: TypeRef if toAvoid(tp.symbol) =>
81+
val avoided = tp.info match {
82+
case TypeAlias(alias) =>
83+
apply(alias)
84+
case TypeBounds(lo, hi) =>
85+
range(atVariance(-variance)(apply(lo)), apply(hi))
86+
case info: ClassInfo =>
87+
range(tp.bottomType, apply(classBound(info)))
88+
case _ =>
89+
range(tp.bottomType, tp.topType) // should happen only in error cases
90+
}
91+
avoided
92+
case tp: ThisType if toAvoid(tp.cls) =>
93+
range(tp.bottomType, apply(classBound(tp.cls.classInfo)))
94+
case tp: TypeVar if ctx.typerState.constraint.contains(tp) =>
95+
val lo = ctx.typeComparer.instanceType(tp.origin, fromBelow = true)
96+
val lo1 = apply(lo)
97+
//println(i"INST $tp --> $lo --> $lo1")
98+
if (lo1 ne lo) lo1 else tp
99+
case tp: TermRef if false =>
100+
val saved = variance
101+
variance = 0
102+
val prefix1 = this(tp.prefix)
103+
variance = saved
104+
if (isRange(prefix1)) range(tp.bottomType, apply(tp.info.widenExpr))
105+
else derivedSelect(tp, prefix1)
106+
case _ =>
107+
mapOver(tp)
108+
}
109+
110+
/** Needs to handle the case where the prefix type does not have a member
111+
* named `tp.name` anymmore.
112+
*/
113+
override def derivedSelect(tp: NamedType, pre: Type) =
114+
if (pre eq tp.prefix) tp
115+
else if (tp.isTerm && variance > 0 && !pre.isInstanceOf[SingletonType])
116+
apply(tp.info.widenExpr)
117+
else if (upper(pre).member(tp.name).exists) super.derivedSelect(tp, pre)
118+
else range(tp.bottomType, tp.topType)
119+
}
54120
val widenMap = new TypeMap {
55121
lazy val forbidden = symsToAvoid.toSet
56122
def toAvoid(tp: Type): Boolean =
@@ -128,7 +194,10 @@ trait TypeAssigner {
128194
mapOver(tp)
129195
}
130196
}
131-
widenMap(tp)
197+
//val was = widenMap(tp)
198+
val now = wmap(tp)
199+
//if (was.show != now.show) println(i"difference for avoid $tp, ${tp.toString}, forbidden = $symsToAvoid%, %, was: $was, now: $now")
200+
now
132201
}
133202

134203
def avoidingType(expr: Tree, bindings: List[Tree])(implicit ctx: Context): Type =

0 commit comments

Comments
 (0)