@@ -49,6 +49,10 @@ object Inliner {
4949 object addAccessors extends TreeMap {
5050 val accessors = new mutable.ListBuffer [MemberDef ]
5151
52+ type AccessorMap = mutable.HashMap [Symbol , DefDef ]
53+ private val getters = new AccessorMap
54+ private val setters = new AccessorMap
55+
5256 /** A definition needs an accessor if it is private, protected, or qualified private
5357 * and it is not part of the tree that gets inlined. The latter test is implemented
5458 * by excluding all symbols properly contained in the inlined method.
@@ -85,71 +89,77 @@ object Inliner {
8589 * @param rhs A function that builds the right-hand side of the accessor,
8690 * given a reference to the accessed symbol and any type and
8791 * value arguments the need to be integrated.
92+ * @param seen An map of already generated accessor methods of this kind (getter or setter)
8893 * @return The call to the accessor method that replaces the original access.
8994 */
9095 def addAccessor (tree : Tree , refPart : Tree , targs : List [Tree ], argss : List [List [Tree ]],
91- accessedType : Type , rhs : (Tree , List [Type ], List [List [Tree ]]) => Tree )(implicit ctx : Context ): Tree = {
96+ accessedType : Type , rhs : (Tree , List [Type ], List [List [Tree ]]) => Tree ,
97+ seen : AccessorMap )(implicit ctx : Context ): Tree = {
9298 val qual = qualifier(refPart)
99+
93100 def refIsLocal = qual match {
94101 case qual : This => qual.symbol == refPart.symbol.owner
95102 case _ => false
96103 }
97- val (accessorDef, accessorRef) =
98- if (refPart.symbol.isStatic || refIsLocal) {
99- // Easy case: Reference to a static symbol or a symbol referenced via `this.`
100- val accessorType = accessedType.ensureMethodic
101- val accessor = accessorSymbol(tree, accessorType).asTerm
102- val accessorDef = polyDefDef(accessor, tps => argss =>
103- rhs(refPart, tps, argss).withPos(tree.pos.focus))
104- val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss).withPos(tree.pos)
105- (accessorDef, accessorRef)
106- } else {
107- // Hard case: Reference needs to go via a dynamic prefix
108- inlining.println(i " adding inline accessor for $tree -> ( ${qual.tpe}, $refPart: ${refPart.getClass}, [ $targs%, %], ( $argss%, %)) " )
109-
110- // Need to dealias in order to catch all possible references to abstracted over types in
111- // substitutions
112- val dealiasMap = new TypeMap {
113- def apply (t : Type ) = mapOver(t.dealias)
114- }
115104
116- val qualType = dealiasMap(qual.tpe.widen)
105+ def accessorDef (accessorType : Type , accessorDefFn : TermSymbol => DefDef ): DefDef =
106+ seen.getOrElseUpdate(refPart.symbol, {
107+ val acc = accessorSymbol(tree, accessorType).asTerm
108+ val accessorDef = accessorDefFn(acc)
109+ accessors += accessorDef
110+ inlining.println(i " added inline accessor: $accessorDef" )
111+ accessorDef
112+ })
113+
114+ if (refPart.symbol.isStatic || refIsLocal) {
115+ // Easy case: Reference to a static symbol or a symbol referenced via `this.`
116+ val accDef = accessorDef(
117+ accessedType.ensureMethodic,
118+ polyDefDef(_, tps => argss => rhs(refPart, tps, argss).withPos(tree.pos.focus)))
117119
118- // Add qualifier type as leading method argument to argument `tp`
119- def addQualType (tp : Type ): Type = tp match {
120- case tp : PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, addQualType(tp.resultType))
121- case tp : ExprType => addQualType(tp.resultType)
122- case tp => MethodType (qualType :: Nil , tp)
123- }
120+ ref(accDef.symbol).appliedToTypeTrees(targs).appliedToArgss(argss).withPos(tree.pos)
121+ }
122+ else {
123+ // Hard case: Reference needs to go via a dynamic prefix
124+ inlining.println(i " adding inline accessor for $tree -> ( ${qual.tpe}, $refPart: ${refPart.getClass}, [ $targs%, %], ( $argss%, %)) " )
125+
126+ // Need to dealias in order to catch all possible references to abstracted over types in
127+ // substitutions
128+ val dealiasMap = new TypeMap {
129+ def apply (t : Type ) = mapOver(t.dealias)
130+ }
124131
125- // The types that are local to the inlined method, and that therefore have
126- // to be abstracted out in the accessor, which is external to the inlined method
127- val localRefs = qualType.namedPartsWith(ref =>
128- ref.isType && ref.symbol.isContainedIn(inlineMethod)).toList
132+ val qualType = dealiasMap(qual.tpe.widen)
129133
130- // Abstract accessed type over local refs
131- def abstractQualType (mtpe : Type ): Type =
132- if (localRefs.isEmpty) mtpe
133- else PolyType .fromParams(localRefs.map(_.symbol.asType), mtpe)
134- .asInstanceOf [PolyType ].flatten
134+ // Add qualifier type as leading method argument to argument `tp`
135+ def addQualType (tp : Type ): Type = tp match {
136+ case tp : PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, addQualType(tp.resultType))
137+ case tp : ExprType => addQualType(tp.resultType)
138+ case tp => MethodType (qualType :: Nil , tp)
139+ }
140+
141+ // The types that are local to the inlined method, and that therefore have
142+ // to be abstracted out in the accessor, which is external to the inlined method
143+ val localRefs = qualType.namedPartsWith(ref =>
144+ ref.isType && ref.symbol.isContainedIn(inlineMethod)).toList
135145
136- val accessorType = abstractQualType(addQualType(dealiasMap(accessedType)))
137- val accessor = accessorSymbol(tree, accessorType).asTerm
146+ // Abstract accessed type over local refs
147+ def abstractQualType (mtpe : Type ): Type =
148+ if (localRefs.isEmpty) mtpe
149+ else PolyType .fromParams(localRefs.map(_.symbol.asType), mtpe)
150+ .asInstanceOf [PolyType ].flatten
138151
139- val accessorDef = polyDefDef(accessor, tps => argss =>
152+ val accDef = accessorDef(
153+ abstractQualType(addQualType(dealiasMap(accessedType))),
154+ polyDefDef(_, tps => argss =>
140155 rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail)
141- .withPos(tree.pos.focus)
142- )
143-
144- val accessorRef = ref(accessor)
145- .appliedToTypeTrees(localRefs.map(TypeTree (_)) ++ targs)
146- .appliedToArgss((qual :: Nil ) :: argss)
147- .withPos(tree.pos)
148- (accessorDef, accessorRef)
149- }
150- accessors += accessorDef
151- inlining.println(i " added inline accessor: $accessorDef" )
152- accessorRef
156+ .withPos(tree.pos.focus)))
157+
158+ ref(accDef.symbol)
159+ .appliedToTypeTrees(localRefs.map(TypeTree (_)) ++ targs)
160+ .appliedToArgss((qual :: Nil ) :: argss)
161+ .withPos(tree.pos)
162+ }
153163 }
154164
155165 override def transform (tree : Tree )(implicit ctx : Context ): Tree = super .transform {
@@ -160,12 +170,14 @@ object Inliner {
160170 if (methPart.symbol.isConstructor && needsAccessor(methPart.symbol)) {
161171 ctx.error(" Cannot use private constructors in inline methods" , tree.pos)
162172 tree // TODO: create a proper accessor for the private constructor
163- } else {
173+ }
174+ else
164175 addAccessor(tree, methPart, targs, argss,
165176 accessedType = methPart.tpe.widen,
166- rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss))
167- }
168- } else {
177+ rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss),
178+ seen = getters)
179+ }
180+ else {
169181 // TODO: Handle references to non-public types.
170182 // This is quite tricky, as such types can appear anywhere, including as parts
171183 // of types of other things. For the moment we do nothing and complain
@@ -181,7 +193,8 @@ object Inliner {
181193 case Assign (lhs : RefTree , rhs) if needsAccessor(lhs.symbol) =>
182194 addAccessor(tree, lhs, Nil , (rhs :: Nil ) :: Nil ,
183195 accessedType = MethodType (rhs.tpe.widen :: Nil , defn.UnitType ),
184- rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head))
196+ rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head),
197+ seen = setters)
185198 case _ => tree
186199 }
187200 }
0 commit comments