@@ -218,6 +218,31 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
218
218
private def newSym (name : Name , flags : FlagSet , info : Type ): Symbol =
219
219
ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos)
220
220
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
+
221
246
/** Populate `paramBinding` and `bindingsBuf` by matching parameters with
222
247
* corresponding arguments. `bindingbuf` will be further extended later by
223
248
* proxies to this-references.
@@ -230,20 +255,9 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
230
255
computeParamBindings(tp.resultType, Nil , argss)
231
256
case tp : MethodType =>
232
257
(tp.paramNames, tp.paramInfos, argss.head).zipped.foreach { (name, paramtp, arg) =>
233
- def isByName = paramtp.dealias.isInstanceOf [ExprType ]
234
258
paramBinding(name) = arg.tpe.dealias match {
235
259
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
247
261
}
248
262
}
249
263
computeParamBindings(tp.resultType, targs, argss.tail)
@@ -265,7 +279,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
265
279
* The proxy is not yet entered in `bindingsBuf`; that will come later.
266
280
* 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored
267
281
* 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
269
283
* references of a method are (we only know the method's type, but that contains TypeParamRefs
270
284
* and MethodParams, not TypeRefs or TermRefs.
271
285
*/
@@ -374,16 +388,15 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
374
388
// The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
375
389
val expansion1 = InlineTyper .typed(expansion, pt)(inlineCtx)
376
390
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
-
383
391
/** 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" )
385
396
386
- tpd.Inlined (call, bindings, expansion1)
397
+ val (finalBindings, finalExpansion) = dropUnusedDefs(bindings, expansion1)
398
+
399
+ tpd.Inlined (call, finalBindings, finalExpansion)
387
400
}
388
401
}
389
402
@@ -414,8 +427,6 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
414
427
*/
415
428
private object InlineTyper extends ReTyper {
416
429
417
- var retainedInlineables = Set [Symbol ]()
418
-
419
430
override def ensureAccessible (tpe : Type , superAccess : Boolean , pos : Position )(implicit ctx : Context ): Type = {
420
431
tpe match {
421
432
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) {
455
466
}
456
467
}
457
468
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
465
491
}
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
+ }
466
567
}
467
568
}
0 commit comments