@@ -25,44 +25,28 @@ import util.{Property, SourceFile, NoSource}
25
25
import collection .mutable
26
26
import transform .TypeUtils ._
27
27
28
- /** Todo wrt inline SIP:
29
- *
30
- * 1. According to Inline SIP, by-name parameters are not hoisted out, but we currently
31
- * do hoist them.
32
- *
33
- * 2. Inline call-by-name parameters are currently ignored. Not sure what the rules should be.
34
- */
35
28
object Inliner {
36
29
import tpd ._
37
30
38
- /** An attachment for inline methods, which contains
39
- *
40
- * - the body to inline, as a typed tree
41
- * - the definitions of all needed accessors to non-public members from inlined code
42
- *
43
- * @param treeExpr A function that computes the tree to be inlined, given a context
44
- * This tree may still refer to non-public members.
45
- * @param inlineCtxFn A function that maps the current context to the context in
46
- * which to compute the tree. The resulting context needs
47
- * to have the inlined method as owner.
48
- *
49
- * The reason to use a function rather than a fixed context here
50
- * is to avoid space leaks. InlineInfos can survive multiple runs
51
- * because they might be created as part of an unpickled method
52
- * and consumed only in a future run (or never).
31
+ /** A key to be used in a context property that tracks enclosing inlined calls */
32
+ private val InlinedCalls = new Property .Key [List [Tree ]] // to be used in context
33
+
34
+ /** Adds accessors accessors for all non-public term members accessed
35
+ * from `tree`. Non-public type members are currently left as they are.
36
+ * This means that references to a private type will lead to typing failures
37
+ * on the code when it is inlined. Less than ideal, but hard to do better (see below).
38
+ *
39
+ * @return If there are accessors generated, a thicket consisting of the rewritten `tree`
40
+ * and all accessors, otherwise the original tree.
53
41
*/
54
- private final class InlineInfo (treeExpr : Context => Tree , var inlineCtxFn : Context => Context ) {
55
- private val myAccessors = new mutable.ListBuffer [MemberDef ]
56
- private var myBody : Tree = _
57
- private var evaluated = false
42
+ def makeInlineable (tree : Tree )(implicit ctx : Context ) = {
58
43
59
44
/** A tree map which inserts accessors for all non-public term members accessed
60
- * from inlined code. Non-public type members are currently left as they are.
61
- * This means that references to a provate type will lead to typing failures
62
- * on the code when it is inlined. Less than ideal, but hard to do better (see below).
45
+ * from inlined code. Accesors are collected in the `accessors` buffer.
63
46
*/
64
- private def prepareForInline (inlineCtx : Context ) = new TreeMap {
65
- val inlineMethod = inlineCtx.owner
47
+ object addAccessors extends TreeMap {
48
+ val inlineMethod = ctx.owner
49
+ val accessors = new mutable.ListBuffer [MemberDef ]
66
50
67
51
/** A definition needs an accessor if it is private, protected, or qualified private */
68
52
def needsAccessor (sym : Symbol )(implicit ctx : Context ) =
@@ -149,7 +133,7 @@ object Inliner {
149
133
.appliedToArgss((qual :: Nil ) :: argss)
150
134
(accessorDef, accessorRef)
151
135
}
152
- myAccessors += accessorDef
136
+ accessors += accessorDef
153
137
inlining.println(i " added inline accessor: $accessorDef" )
154
138
accessorRef
155
139
}
@@ -184,106 +168,61 @@ object Inliner {
184
168
}
185
169
}
186
170
187
- /** Is the inline info evaluated? */
188
- def isEvaluated = evaluated
189
-
190
- private def ensureEvaluated ()(implicit ctx : Context ) =
191
- if (! evaluated) {
192
- evaluated = true // important to set early to prevent overwrites by attachInlineInfo in typedDefDef
193
- val inlineCtx = inlineCtxFn(ctx)
194
- inlineCtxFn = null // null out to avoid space leaks
195
- try {
196
- myBody = treeExpr(inlineCtx)
197
- myBody = prepareForInline(inlineCtx).transform(myBody)(inlineCtx)
198
- inlining.println(i " inlinable body of ${inlineCtx.owner} = $myBody" )
199
- }
200
- catch {
201
- case ex : AssertionError =>
202
- println(i " failure while expanding ${inlineCtx.owner}" )
203
- throw ex
204
- }
205
- }
206
- else assert(myBody != null )
207
-
208
- /** The body to inline */
209
- def body (implicit ctx : Context ): Tree = {
210
- ensureEvaluated()
211
- myBody
212
- }
213
-
214
- /** The accessor defs to non-public members which need to be defined
215
- * together with the inline method
216
- */
217
- def accessors (implicit ctx : Context ): List [MemberDef ] = {
218
- ensureEvaluated()
219
- myAccessors.toList
220
- }
171
+ val tree1 = addAccessors.transform(tree)
172
+ flatTree(tree1 :: addAccessors.accessors.toList)
221
173
}
222
174
223
- /** A key to be used in an attachment for `@inline` annotations */
224
- private val InlineInfo = new Property .Key [InlineInfo ]
225
-
226
- /** A key to be used in a context property that tracks enclosing inlined calls */
227
- private val InlinedCalls = new Property .Key [List [Tree ]] // to be used in context
228
-
229
175
/** Register inline info for given inline method `sym`.
230
176
*
231
177
* @param sym The symbol denotatioon of the inline method for which info is registered
232
178
* @param treeExpr A function that computes the tree to be inlined, given a context
233
179
* This tree may still refer to non-public members.
234
- * @param inlineCtxFn A function that maps the current context to the context in
235
- * which to compute the tree. The resulting context needs
180
+ * @param ctx The context to use for evaluating `treeExpr`. It needs
236
181
* to have the inlined method as owner.
237
182
*/
238
183
def registerInlineInfo (
239
- sym : SymDenotation , treeExpr : Context => Tree , inlineCtxFn : Context => Context )(implicit ctx : Context ): Unit = {
240
- if (sym.unforcedAnnotation(defn.BodyAnnot ).isEmpty) {
241
- val inlineAnnotTree = sym.unforcedAnnotation(defn.InlineAnnot ).get.tree
242
- inlineAnnotTree.getAttachment(InlineInfo ) match {
243
- case Some (inlineInfo) if inlineInfo.isEvaluated => // keep existing attachment
244
- case _ =>
245
- if (! ctx.isAfterTyper)
246
- inlineAnnotTree.putAttachment(InlineInfo , new InlineInfo (treeExpr, inlineCtxFn))
247
- }
184
+ sym : SymDenotation , treeExpr : Context => Tree )(implicit ctx : Context ): Unit = {
185
+ sym.unforcedAnnotation(defn.BodyAnnot ) match {
186
+ case Some (ann : ConcreteBodyAnnotation ) =>
187
+ case Some (ann : LazyBodyAnnotation ) if ann.isEvaluated =>
188
+ case _ =>
189
+ if (! ctx.isAfterTyper) {
190
+ val inlineCtx = ctx
191
+ sym.updateAnnotation(LazyBodyAnnotation { _ =>
192
+ implicit val ctx : Context = inlineCtx
193
+ val tree1 = treeExpr(ctx)
194
+ makeInlineable(tree1)
195
+ })
196
+ }
248
197
}
249
198
}
250
199
251
- /** Register an evaluated inline body for `sym` */
252
- def updateInlineBody (sym : SymDenotation , body : Tree )(implicit ctx : Context ): Unit = {
253
- assert(sym.unforcedAnnotation(defn.BodyAnnot ).isDefined)
254
- sym.removeAnnotation(defn.BodyAnnot )
255
- sym.addAnnotation(ConcreteBodyAnnotation (body))
256
- }
257
-
258
- /** Optionally, the inline info attached to the `@inline` annotation of `sym`. */
259
- private def inlineInfo (sym : SymDenotation )(implicit ctx : Context ): Option [InlineInfo ] =
260
- sym.getAnnotation(defn.InlineAnnot ).get.tree.getAttachment(InlineInfo )
261
-
262
- /** Optionally, the inline body attached to the `@inline` annotation of `sym`. */
263
- private def inlineBody (sym : SymDenotation )(implicit ctx : Context ): Option [Tree ] =
264
- sym.getAnnotation(defn.BodyAnnot ).map(_.tree)
265
-
266
- /** Definition is an inline method with a known body to inline (note: definitions coming
200
+ /** `sym` has an inline method with a known body to inline (note: definitions coming
267
201
* from Scala2x class files might be `@inline`, but still lack that body.
268
202
*/
269
203
def hasBodyToInline (sym : SymDenotation )(implicit ctx : Context ): Boolean =
270
- sym.isInlineMethod && (inlineInfo(sym).isDefined || inlineBody(sym).isDefined)
204
+ sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot )
205
+
206
+ private def bodyAndAccessors (sym : SymDenotation )(implicit ctx : Context ): (Tree , List [MemberDef ]) =
207
+ sym.unforcedAnnotation(defn.BodyAnnot ).get.tree match {
208
+ case Thicket (body :: accessors) => (body, accessors.asInstanceOf [List [MemberDef ]])
209
+ case body => (body, Nil )
210
+ }
271
211
272
212
/** The body to inline for method `sym`.
273
213
* @pre hasBodyToInline(sym)
274
214
*/
275
215
def bodyToInline (sym : SymDenotation )(implicit ctx : Context ): Tree =
276
- inlineInfo (sym).map(_.body).getOrElse(inlineBody(sym).get)
216
+ bodyAndAccessors (sym)._1
277
217
278
218
/** The accessors to non-public members needed by the inlinable body of `sym`.
219
+ * These accessors are dropped as a side effect of calling this method.
279
220
* @pre hasBodyToInline(sym)
280
221
*/
281
222
def removeInlineAccessors (sym : SymDenotation )(implicit ctx : Context ): List [MemberDef ] = {
282
- val inlineAnnotTree = sym.getAnnotation(defn.InlineAnnot ).get.tree
283
- val inlineInfo = inlineAnnotTree.removeAttachment(InlineInfo ).get
284
- sym.addAnnotation(ConcreteBodyAnnotation (inlineInfo.body))
285
- assert(sym.getAnnotation(defn.BodyAnnot ).isDefined)
286
- inlineInfo.accessors
223
+ val (body, accessors) = bodyAndAccessors(sym)
224
+ if (accessors.nonEmpty) sym.updateAnnotation(ConcreteBodyAnnotation (body))
225
+ accessors
287
226
}
288
227
289
228
/** Try to inline a call to a `@inline` method. Fail with error if the maximal
0 commit comments