@@ -218,6 +218,31 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
218218 private def newSym (name : Name , flags : FlagSet , info : Type ): Symbol =
219219 ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos)
220220
221+ /** A binding for the parameter of an inlined method. This is a `val` def for
222+ * by-value parameters and a `def` def for by-name parameters. `val` defs inherit
223+ * inline annotations from their parameters. The generated `def` is appended
224+ * to `bindingsBuf`.
225+ * @param name the name of the parameter
226+ * @param paramtp the type of the parameter
227+ * @param arg the argument corresponding to the parameter
228+ * @param bindingsBuf the buffer to which the definition should be appended
229+ */
230+ private def paramBindingDef (name : Name , paramtp : Type , arg : Tree ,
231+ bindingsBuf : mutable.ListBuffer [ValOrDefDef ]): ValOrDefDef = {
232+ val argtpe = arg.tpe.dealias
233+ def isByName = paramtp.dealias.isInstanceOf [ExprType ]
234+ val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot )) Inline else EmptyFlags
235+ val (bindingFlags, bindingType) =
236+ if (isByName) (Method , ExprType (argtpe.widen))
237+ else (inlineFlag, argtpe.widen)
238+ val boundSym = newSym(name, bindingFlags, bindingType).asTerm
239+ val binding =
240+ if (isByName) DefDef (boundSym, arg.changeOwner(ctx.owner, boundSym))
241+ else ValDef (boundSym, arg)
242+ bindingsBuf += binding
243+ binding
244+ }
245+
221246 /** Populate `paramBinding` and `bindingsBuf` by matching parameters with
222247 * corresponding arguments. `bindingbuf` will be further extended later by
223248 * proxies to this-references.
@@ -230,20 +255,9 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
230255 computeParamBindings(tp.resultType, Nil , argss)
231256 case tp : MethodType =>
232257 (tp.paramNames, tp.paramInfos, argss.head).zipped.foreach { (name, paramtp, arg) =>
233- def isByName = paramtp.dealias.isInstanceOf [ExprType ]
234258 paramBinding(name) = arg.tpe.dealias match {
235259 case _ : SingletonType if isIdempotentExpr(arg) => arg.tpe
236- case argtpe =>
237- val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot )) Inline else EmptyFlags
238- val (bindingFlags, bindingType) =
239- if (isByName) (inlineFlag | Method , ExprType (argtpe.widen))
240- else (inlineFlag, argtpe.widen)
241- val boundSym = newSym(name, bindingFlags, bindingType).asTerm
242- val binding =
243- if (isByName) DefDef (boundSym, arg.changeOwner(ctx.owner, boundSym))
244- else ValDef (boundSym, arg)
245- bindingsBuf += binding
246- boundSym.termRef
260+ case _ => paramBindingDef(name, paramtp, arg, bindingsBuf).symbol.termRef
247261 }
248262 }
249263 computeParamBindings(tp.resultType, targs, argss.tail)
@@ -265,7 +279,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
265279 * The proxy is not yet entered in `bindingsBuf`; that will come later.
266280 * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored
267281 * in `paramNames` under the parameter's name. This roundabout way to bind parameter
268- * references to proxies is done because we not known a priori what the parameter
282+ * references to proxies is done because we don't know a priori what the parameter
269283 * references of a method are (we only know the method's type, but that contains TypeParamRefs
270284 * and MethodParams, not TypeRefs or TermRefs.
271285 */
@@ -374,16 +388,15 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
374388 // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
375389 val expansion1 = InlineTyper .typed(expansion, pt)(inlineCtx)
376390
377- /** Does given definition bind a closure that will be inlined? */
378- def bindsDeadInlineable (defn : ValOrDefDef ) = Ident (defn.symbol.termRef) match {
379- case InlineableArg (_) => ! InlineTyper .retainedInlineables.contains(defn.symbol)
380- case _ => false
381- }
382-
383391 /** All bindings in `bindingsBuf` except bindings of inlineable closures */
384- val bindings = bindingsBuf.toList.filterNot(bindsDeadInlineable).map(_.withPos(call.pos))
392+ val bindings = bindingsBuf.toList.map(_.withPos(call.pos))
393+
394+ inlining.println(i " original bindings = $bindings% \n % " )
395+ inlining.println(i " original expansion = $expansion1" )
385396
386- tpd.Inlined (call, bindings, expansion1)
397+ val (finalBindings, finalExpansion) = dropUnusedDefs(bindings, expansion1)
398+
399+ tpd.Inlined (call, finalBindings, finalExpansion)
387400 }
388401 }
389402
@@ -414,8 +427,6 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
414427 */
415428 private object InlineTyper extends ReTyper {
416429
417- var retainedInlineables = Set [Symbol ]()
418-
419430 override def ensureAccessible (tpe : Type , superAccess : Boolean , pos : Position )(implicit ctx : Context ): Type = {
420431 tpe match {
421432 case tpe @ TypeRef (pre, _) if ! tpe.symbol.isAccessibleFrom(pre, superAccess) =>
@@ -455,13 +466,103 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
455466 }
456467 }
457468
458- override def typedApply (tree : untpd.Apply , pt : Type )(implicit ctx : Context ) =
459- tree.asInstanceOf [tpd.Tree ] match {
460- case Apply (Select (InlineableArg (closure(_, fn, _)), nme.apply), args) =>
461- inlining.println(i " reducing $tree with closure $fn" )
462- typed(fn.appliedToArgs(args), pt)
463- case _ =>
464- super .typedApply(tree, pt)
469+ override def typedApply (tree : untpd.Apply , pt : Type )(implicit ctx : Context ) = {
470+
471+ def betaReduce (tree : Tree ) = tree match {
472+ case Apply (Select (cl @ closureDef(ddef), nme.apply), args) =>
473+ ddef.tpe.widen match {
474+ case mt : MethodType if ddef.vparamss.head.length == args.length =>
475+ val bindingsBuf = new mutable.ListBuffer [ValOrDefDef ]
476+ val argSyms = (mt.paramNames, mt.paramInfos, args).zipped.map { (name, paramtp, arg) =>
477+ arg.tpe.dealias match {
478+ case ref @ TermRef (NoPrefix , _) => ref.symbol
479+ case _ => paramBindingDef(name, paramtp, arg, bindingsBuf).symbol
480+ }
481+ }
482+ val expander = new TreeTypeMap (
483+ oldOwners = ddef.symbol :: Nil ,
484+ newOwners = ctx.owner :: Nil ,
485+ substFrom = ddef.vparamss.head.map(_.symbol),
486+ substTo = argSyms)
487+ Block (bindingsBuf.toList, expander.transform(ddef.rhs))
488+ case _ => tree
489+ }
490+ case _ => tree
465491 }
492+
493+ betaReduce(super .typedApply(tree, pt))
494+ }
495+ }
496+
497+ /** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings.
498+ * Inline def bindings that are used only once.
499+ */
500+ def dropUnusedDefs (bindings : List [ValOrDefDef ], tree : Tree )(implicit ctx : Context ): (List [ValOrDefDef ], Tree ) = {
501+ val refCount = newMutableSymbolMap[Int ]
502+ val bindingOfSym = newMutableSymbolMap[ValOrDefDef ]
503+ def isInlineable (binding : ValOrDefDef ) = binding match {
504+ case DefDef (_, Nil , Nil , _, _) => true
505+ case vdef @ ValDef (_, _, _) => isPureExpr(vdef.rhs)
506+ case _ => false
507+ }
508+ for (binding <- bindings if isInlineable(binding)) {
509+ refCount(binding.symbol) = 0
510+ bindingOfSym(binding.symbol) = binding
511+ }
512+ val countRefs = new TreeTraverser {
513+ override def traverse (t : Tree )(implicit ctx : Context ) = {
514+ t match {
515+ case t : RefTree =>
516+ refCount.get(t.symbol) match {
517+ case Some (x) => refCount(t.symbol) = x + 1
518+ case none =>
519+ }
520+ case _ : New | _ : TypeTree =>
521+ // println(i"refcount ${t.tpe}")
522+ t.tpe.foreachPart {
523+ case ref : TermRef =>
524+ refCount.get(ref.symbol) match {
525+ case Some (x) => refCount(ref.symbol) = x + 2
526+ case none =>
527+ }
528+ case _ =>
529+ }
530+ case _ =>
531+ }
532+ traverseChildren(t)
533+ }
534+ }
535+ countRefs.traverse(tree)
536+ for (binding <- bindings) countRefs.traverse(binding.rhs)
537+ val inlineBindings = new TreeMap {
538+ override def transform (t : Tree )(implicit ctx : Context ) =
539+ super .transform {
540+ t match {
541+ case t : RefTree =>
542+ val sym = t.symbol
543+ refCount.get(sym) match {
544+ case Some (1 ) if sym.is(Method ) =>
545+ bindingOfSym(sym).rhs.changeOwner(sym, ctx.owner)
546+ case none => t
547+ }
548+ case _ => t
549+ }
550+ }
551+ }
552+ def retain (binding : ValOrDefDef ) = refCount.get(binding.symbol) match {
553+ case Some (x) => x > 1 || x == 1 && ! binding.symbol.is(Method )
554+ case none => true
555+ }
556+ val retained = bindings.filterConserve(retain)
557+ if (retained `eq` bindings) {
558+ // println(i"DONE\n${bindings}%\n% ;;;\n ${tree}")
559+ (bindings, tree)
560+ }
561+ else {
562+ val expanded = inlineBindings.transform(tree)
563+ // println(i"ref counts: ${refCount.toMap map { case (sym, count) => i"$sym -> $count" }}")
564+ // println(i"""MAPPING\n${bindings}%\n% ;;;\n ${tree} \n------->\n${retained}%\n%;;;\n ${expanded} """)
565+ dropUnusedDefs(retained, expanded)
566+ }
466567 }
467568}
0 commit comments