Skip to content

Commit 62fc304

Browse files
committed
Avoid generating mutliple inline accessors for the same access
1 parent bde27bf commit 62fc304

File tree

1 file changed

+66
-55
lines changed

1 file changed

+66
-55
lines changed

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

Lines changed: 66 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ object Inliner {
4747
*/
4848
object addAccessors extends TreeMap {
4949
val accessors = new mutable.ListBuffer[MemberDef]
50+
private val getters = new mutable.HashMap[(Symbol, List[Tree], List[List[Tree]]), Tree]
51+
private val setters = new mutable.HashMap[Symbol, Tree]
5052

5153
/** A definition needs an accessor if it is private, protected, or qualified private
5254
* and it is not part of the tree that gets inlined. The latter test is implemented
@@ -89,66 +91,70 @@ object Inliner {
8991
def addAccessor(tree: Tree, refPart: Tree, targs: List[Tree], argss: List[List[Tree]],
9092
accessedType: Type, rhs: (Tree, List[Type], List[List[Tree]]) => Tree)(implicit ctx: Context): Tree = {
9193
val qual = qualifier(refPart)
94+
9295
def refIsLocal = qual match {
9396
case qual: This => qual.symbol == refPart.symbol.owner
9497
case _ => false
9598
}
96-
val (accessorDef, accessorRef) =
97-
if (refPart.symbol.isStatic || refIsLocal) {
98-
// Easy case: Reference to a static symbol or a symbol referenced via `this.`
99-
val accessorType = accessedType.ensureMethodic
100-
val accessor = accessorSymbol(tree, accessorType).asTerm
101-
val accessorDef = polyDefDef(accessor, tps => argss =>
102-
rhs(refPart, tps, argss).withPos(tree.pos.focus))
103-
val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss).withPos(tree.pos)
104-
(accessorDef, accessorRef)
105-
} else {
106-
// Hard case: Reference needs to go via a dynamic prefix
107-
inlining.println(i"adding inline accessor for $tree -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))")
108-
109-
// Need to dealias in order to catch all possible references to abstracted over types in
110-
// substitutions
111-
val dealiasMap = new TypeMap {
112-
def apply(t: Type) = mapOver(t.dealias)
113-
}
11499

115-
val qualType = dealiasMap(qual.tpe.widen)
100+
def addAcc(accessorDef: MemberDef) = {
101+
accessors += accessorDef
102+
inlining.println(i"added inline accessor: $accessorDef")
103+
}
116104

117-
// Add qualifier type as leading method argument to argument `tp`
118-
def addQualType(tp: Type): Type = tp match {
119-
case tp: PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, addQualType(tp.resultType))
120-
case tp: ExprType => addQualType(tp.resultType)
121-
case tp => MethodType(qualType :: Nil, tp)
122-
}
105+
if (refPart.symbol.isStatic || refIsLocal) {
106+
// Easy case: Reference to a static symbol or a symbol referenced via `this.`
107+
val accessorType = accessedType.ensureMethodic
108+
val accessor = accessorSymbol(tree, accessorType).asTerm
109+
110+
addAcc(
111+
polyDefDef(accessor, tps => argss =>
112+
rhs(refPart, tps, argss).withPos(tree.pos.focus)))
113+
114+
ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss).withPos(tree.pos)
115+
}
116+
else {
117+
// Hard case: Reference needs to go via a dynamic prefix
118+
inlining.println(i"adding inline accessor for $tree -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))")
119+
120+
// Need to dealias in order to catch all possible references to abstracted over types in
121+
// substitutions
122+
val dealiasMap = new TypeMap {
123+
def apply(t: Type) = mapOver(t.dealias)
124+
}
125+
126+
val qualType = dealiasMap(qual.tpe.widen)
123127

124-
// The types that are local to the inlined method, and that therefore have
125-
// to be abstracted out in the accessor, which is external to the inlined method
126-
val localRefs = qualType.namedPartsWith(ref =>
127-
ref.isType && ref.symbol.isContainedIn(inlineMethod)).toList
128+
// Add qualifier type as leading method argument to argument `tp`
129+
def addQualType(tp: Type): Type = tp match {
130+
case tp: PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, addQualType(tp.resultType))
131+
case tp: ExprType => addQualType(tp.resultType)
132+
case tp => MethodType(qualType :: Nil, tp)
133+
}
134+
135+
// The types that are local to the inlined method, and that therefore have
136+
// to be abstracted out in the accessor, which is external to the inlined method
137+
val localRefs = qualType.namedPartsWith(ref =>
138+
ref.isType && ref.symbol.isContainedIn(inlineMethod)).toList
128139

129-
// Abstract accessed type over local refs
130-
def abstractQualType(mtpe: Type): Type =
131-
if (localRefs.isEmpty) mtpe
132-
else PolyType.fromParams(localRefs.map(_.symbol.asType), mtpe)
133-
.asInstanceOf[PolyType].flatten
140+
// Abstract accessed type over local refs
141+
def abstractQualType(mtpe: Type): Type =
142+
if (localRefs.isEmpty) mtpe
143+
else PolyType.fromParams(localRefs.map(_.symbol.asType), mtpe)
144+
.asInstanceOf[PolyType].flatten
134145

135-
val accessorType = abstractQualType(addQualType(dealiasMap(accessedType)))
136-
val accessor = accessorSymbol(tree, accessorType).asTerm
146+
val accessorType = abstractQualType(addQualType(dealiasMap(accessedType)))
147+
val accessor = accessorSymbol(tree, accessorType).asTerm
137148

138-
val accessorDef = polyDefDef(accessor, tps => argss =>
149+
addAcc(
150+
polyDefDef(accessor, tps => argss =>
139151
rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail)
140-
.withPos(tree.pos.focus)
141-
)
152+
.withPos(tree.pos.focus)))
142153

143-
val accessorRef = ref(accessor)
144-
.appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs)
145-
.appliedToArgss((qual :: Nil) :: argss)
146-
.withPos(tree.pos)
147-
(accessorDef, accessorRef)
148-
}
149-
accessors += accessorDef
150-
inlining.println(i"added inline accessor: $accessorDef")
151-
accessorRef
154+
ref(accessor)
155+
.appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs)
156+
.appliedToArgss((qual :: Nil) :: argss)
157+
}
152158
}
153159

154160
override def transform(tree: Tree)(implicit ctx: Context): Tree = super.transform {
@@ -159,12 +165,15 @@ object Inliner {
159165
if (methPart.symbol.isConstructor && needsAccessor(methPart.symbol)) {
160166
ctx.error("Cannot use private constructors in inline methods", tree.pos)
161167
tree // TODO: create a proper accessor for the private constructor
162-
} else {
163-
addAccessor(tree, methPart, targs, argss,
164-
accessedType = methPart.tpe.widen,
165-
rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss))
166168
}
167-
} else {
169+
else
170+
getters.getOrElseUpdate((methPart.symbol, targs, argss),
171+
addAccessor(tree, methPart, targs, argss,
172+
accessedType = methPart.tpe.widen,
173+
rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss)))
174+
.withPos(tree.pos)
175+
}
176+
else {
168177
// TODO: Handle references to non-public types.
169178
// This is quite tricky, as such types can appear anywhere, including as parts
170179
// of types of other things. For the moment we do nothing and complain
@@ -178,9 +187,11 @@ object Inliner {
178187
tree
179188
}
180189
case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) =>
181-
addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil,
190+
setters.getOrElseUpdate(lhs.symbol,
191+
addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil,
182192
accessedType = MethodType(rhs.tpe.widen :: Nil, defn.UnitType),
183-
rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head))
193+
rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head)))
194+
.withPos(tree.pos)
184195
case _ => tree
185196
}
186197
}

0 commit comments

Comments
 (0)