@@ -15,7 +15,7 @@ import StdNames.nme
1515import Contexts .Context
1616import Names .{Name , TermName , EmptyTermName }
1717import NameOps ._
18- import NameKinds .{ClassifiedNameKind , InlineAccessorName }
18+ import NameKinds .{ClassifiedNameKind , InlineAccessorName , UniqueName }
1919import ProtoTypes .selectionProto
2020import SymDenotations .SymDenotation
2121import Annotations ._
@@ -63,23 +63,13 @@ object Inliner {
6363 // This is quite tricky, as such types can appear anywhere, including as parts
6464 // of types of other things. For the moment we do nothing and complain
6565 // at the implicit expansion site if there's a reference to an inaccessible type.
66- override def transform (tree : Tree )(implicit ctx : Context ): Tree = tree match {
67- case tree : Assign =>
68- transform(tree.lhs) match {
69- case lhs1 : RefTree =>
70- lhs1.name match {
71- case InlineAccessorName (name) =>
72- cpy.Apply (tree)(useSetter(lhs1), transform(tree.rhs) :: Nil )
73- case _ =>
74- super .transform(tree)
75- }
76- case _ =>
77- super .transform(tree)
78- }
79- case _ =>
80- super .transform(accessorIfNeeded(tree))
81- }
82-
66+ override def transform (tree : Tree )(implicit ctx : Context ): Tree =
67+ super .transform(accessorIfNeeded(tree)) match {
68+ case tree1 @ Assign (lhs : RefTree , rhs) if lhs.symbol.name.is(InlineAccessorName ) =>
69+ cpy.Apply (tree1)(useSetter(lhs), rhs :: Nil )
70+ case tree1 =>
71+ tree1
72+ }
8373 }
8474
8575 /** Adds accessors for all non-public term members accessed
@@ -114,22 +104,134 @@ object Inliner {
114104 * to have the inlined method as owner.
115105 */
116106 def registerInlineInfo (
117- sym : SymDenotation , originalBody : untpd.Tree , treeExpr : Context => Tree )(implicit ctx : Context ): Unit = {
118- sym .unforcedAnnotation(defn.BodyAnnot ) match {
107+ inlined : Symbol , originalBody : untpd.Tree , treeExpr : Context => Tree )(implicit ctx : Context ): Unit = {
108+ inlined .unforcedAnnotation(defn.BodyAnnot ) match {
119109 case Some (ann : ConcreteBodyAnnotation ) =>
120110 case Some (ann : LazyBodyAnnotation ) if ann.isEvaluated =>
121111 case _ =>
122112 if (! ctx.isAfterTyper) {
123113 val inlineCtx = ctx
124- sym .updateAnnotation(LazyBodyAnnotation { _ =>
114+ inlined .updateAnnotation(LazyBodyAnnotation { _ =>
125115 implicit val ctx = inlineCtx
126- val body = treeExpr(ctx)
127- 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
128125 })
129126 }
130127 }
131128 }
132129
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+
133235 /** `sym` has an inline method with a known body to inline (note: definitions coming
134236 * from Scala2x class files might be `@inline`, but still lack that body.
135237 */
@@ -391,17 +493,38 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
391493 case _ => tree
392494 }}
393495
394- val inlineCtx = inlineContext(call)
496+ val inlineTyper = if (meth.isTransparentMethod) new InlineTyper else new InlineReTyper
497+ val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope
498+
395499 // The complete translation maps references to `this` and parameters to
396500 // corresponding arguments or proxies on the type and term level. It also changes
397501 // the owner from the inlined method to the current owner.
398502 val inliner = new TreeTypeMap (typeMap, treeMap, meth :: Nil , ctx.owner :: Nil )(inlineCtx)
399503
400- 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+
401524 trace(i " inlining $call\n , BINDINGS = \n ${bindingsBuf.toList}% \n % \n EXPANSION = \n $expansion" , inlining, show = true ) {
402525
403526 // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
404- val expansion1 = InlineTyper .typed(expansion, pt)(inlineCtx)
527+ val expansion1 = inlineTyper .typed(expansion, pt)(inlineCtx)
405528
406529 /** All bindings in `bindingsBuf` except bindings of inlineable closures */
407530 val bindings = bindingsBuf.toList.map(_.withPos(call.pos))
@@ -421,8 +544,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
421544 */
422545 private object InlineableArg {
423546 lazy val paramProxies = paramProxy.values.toSet
424- def unapply (tree : Ident )(implicit ctx : Context ): Option [Tree ] =
425- if (paramProxies.contains(tree.tpe ))
547+ def unapply (tree : Trees . Ident [_] )(implicit ctx : Context ): Option [Tree ] =
548+ if (paramProxies.contains(tree.typeOpt ))
426549 bindingsBuf.find(_.name == tree.name) match {
427550 case Some (vdef : ValDef ) if vdef.symbol.is(Inline ) =>
428551 Some (vdef.rhs.changeOwner(vdef.symbol, ctx.owner))
@@ -433,46 +556,24 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
433556 else None
434557 }
435558
436- /** A typer for inlined code. Its purpose is:
559+ /** A base trait of InlineTyper and InlineReTyper containing operations that
560+ * work the same way for both. Beyond typing or retyping, an inline typer performs
561+ * the following functions:
562+ *
437563 * 1. Implement constant folding over inlined code
438564 * 2. Selectively expand ifs with constant conditions
439- * 3. Inline arguments that are inlineable closures
565+ * 3. Inline arguments that are by-name closures
440566 * 4. Make sure inlined code is type-correct.
441567 * 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
442568 */
443- private object InlineTyper extends ReTyper {
444-
445- override def ensureAccessible (tpe : Type , superAccess : Boolean , pos : Position )(implicit ctx : Context ): Type = {
446- tpe match {
447- case tpe @ TypeRef (pre, _) if ! tpe.symbol.isAccessibleFrom(pre, superAccess) =>
448- tpe.info match {
449- case TypeAlias (alias) => return ensureAccessible(alias, superAccess, pos)
450- case _ =>
451- }
452- case _ =>
453- }
454- super .ensureAccessible(tpe, superAccess, pos)
455- }
456-
457- override def typedIdent (tree : untpd.Ident , pt : Type )(implicit ctx : Context ) =
458- tree.asInstanceOf [tpd.Tree ] match {
459- case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
460- case _ => super .typedIdent(tree, pt)
461- }
462-
463- override def typedSelect (tree : untpd.Select , pt : Type )(implicit ctx : Context ): Tree = {
464- assert(tree.hasType, tree)
465- val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this ))
466- val res = untpd.cpy.Select (tree)(qual1, tree.name).withType(tree.typeOpt)
467- ensureAccessible(res.tpe, tree.qualifier.isInstanceOf [untpd.Super ], tree.pos)
468- res
469- }
569+ trait InlineTyping extends Typer {
470570
471571 override def typedIf (tree : untpd.If , pt : Type )(implicit ctx : Context ) = {
472572 val cond1 = typed(tree.cond, defn.BooleanType )
473573 cond1.tpe.widenTermRefExpr match {
474574 case ConstantType (Constant (condVal : Boolean )) =>
475- val selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
575+ var selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
576+ if (selected.isEmpty) selected = tpd.Literal (Constant (()))
476577 if (isIdempotentExpr(cond1)) selected
477578 else Block (cond1 :: Nil , selected)
478579 case _ =>
@@ -509,6 +610,48 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
509610 }
510611 }
511612
613+ /** A full typer used for transparent methods */
614+ private class InlineTyper extends Typer with InlineTyping {
615+
616+ override def typedTypedSplice (tree : untpd.TypedSplice )(implicit ctx : Context ): Tree =
617+ tree.splice match {
618+ case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
619+ case _ => super .typedTypedSplice(tree)
620+ }
621+ }
622+
623+ /** A re-typer used for inlined methods */
624+ private class InlineReTyper extends ReTyper with InlineTyping {
625+
626+ override def ensureAccessible (tpe : Type , superAccess : Boolean , pos : Position )(implicit ctx : Context ): Type = {
627+ tpe match {
628+ case tpe @ TypeRef (pre, _) if ! tpe.symbol.isAccessibleFrom(pre, superAccess) =>
629+ tpe.info match {
630+ case TypeAlias (alias) => return ensureAccessible(alias, superAccess, pos)
631+ case _ =>
632+ }
633+ case _ =>
634+ }
635+ super .ensureAccessible(tpe, superAccess, pos)
636+ }
637+
638+ override def typedIdent (tree : untpd.Ident , pt : Type )(implicit ctx : Context ) =
639+ tree.asInstanceOf [tpd.Tree ] match {
640+ case InlineableArg (rhs) => inlining.println(i " inline arg $tree -> $rhs" ); rhs
641+ case _ => super .typedIdent(tree, pt)
642+ }
643+
644+ override def typedSelect (tree : untpd.Select , pt : Type )(implicit ctx : Context ): Tree = {
645+ assert(tree.hasType, tree)
646+ val qual1 = typed(tree.qualifier, selectionProto(tree.name, pt, this ))
647+ val res = untpd.cpy.Select (tree)(qual1, tree.name).withType(tree.typeOpt)
648+ ensureAccessible(res.tpe, tree.qualifier.isInstanceOf [untpd.Super ], tree.pos)
649+ res
650+ }
651+
652+ override def newLikeThis : Typer = new InlineReTyper
653+ }
654+
512655 /** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings.
513656 * Inline def bindings that are used only once.
514657 */
0 commit comments