@@ -104,22 +104,134 @@ object Inliner {
104
104
* to have the inlined method as owner.
105
105
*/
106
106
def registerInlineInfo (
107
- sym : SymDenotation , originalBody : untpd.Tree , treeExpr : Context => Tree )(implicit ctx : Context ): Unit = {
108
- sym .unforcedAnnotation(defn.BodyAnnot ) match {
107
+ inlined : Symbol , originalBody : untpd.Tree , treeExpr : Context => Tree )(implicit ctx : Context ): Unit = {
108
+ inlined .unforcedAnnotation(defn.BodyAnnot ) match {
109
109
case Some (ann : ConcreteBodyAnnotation ) =>
110
110
case Some (ann : LazyBodyAnnotation ) if ann.isEvaluated =>
111
111
case _ =>
112
112
if (! ctx.isAfterTyper) {
113
113
val inlineCtx = ctx
114
- sym .updateAnnotation(LazyBodyAnnotation { _ =>
114
+ inlined .updateAnnotation(LazyBodyAnnotation { _ =>
115
115
implicit val ctx = inlineCtx
116
- val body = treeExpr(ctx)
117
- if (ctx.reporter.hasErrors) body else ctx.compilationUnit.inlineAccessors.makeInlineable(body)
116
+ val rawBody = treeExpr(ctx)
117
+ val typedBody =
118
+ if (ctx.reporter.hasErrors) rawBody
119
+ else ctx.compilationUnit.inlineAccessors.makeInlineable(rawBody)
120
+ val inlineableBody =
121
+ if (inlined.isInlinedMethod) typedBody
122
+ else addReferences(inlined, originalBody, typedBody)
123
+ inlining.println(i " Body to inline for $inlined: $inlineableBody" )
124
+ inlineableBody
118
125
})
119
126
}
120
127
}
121
128
}
122
129
130
+ /** Tweak untyped tree `original` so that all external references are typed
131
+ * and it reflects the changes in the corresponding typed tree `typed` that
132
+ * make `typed` inlineable. Concretely:
133
+ *
134
+ * - all external references via identifiers or this-references are converted
135
+ * to typed splices,
136
+ * - if X gets an inline accessor in `typed`, references to X in `original`
137
+ * are converted to the inline accessor name.
138
+ */
139
+ def addReferences (inlineMethod : Symbol ,
140
+ original : untpd.Tree , typed : tpd.Tree )(implicit ctx : Context ): tpd.Tree = {
141
+
142
+ def isExternal (sym : Symbol ) = sym.exists && ! isLocal(sym, inlineMethod)
143
+
144
+ // Maps from positions to external reference types and inline selector names.
145
+ object referenced extends TreeTraverser {
146
+ val typeAtPos = mutable.Map [Position , Type ]()
147
+ val nameAtPos = mutable.Map [Position , Name ]()
148
+ val implicitSyms = mutable.Set [Symbol ]()
149
+ val implicitRefs = new mutable.ListBuffer [Tree ]
150
+ def registerIfContextualImplicit (tree : Tree ) = tree match {
151
+ case tree : RefTree
152
+ if tree.removeAttachment(ContextualImplicit ).isDefined &&
153
+ isExternal(tree.symbol) &&
154
+ ! implicitSyms.contains(tree.symbol) =>
155
+ if (tree.existsSubTree(t => isLocal(tree.symbol, inlineMethod)))
156
+ ctx.warning(" implicit reference $tree is dropped at inline site because it refers to local symbol(s)" , tree.pos)
157
+ else {
158
+ implicitSyms += tree.symbol
159
+ implicitRefs += tree
160
+ }
161
+ case _ =>
162
+ }
163
+ def traverse (tree : Tree )(implicit ctx : Context ): Unit = {
164
+ tree match {
165
+ case _ : Ident | _ : This =>
166
+ // println(i"leaf: $tree at ${tree.pos}")
167
+ if (isExternal(tree.symbol)) {
168
+ inlining.println(i " type at pos ${tree.pos.toSynthetic} = ${tree.tpe}" )
169
+ typeAtPos(tree.pos.toSynthetic) = tree.tpe
170
+ }
171
+ case _ : Select if tree.symbol.name.is(InlineAccessorName ) =>
172
+ inlining.println(i " accessor: $tree at ${tree.pos}" )
173
+ nameAtPos(tree.pos.toSynthetic) = tree.symbol.name
174
+ case _ =>
175
+ }
176
+ registerIfContextualImplicit(tree)
177
+ traverseChildren(tree)
178
+ }
179
+ }
180
+ referenced.traverse(typed)
181
+
182
+ // The untyped tree transform that applies the tweaks
183
+ object addRefs extends untpd.UntypedTreeMap {
184
+ override def transform (tree : untpd.Tree )(implicit ctx : Context ): untpd.Tree = {
185
+ def adjustLeaf (tree : untpd.Tree ): untpd.Tree = referenced.typeAtPos.get(tree.pos.toSynthetic) match {
186
+ case Some (tpe) => untpd.TypedSplice (tree.withType(tpe))
187
+ case none => tree
188
+ }
189
+ def adjustName (name : Name ) = referenced.nameAtPos.get(tree.pos.toSynthetic) match {
190
+ case Some (n) => n
191
+ case none => name
192
+ }
193
+ def adjustQualifier (tree : untpd.Tree ): untpd.Tree = tree match {
194
+ case tree @ Ident (name1) =>
195
+ referenced.typeAtPos.get(tree.pos.startPos) match {
196
+ case Some (tp : ThisType ) =>
197
+ val qual = untpd.TypedSplice (This (tp.cls).withPos(tree.pos.startPos))
198
+ cpy.Select (tree)(qual, name1)
199
+ case none =>
200
+ tree
201
+ }
202
+ case tree => tree
203
+ }
204
+ val tree1 = super .transform(tree)
205
+ tree1 match {
206
+ case This (_) =>
207
+ adjustLeaf(tree1)
208
+ case Ident (name) =>
209
+ adjustQualifier(adjustLeaf(cpy.Ident (tree1)(adjustName(name))))
210
+ case Select (pre, name) =>
211
+ cpy.Select (tree1)(pre, adjustName(name))
212
+ case tree : untpd.DerivedTypeTree =>
213
+ inlining.println(i " inlining derived $tree --> ${ctx.typer.typed(tree)}" )
214
+ untpd.TypedSplice (ctx.typer.typed(tree))
215
+ case _ =>
216
+ tree1
217
+ }
218
+ }
219
+ }
220
+ val implicitBindings =
221
+ for (iref <- referenced.implicitRefs.toList) yield {
222
+ val localImplicit = iref.symbol.asTerm.copy(
223
+ owner = inlineMethod,
224
+ name = UniqueName .fresh(iref.symbol.name.asTermName),
225
+ flags = Implicit | Method | Stable ,
226
+ info = iref.symbol.info.ensureMethodic,
227
+ coord = inlineMethod.pos).asTerm
228
+ polyDefDef(localImplicit, tps => vrefss =>
229
+ iref.appliedToTypes(tps).appliedToArgss(vrefss))
230
+ }
231
+ val untpdSplice = tpd.UntypedSplice (addRefs.transform(original)).withType(typed.tpe)
232
+ seq(implicitBindings, untpdSplice)
233
+ }
234
+
123
235
/** `sym` has an inline method with a known body to inline (note: definitions coming
124
236
* from Scala2x class files might be `@inline`, but still lack that body.
125
237
*/
@@ -383,12 +495,32 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
383
495
384
496
val inlineTyper = new InlineTyper
385
497
val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope
498
+
386
499
// The complete translation maps references to `this` and parameters to
387
500
// corresponding arguments or proxies on the type and term level. It also changes
388
501
// the owner from the inlined method to the current owner.
389
502
val inliner = new TreeTypeMap (typeMap, treeMap, meth :: Nil , ctx.owner :: Nil )(inlineCtx)
390
503
391
- val expansion = inliner(rhsToInline.withPos(call.pos))
504
+ val expansion = inliner.transform(rhsToInline.withPos(call.pos)) match {
505
+ case Block (implicits, tpd.UntypedSplice (expansion)) =>
506
+ val prevOwners = implicits.map(_.symbol.owner).distinct
507
+ val localizer = new TreeTypeMap (oldOwners = prevOwners, newOwners = prevOwners.map(_ => ctx.owner))
508
+ val (_, implicits1) = localizer.transformDefs(implicits)
509
+ for (idef <- implicits1) {
510
+ bindingsBuf += idef.withType(idef.symbol.typeRef).asInstanceOf [ValOrDefDef ]
511
+ // Note: Substituting new symbols does not automatically lead to good prefixes
512
+ // if the previous symbol was owned by a class. That's why we need to set the type
513
+ // of `idef` explicitly. It would be nice if substituters were smarter, but
514
+ // it seems non-trivial to come up with rules that work in
515
+ inlineCtx.enter(idef.symbol)
516
+ }
517
+ expansion
518
+ case tpd.UntypedSplice (expansion) =>
519
+ expansion
520
+ case expansion =>
521
+ expansion
522
+ }
523
+
392
524
trace(i " inlining $call\n , BINDINGS = \n ${bindingsBuf.toList}% \n % \n EXPANSION = \n $expansion" , inlining, show = true ) {
393
525
394
526
// The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
0 commit comments