@@ -15,7 +15,7 @@ import StdNames.nme
15
15
import Contexts .Context
16
16
import Names .{Name , TermName , EmptyTermName }
17
17
import NameOps ._
18
- import NameKinds .InlineAccessorName
18
+ import NameKinds .{ ClassifiedNameKind , InlineGetterName , InlineSetterName }
19
19
import ProtoTypes .selectionProto
20
20
import SymDenotations .SymDenotation
21
21
import Annotations ._
@@ -47,11 +47,6 @@ object Inliner {
47
47
* from inlined code. Accessors are collected in the `accessors` buffer.
48
48
*/
49
49
object addAccessors extends TreeMap {
50
- val accessors = new mutable.ListBuffer [MemberDef ]
51
-
52
- type AccessorMap = mutable.HashMap [Symbol , DefDef ]
53
- private val getters = new AccessorMap
54
- private val setters = new AccessorMap
55
50
56
51
/** A definition needs an accessor if it is private, protected, or qualified private
57
52
* and it is not part of the tree that gets inlined. The latter test is implemented
@@ -62,140 +57,64 @@ object Inliner {
62
57
(sym.is(AccessFlags ) || sym.privateWithin.exists) &&
63
58
! sym.owner.isContainedIn(inlineMethod)
64
59
65
- /** The name of the next accessor to be generated */
66
- def accessorName (implicit ctx : Context ) = InlineAccessorName .fresh(inlineMethod.name.asTermName)
67
-
68
60
/** A fresh accessor symbol.
69
61
*
70
62
* @param tree The tree representing the original access to the non-public member
71
- * @param accessorInfo The type of the accessor
72
63
*/
73
- def accessorSymbol (tree : Tree , accessorInfo : Type )(implicit ctx : Context ): Symbol =
74
- ctx.newSymbol(
75
- owner = inlineMethod.owner,
76
- name = if (tree.isTerm) accessorName.toTermName else accessorName.toTypeName,
77
- flags = if (tree.isTerm) Synthetic | Method else Synthetic ,
78
- info = accessorInfo,
79
- coord = tree.pos).entered
80
-
81
- /** Add an accessor to a non-public method and replace the original access with a
82
- * call to the accessor.
64
+ def newAccessorSymbol (accessed : TermSymbol , name : TermName , info : Type )(implicit ctx : Context ): TermSymbol =
65
+ ctx.newSymbol(accessed.owner, name, Synthetic | Method , info, coord = accessed.pos).entered
66
+
67
+ /** Create an inline accessor unless one exists already, and replace the original
68
+ * access with a reference to the accessor.
83
69
*
84
- * @param tree The original access to the non-public symbol
85
- * @param refPart The part that refers to the method or field of the original access
86
- * @param targs All type arguments passed in the access, if any
87
- * @param argss All value arguments passed in the access, if any
88
- * @param accessedType The type of the accessed method or field, as seen from the access site.
89
- * @param rhs A function that builds the right-hand side of the accessor,
90
- * given a reference to the accessed symbol and any type and
91
- * value arguments the need to be integrated.
92
- * @param seen An map of already generated accessor methods of this kind (getter or setter)
93
- * @return The call to the accessor method that replaces the original access.
70
+ * @param reference The original reference to the non-public symbol
71
+ * @param onLHS The reference is on the left-hand side of an assignment
94
72
*/
95
- def addAccessor (tree : Tree , refPart : Tree , targs : List [Tree ], argss : List [List [Tree ]],
96
- accessedType : Type , rhs : (Tree , List [Type ], List [List [Tree ]]) => Tree ,
97
- seen : AccessorMap )(implicit ctx : Context ): Tree = {
98
- val qual = qualifier(refPart)
99
-
100
- def refIsLocal = qual match {
101
- case qual : This => qual.symbol == refPart.symbol.owner
102
- case _ => false
103
- }
73
+ def useAccessor (reference : RefTree , onLHS : Boolean )(implicit ctx : Context ): Tree = {
104
74
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)))
75
+ def nameKind = if (onLHS) InlineSetterName else InlineGetterName
76
+ val accessed = reference.symbol.asTerm
119
77
120
- ref(accDef.symbol).appliedToTypeTrees(targs).appliedToArgss(argss).withPos(tree.pos)
78
+ def refersToAccessed (sym : Symbol ) = sym.getAnnotation(defn.AccessedAnnot ) match {
79
+ case Some (Annotation .Accessed (sym)) => sym `eq` accessed
80
+ case _ => false
121
81
}
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
- }
131
82
132
- val qualType = dealiasMap(qual.tpe.widen)
83
+ val accessorInfo =
84
+ if (onLHS) MethodType (accessed.info :: Nil , defn.UnitType )
85
+ else accessed.info.ensureMethodic
86
+ val accessorName = nameKind(accessed.name)
87
+ val accessorSymbol =
88
+ accessed.owner.info.decl(accessorName).suchThat(refersToAccessed).symbol
89
+ .orElse {
90
+ val acc = newAccessorSymbol(accessed, accessorName, accessorInfo)
91
+ acc.addAnnotation(Annotation .Accessed (accessed))
92
+ acc
93
+ }
133
94
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)
95
+ { reference match {
96
+ case Select (qual, _) => qual.select(accessorSymbol)
97
+ case Ident (name) => ref(accessorSymbol)
139
98
}
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
145
-
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
151
-
152
- val accDef = accessorDef(
153
- abstractQualType(addQualType(dealiasMap(accessedType))),
154
- polyDefDef(_, tps => argss =>
155
- rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail)
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
- }
99
+ }.withPos(reference.pos)
163
100
}
164
101
102
+ // TODO: Also handle references to non-public types.
103
+ // This is quite tricky, as such types can appear anywhere, including as parts
104
+ // of types of other things. For the moment we do nothing and complain
105
+ // at the implicit expansion site if there's a reference to an inaccessible type.
165
106
override def transform (tree : Tree )(implicit ctx : Context ): Tree = super .transform {
166
107
tree match {
167
- case _ : Apply | _ : TypeApply | _ : RefTree if needsAccessor(tree.symbol) =>
168
- if (tree.isTerm) {
169
- val (methPart, targs, argss) = decomposeCall(tree)
170
- if (methPart.symbol.isConstructor && needsAccessor(methPart.symbol)) {
171
- ctx.error(" Cannot use private constructors in inline methods" , tree.pos)
172
- tree // TODO: create a proper accessor for the private constructor
173
- }
174
- else
175
- addAccessor(tree, methPart, targs, argss,
176
- accessedType = methPart.tpe.widen,
177
- rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss),
178
- seen = getters)
179
- }
180
- else {
181
- // TODO: Handle references to non-public types.
182
- // This is quite tricky, as such types can appear anywhere, including as parts
183
- // of types of other things. For the moment we do nothing and complain
184
- // at the implicit expansion site if there's a reference to an inaccessible type.
185
- // Draft code (incomplete):
186
- //
187
- // val accessor = accessorSymbol(tree, TypeAlias(tree.tpe)).asType
188
- // myAccessors += TypeDef(accessor).withPos(tree.pos.focus)
189
- // ref(accessor).withPos(tree.pos)
190
- //
191
- tree
108
+ case tree : RefTree if needsAccessor(tree.symbol) =>
109
+ if (tree.symbol.isConstructor) {
110
+ ctx.error(" Implementation restriction: cannot use private constructors in inline methods" , tree.pos)
111
+ tree // TODO: create a proper accessor for the private constructor
192
112
}
113
+ else useAccessor(tree, onLHS = false )
193
114
case Assign (lhs : RefTree , rhs) if needsAccessor(lhs.symbol) =>
194
- addAccessor(tree, lhs, Nil , (rhs :: Nil ) :: Nil ,
195
- accessedType = MethodType (rhs.tpe.widen :: Nil , defn.UnitType ),
196
- rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head),
197
- seen = setters)
198
- case _ => tree
115
+ cpy.Apply (tree)(useAccessor(lhs, onLHS = true ), List (rhs))
116
+ case _ =>
117
+ tree
199
118
}
200
119
}
201
120
}
@@ -204,12 +123,23 @@ object Inliner {
204
123
// Inline methods in local scopes can only be called in the scope they are defined,
205
124
// so no accessors are needed for them.
206
125
tree
207
- else {
208
- val tree1 = addAccessors.transform(tree)
209
- flatTree(tree1 :: addAccessors.accessors.toList)
210
- }
126
+ else addAccessors.transform(tree)
211
127
}
212
128
129
+ /** The inline accessor definitions that need to be added to class `cls` */
130
+ def accessorDefs (cls : Symbol )(implicit ctx : Context ): List [DefDef ] =
131
+ for (accessor <- cls.info.decls.filter(sym => sym.name.is(InlineGetterName ) || sym.name.is(InlineSetterName )))
132
+ yield polyDefDef(accessor.asTerm, tps => argss => {
133
+ val Annotation .Accessed (accessed) = accessor.getAnnotation(defn.AccessedAnnot ).get
134
+ val rhs =
135
+ if (accessor.name.is(InlineSetterName ) &&
136
+ argss.nonEmpty && argss.head.nonEmpty) // defensive conditions
137
+ ref(accessed).becomes(argss.head.head)
138
+ else
139
+ ref(accessed).appliedToTypes(tps).appliedToArgss(argss)
140
+ rhs.withPos(accessed.pos)
141
+ })
142
+
213
143
/** Register inline info for given inline method `sym`.
214
144
*
215
145
* @param sym The symbol denotatioon of the inline method for which info is registered
@@ -241,27 +171,11 @@ object Inliner {
241
171
def hasBodyToInline (sym : SymDenotation )(implicit ctx : Context ): Boolean =
242
172
sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot )
243
173
244
- private def bodyAndAccessors (sym : SymDenotation )(implicit ctx : Context ): (Tree , List [MemberDef ]) =
245
- sym.unforcedAnnotation(defn.BodyAnnot ).get.tree match {
246
- case Thicket (body :: accessors) => (body, accessors.asInstanceOf [List [MemberDef ]])
247
- case body => (body, Nil )
248
- }
249
-
250
174
/** The body to inline for method `sym`.
251
175
* @pre hasBodyToInline(sym)
252
176
*/
253
177
def bodyToInline (sym : SymDenotation )(implicit ctx : Context ): Tree =
254
- bodyAndAccessors(sym)._1
255
-
256
- /** The accessors to non-public members needed by the inlinable body of `sym`.
257
- * These accessors are dropped as a side effect of calling this method.
258
- * @pre hasBodyToInline(sym)
259
- */
260
- def removeInlineAccessors (sym : SymDenotation )(implicit ctx : Context ): List [MemberDef ] = {
261
- val (body, accessors) = bodyAndAccessors(sym)
262
- if (accessors.nonEmpty) sym.updateAnnotation(ConcreteBodyAnnotation (body))
263
- accessors
264
- }
178
+ sym.unforcedAnnotation(defn.BodyAnnot ).get.tree
265
179
266
180
/** Try to inline a call to a `@inline` method. Fail with error if the maximal
267
181
* inline depth is exceeded.
0 commit comments