@@ -231,13 +231,125 @@ object Inliner {
231
231
val inlineCtx = ctx
232
232
inlined.updateAnnotation(LazyBodyAnnotation { _ =>
233
233
implicit val ctx = inlineCtx
234
- val body = treeExpr(ctx)
235
- if (ctx.reporter.hasErrors) body else ctx.compilationUnit.inlineAccessors.makeInlineable(body)
234
+ val rawBody = treeExpr(ctx)
235
+ val typedBody =
236
+ if (ctx.reporter.hasErrors) rawBody
237
+ else ctx.compilationUnit.inlineAccessors.makeInlineable(rawBody)
238
+ val inlineableBody =
239
+ if (inlined.isInlinedMethod) typedBody
240
+ else addReferences(inlined, originalBody, typedBody)
241
+ inlining.println(i " Body to inline for $inlined: $inlineableBody" )
242
+ inlineableBody
236
243
})
237
244
}
238
245
}
239
246
}
240
247
248
+ /** Tweak untyped tree `original` so that all external references are typed
249
+ * and it reflects the changes in the corresponding typed tree `typed` that
250
+ * make `typed` inlineable. Concretely:
251
+ *
252
+ * - all external references via identifiers or this-references are converted
253
+ * to typed splices,
254
+ * - if X gets an inline accessor in `typed`, references to X in `original`
255
+ * are converted to the inline accessor name.
256
+ */
257
+ def addReferences (inlineMethod : Symbol ,
258
+ original : untpd.Tree , typed : tpd.Tree )(implicit ctx : Context ): tpd.Tree = {
259
+
260
+ def isExternal (sym : Symbol ) = sym.exists && ! isLocal(sym, inlineMethod)
261
+
262
+ // Maps from positions to external reference types and inline selector names.
263
+ object referenced extends TreeTraverser {
264
+ val typeAtPos = mutable.Map [Position , Type ]()
265
+ val nameAtPos = mutable.Map [Position , Name ]()
266
+ val implicitSyms = mutable.Set [Symbol ]()
267
+ val implicitRefs = new mutable.ListBuffer [Tree ]
268
+ def registerIfContextualImplicit (tree : Tree ) = tree match {
269
+ case tree : RefTree
270
+ if tree.removeAttachment(ContextualImplicit ).isDefined &&
271
+ isExternal(tree.symbol) &&
272
+ ! implicitSyms.contains(tree.symbol) =>
273
+ if (tree.existsSubTree(t => isLocal(tree.symbol, inlineMethod)))
274
+ ctx.warning(" implicit reference $tree is dropped at inline site because it refers to local symbol(s)" , tree.pos)
275
+ else {
276
+ implicitSyms += tree.symbol
277
+ implicitRefs += tree
278
+ }
279
+ case _ =>
280
+ }
281
+ def traverse (tree : Tree )(implicit ctx : Context ): Unit = {
282
+ tree match {
283
+ case _ : Ident | _ : This =>
284
+ // println(i"leaf: $tree at ${tree.pos}")
285
+ if (isExternal(tree.symbol)) {
286
+ inlining.println(i " type at pos ${tree.pos.toSynthetic} = ${tree.tpe}" )
287
+ typeAtPos(tree.pos.toSynthetic) = tree.tpe
288
+ }
289
+ case _ : Select if tree.symbol.name.is(InlineAccessorName ) =>
290
+ inlining.println(i " accessor: $tree at ${tree.pos}" )
291
+ nameAtPos(tree.pos.toSynthetic) = tree.symbol.name
292
+ case _ =>
293
+ }
294
+ registerIfContextualImplicit(tree)
295
+ traverseChildren(tree)
296
+ }
297
+ }
298
+ referenced.traverse(typed)
299
+
300
+ // The untyped tree transform that applies the tweaks
301
+ object addRefs extends untpd.UntypedTreeMap {
302
+ override def transform (tree : untpd.Tree )(implicit ctx : Context ): untpd.Tree = {
303
+ def adjustLeaf (tree : untpd.Tree ): untpd.Tree = referenced.typeAtPos.get(tree.pos.toSynthetic) match {
304
+ case Some (tpe) => untpd.TypedSplice (tree.withType(tpe))
305
+ case none => tree
306
+ }
307
+ def adjustName (name : Name ) = referenced.nameAtPos.get(tree.pos.toSynthetic) match {
308
+ case Some (n) => n
309
+ case none => name
310
+ }
311
+ def adjustQualifier (tree : untpd.Tree ): untpd.Tree = tree match {
312
+ case tree @ Ident (name1) =>
313
+ referenced.typeAtPos.get(tree.pos.startPos) match {
314
+ case Some (tp : ThisType ) =>
315
+ val qual = untpd.TypedSplice (This (tp.cls).withPos(tree.pos.startPos))
316
+ cpy.Select (tree)(qual, name1)
317
+ case none =>
318
+ tree
319
+ }
320
+ case tree => tree
321
+ }
322
+ val tree1 = super .transform(tree)
323
+ tree1 match {
324
+ case This (_) =>
325
+ adjustLeaf(tree1)
326
+ case Ident (name) =>
327
+ adjustQualifier(adjustLeaf(cpy.Ident (tree1)(adjustName(name))))
328
+ case Select (pre, name) =>
329
+ cpy.Select (tree1)(pre, adjustName(name))
330
+ case tree : untpd.DerivedTypeTree =>
331
+ inlining.println(i " inlining derived $tree --> ${ctx.typer.typed(tree)}" )
332
+ untpd.TypedSplice (ctx.typer.typed(tree))
333
+ case _ =>
334
+ tree1
335
+ }
336
+ }
337
+ }
338
+ val implicitBindings =
339
+ for (iref <- referenced.implicitRefs.toList) yield {
340
+ val localImplicit = iref.symbol.asTerm.copy(
341
+ owner = inlineMethod,
342
+ name = UniqueName .fresh(iref.symbol.name.asTermName),
343
+ flags = Implicit | Method | Stable ,
344
+ info = iref.symbol.info.ensureMethodic,
345
+ coord = inlineMethod.pos).asTerm
346
+ polyDefDef(localImplicit, tps => vrefss =>
347
+ iref.appliedToTypes(tps).appliedToArgss(vrefss))
348
+ }
349
+ val untpdSplice = tpd.UntypedSplice (addRefs.transform(original)).withType(typed.tpe)
350
+ seq(implicitBindings, untpdSplice)
351
+ }
352
+
241
353
/** `sym` has an inline method with a known body to inline (note: definitions coming
242
354
* from Scala2x class files might be `@inline`, but still lack that body.
243
355
*/
@@ -499,17 +611,38 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
499
611
case _ => tree
500
612
}}
501
613
502
- val inlineCtx = inlineContext(call)
614
+ val inlineTyper = if (meth.isTransparentMethod) new InlineTyper else new InlineReTyper
615
+ val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope
616
+
503
617
// The complete translation maps references to `this` and parameters to
504
618
// corresponding arguments or proxies on the type and term level. It also changes
505
619
// the owner from the inlined method to the current owner.
506
620
val inliner = new TreeTypeMap (typeMap, treeMap, meth :: Nil , ctx.owner :: Nil )(inlineCtx)
507
621
508
- val expansion = inliner(rhsToInline.withPos(call.pos))
622
+ val expansion = inliner.transform(rhsToInline.withPos(call.pos)) match {
623
+ case Block (implicits, tpd.UntypedSplice (expansion)) =>
624
+ val prevOwners = implicits.map(_.symbol.owner).distinct
625
+ val localizer = new TreeTypeMap (oldOwners = prevOwners, newOwners = prevOwners.map(_ => ctx.owner))
626
+ val (_, implicits1) = localizer.transformDefs(implicits)
627
+ for (idef <- implicits1) {
628
+ bindingsBuf += idef.withType(idef.symbol.typeRef).asInstanceOf [ValOrDefDef ]
629
+ // Note: Substituting new symbols does not automatically lead to good prefixes
630
+ // if the previous symbol was owned by a class. That's why we need to set the type
631
+ // of `idef` explicitly. It would be nice if substituters were smarter, but
632
+ // it seems non-trivial to come up with rules that work in
633
+ inlineCtx.enter(idef.symbol)
634
+ }
635
+ expansion
636
+ case tpd.UntypedSplice (expansion) =>
637
+ expansion
638
+ case expansion =>
639
+ expansion
640
+ }
641
+
509
642
trace(i " inlining $call\n , BINDINGS = \n ${bindingsBuf.toList}% \n % \n EXPANSION = \n $expansion" , inlining, show = true ) {
510
643
511
644
// The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
512
- val expansion1 = InlineTyper .typed(expansion, pt)(inlineCtx)
645
+ val expansion1 = inlineTyper .typed(expansion, pt)(inlineCtx)
513
646
514
647
/** All bindings in `bindingsBuf` except bindings of inlineable closures */
515
648
val bindings = bindingsBuf.toList.map(_.withPos(call.pos))
@@ -529,8 +662,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
529
662
*/
530
663
private object InlineableArg {
531
664
lazy val paramProxies = paramProxy.values.toSet
532
- def unapply (tree : Ident )(implicit ctx : Context ): Option [Tree ] =
533
- if (paramProxies.contains(tree.tpe ))
665
+ def unapply (tree : Trees . Ident [_] )(implicit ctx : Context ): Option [Tree ] =
666
+ if (paramProxies.contains(tree.typeOpt ))
534
667
bindingsBuf.find(_.name == tree.name) match {
535
668
case Some (vdef : ValDef ) if vdef.symbol.is(Inline ) =>
536
669
Some (vdef.rhs.changeOwner(vdef.symbol, ctx.owner))
@@ -541,46 +674,24 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
541
674
else None
542
675
}
543
676
544
- /** A typer for inlined code. Its purpose is:
677
+ /** A base trait of InlineTyper and InlineReTyper containing operations that
678
+ * work the same way for both. Beyond typing or retyping, an inline typer performs
679
+ * the following functions:
680
+ *
545
681
* 1. Implement constant folding over inlined code
546
682
* 2. Selectively expand ifs with constant conditions
547
683
* 3. Inline arguments that are by-name closures
548
684
* 4. Make sure inlined code is type-correct.
549
685
* 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
550
686
*/
551
- private object InlineTyper extends ReTyper {
552
-
553
- override def ensureAccessible (tpe : Type , superAccess : Boolean , pos : Position )(implicit ctx : Context ): Type = {
554
- tpe match {
555
- case tpe @ TypeRef (pre, _) if ! tpe.symbol.isAccessibleFrom(pre, superAccess) =>
556
- tpe.info match {
557
- case TypeAlias (alias) => return ensureAccessible(alias, superAccess, pos)
558
- case _ =>
559
- }
560
- case _ =>
561
- }
562
- super .ensureAccessible(tpe, superAccess, pos)
563
- }
564
-
565
- override def typedIdent (tree : untpd.Ident , pt : Type )(implicit ctx : Context ) =
566
- tree.asInstanceOf [tpd.Tree ] match {
567
- case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
568
- case _ => super .typedIdent(tree, pt)
569
- }
570
-
571
- override def typedSelect (tree : untpd.Select , pt : Type )(implicit ctx : Context ): Tree = {
572
- assert(tree.hasType, tree)
573
- val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this ))
574
- val res = untpd.cpy.Select (tree)(qual1, tree.name).withType(tree.typeOpt)
575
- ensureAccessible(res.tpe, tree.qualifier.isInstanceOf [untpd.Super ], tree.pos)
576
- res
577
- }
687
+ trait InlineTyping extends Typer {
578
688
579
689
override def typedIf (tree : untpd.If , pt : Type )(implicit ctx : Context ) = {
580
690
val cond1 = typed(tree.cond, defn.BooleanType )
581
691
cond1.tpe.widenTermRefExpr match {
582
692
case ConstantType (Constant (condVal : Boolean )) =>
583
- val selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
693
+ var selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
694
+ if (selected.isEmpty) selected = tpd.Literal (Constant (()))
584
695
if (isIdempotentExpr(cond1)) selected
585
696
else Block (cond1 :: Nil , selected)
586
697
case _ =>
@@ -628,6 +739,48 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
628
739
}
629
740
}
630
741
742
+ /** A full typer used for transparent methods */
743
+ private class InlineTyper extends Typer with InlineTyping {
744
+
745
+ override def typedTypedSplice (tree : untpd.TypedSplice )(implicit ctx : Context ): Tree =
746
+ tree.splice match {
747
+ case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
748
+ case _ => super .typedTypedSplice(tree)
749
+ }
750
+ }
751
+
752
+ /** A re-typer used for inlined methods */
753
+ private class InlineReTyper extends ReTyper with InlineTyping {
754
+
755
+ override def ensureAccessible (tpe : Type , superAccess : Boolean , pos : Position )(implicit ctx : Context ): Type = {
756
+ tpe match {
757
+ case tpe @ TypeRef (pre, _) if ! tpe.symbol.isAccessibleFrom(pre, superAccess) =>
758
+ tpe.info match {
759
+ case TypeAlias (alias) => return ensureAccessible(alias, superAccess, pos)
760
+ case _ =>
761
+ }
762
+ case _ =>
763
+ }
764
+ super .ensureAccessible(tpe, superAccess, pos)
765
+ }
766
+
767
+ override def typedIdent (tree : untpd.Ident , pt : Type )(implicit ctx : Context ) =
768
+ tree.asInstanceOf [tpd.Tree ] match {
769
+ case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
770
+ case _ => super .typedIdent(tree, pt)
771
+ }
772
+
773
+ override def typedSelect (tree : untpd.Select , pt : Type )(implicit ctx : Context ): Tree = {
774
+ assert(tree.hasType, tree)
775
+ val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this ))
776
+ val res = untpd.cpy.Select (tree)(qual1, tree.name).withType(tree.typeOpt)
777
+ ensureAccessible(res.tpe, tree.qualifier.isInstanceOf [untpd.Super ], tree.pos)
778
+ res
779
+ }
780
+
781
+ override def newLikeThis : Typer = new InlineReTyper
782
+ }
783
+
631
784
/** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings.
632
785
* Inline def bindings that are used only once.
633
786
*/
0 commit comments