Skip to content

Commit 18c807e

Browse files
committed
Fix: Avoid generating multiple inline accessors for one symbol
1 parent bde27bf commit 18c807e

File tree

1 file changed

+62
-53
lines changed

1 file changed

+62
-53
lines changed

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

Lines changed: 62 additions & 53 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 = mutable.Map[Symbol, Tree]()
51+
private val setters = mutable.Map[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,71 @@ 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-
}
99+
100+
def addAcc(accessorDef: MemberDef) = {
101+
accessors += accessorDef
102+
inlining.println(i"added inline accessor: $accessorDef")
103+
}
104+
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+
}
114125

115-
val qualType = dealiasMap(qual.tpe.widen)
126+
val qualType = dealiasMap(qual.tpe.widen)
116127

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-
}
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+
}
123134

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
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-
)
142-
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
152+
.withPos(tree.pos.focus)))
153+
154+
ref(accessor)
155+
.appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs)
156+
.appliedToArgss((qual :: Nil) :: argss)
157+
.withPos(tree.pos)
158+
}
152159
}
153160

154161
override def transform(tree: Tree)(implicit ctx: Context): Tree = super.transform {
@@ -160,9 +167,10 @@ object Inliner {
160167
ctx.error("Cannot use private constructors in inline methods", tree.pos)
161168
tree // TODO: create a proper accessor for the private constructor
162169
} else {
163-
addAccessor(tree, methPart, targs, argss,
170+
getters.getOrElseUpdate(methPart.symbol,
171+
addAccessor(tree, methPart, targs, argss,
164172
accessedType = methPart.tpe.widen,
165-
rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss))
173+
rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss)))
166174
}
167175
} else {
168176
// TODO: Handle references to non-public types.
@@ -178,9 +186,10 @@ object Inliner {
178186
tree
179187
}
180188
case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) =>
181-
addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil,
189+
setters.getOrElseUpdate(lhs.symbol,
190+
addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil,
182191
accessedType = MethodType(rhs.tpe.widen :: Nil, defn.UnitType),
183-
rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head))
192+
rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head)))
184193
case _ => tree
185194
}
186195
}

0 commit comments

Comments
 (0)