@@ -49,6 +49,10 @@ object Inliner {
49
49
object addAccessors extends TreeMap {
50
50
val accessors = new mutable.ListBuffer [MemberDef ]
51
51
52
+ type AccessorMap = mutable.HashMap [Symbol , DefDef ]
53
+ private val getters = new AccessorMap
54
+ private val setters = new AccessorMap
55
+
52
56
/** A definition needs an accessor if it is private, protected, or qualified private
53
57
* and it is not part of the tree that gets inlined. The latter test is implemented
54
58
* by excluding all symbols properly contained in the inlined method.
@@ -85,71 +89,77 @@ object Inliner {
85
89
* @param rhs A function that builds the right-hand side of the accessor,
86
90
* given a reference to the accessed symbol and any type and
87
91
* value arguments the need to be integrated.
92
+ * @param seen An map of already generated accessor methods of this kind (getter or setter)
88
93
* @return The call to the accessor method that replaces the original access.
89
94
*/
90
95
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 = {
92
98
val qual = qualifier(refPart)
99
+
93
100
def refIsLocal = qual match {
94
101
case qual : This => qual.symbol == refPart.symbol.owner
95
102
case _ => false
96
103
}
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
- }
115
104
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)))
117
119
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
+ }
124
131
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)
129
133
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
135
145
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
138
151
139
- val accessorDef = polyDefDef(accessor, tps => argss =>
152
+ val accDef = accessorDef(
153
+ abstractQualType(addQualType(dealiasMap(accessedType))),
154
+ polyDefDef(_, tps => argss =>
140
155
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
+ }
153
163
}
154
164
155
165
override def transform (tree : Tree )(implicit ctx : Context ): Tree = super .transform {
@@ -160,12 +170,14 @@ object Inliner {
160
170
if (methPart.symbol.isConstructor && needsAccessor(methPart.symbol)) {
161
171
ctx.error(" Cannot use private constructors in inline methods" , tree.pos)
162
172
tree // TODO: create a proper accessor for the private constructor
163
- } else {
173
+ }
174
+ else
164
175
addAccessor(tree, methPart, targs, argss,
165
176
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 {
169
181
// TODO: Handle references to non-public types.
170
182
// This is quite tricky, as such types can appear anywhere, including as parts
171
183
// of types of other things. For the moment we do nothing and complain
@@ -181,7 +193,8 @@ object Inliner {
181
193
case Assign (lhs : RefTree , rhs) if needsAccessor(lhs.symbol) =>
182
194
addAccessor(tree, lhs, Nil , (rhs :: Nil ) :: Nil ,
183
195
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)
185
198
case _ => tree
186
199
}
187
200
}
0 commit comments