@@ -463,29 +463,35 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
463
463
* - the class `C`
464
464
* - the arguments `args`
465
465
* - any bindings that wrap the instance creation
466
+ * - whether the instance creation is precomputed or by-name
466
467
*/
467
468
private object NewInstance {
468
- def unapply (tree : Tree )(implicit ctx : Context ): Option [(Symbol , List [Tree ], List [Tree ])] = {
469
+ def unapply (tree : Tree )(implicit ctx : Context ): Option [(Symbol , List [Tree ], List [Tree ], Boolean )] = {
469
470
def unapplyLet (bindings : List [Tree ], expr : Tree ) =
470
471
unapply(expr) map {
471
- case (cls, reduced, prefix) => (cls, reduced, bindings ::: prefix)
472
+ case (cls, reduced, prefix, precomputed ) => (cls, reduced, bindings ::: prefix, precomputed )
472
473
}
473
474
tree match {
474
475
case Apply (fn, args) =>
475
476
fn match {
476
477
case Select (New (tpt), nme.CONSTRUCTOR ) =>
477
- Some ((tpt.tpe.classSymbol, args, Nil ))
478
+ Some ((tpt.tpe.classSymbol, args, Nil , false ))
478
479
case TypeApply (Select (New (tpt), nme.CONSTRUCTOR ), _) =>
479
- Some ((tpt.tpe.classSymbol, args, Nil ))
480
+ Some ((tpt.tpe.classSymbol, args, Nil , false ))
480
481
case _ =>
481
482
val meth = fn.symbol
482
483
if (meth.name == nme.apply &&
483
484
meth.flags.is(Synthetic ) &&
484
- meth.owner.linkedClass.is(Case )) Some (meth.owner.linkedClass, args, Nil )
485
+ meth.owner.linkedClass.is(Case ))
486
+ Some (meth.owner.linkedClass, args, Nil , false )
485
487
else None
486
488
}
487
489
case Ident (_) =>
488
- inlineBindings.get(tree.symbol).flatMap(unapply)
490
+ for {
491
+ binding <- inlineBindings.get(tree.symbol)
492
+ (cls, reduced, prefix, precomputed) <- unapply(binding)
493
+ }
494
+ yield (cls, reduced, prefix, precomputed || binding.isInstanceOf [ValDef ])
489
495
case Inlined (_, bindings, expansion) =>
490
496
unapplyLet(bindings, expansion)
491
497
case Block (stats, expr) if isPureExpr(tree) =>
@@ -498,13 +504,13 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
498
504
499
505
/** If `tree` is equivalent to `new C(args).x` where class `C` does not have
500
506
* initialization code and `x` is a parameter corresponding to one of the
501
- * arguments `args`, the corresponding argument, prefixed by the evaluation
502
- * of impure arguments, otherwise `tree` itself .
507
+ * arguments `args`, the corresponding argument, otherwise `tree` itself.
508
+ * Side effects of original arguments need to be preserved .
503
509
*/
504
510
def reduceProjection (tree : Tree )(implicit ctx : Context ): Tree = {
505
511
if (ctx.debug) inlining.println(i " try reduce projection $tree" )
506
512
tree match {
507
- case Select (NewInstance (cls, args, prefix), field) if cls.isNoInitsClass =>
513
+ case Select (NewInstance (cls, args, prefix, precomputed ), field) if cls.isNoInitsClass =>
508
514
def matches (param : Symbol , selection : Symbol ): Boolean =
509
515
param == selection || {
510
516
selection.name match {
@@ -516,17 +522,25 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
516
522
}
517
523
val idx = cls.asClass.paramAccessors.indexWhere(matches(_, tree.symbol))
518
524
if (idx >= 0 && idx < args.length) {
519
- def collectImpure (from : Int , end : Int ) =
520
- (from until end).filterNot(i => isPureExpr(args(i))).toList.map(args)
521
- val leading = collectImpure(0 , idx)
522
- val trailing = collectImpure(idx + 1 , args.length)
525
+ def finish (arg : Tree ) =
526
+ new TreeTypeMap ().transform(arg) // make sure local bindings in argument have fresh symbols
527
+ .reporting(res => i " projecting $tree -> $res" , inlining)
523
528
val arg = args(idx)
524
- val argInPlace =
525
- if (trailing.isEmpty) arg
526
- else letBindUnless(TreeInfo .Pure , arg)(seq(trailing, _))
527
- val fullArg = seq(prefix, seq(leading, argInPlace))
528
- new TreeTypeMap ().transform(fullArg) // make sure local bindings in argument have fresh symbols
529
- .reporting(res => i " projecting $tree -> $res" , inlining)
529
+ if (precomputed)
530
+ if (isPureExpr(arg)) finish(arg)
531
+ else tree // nothing we can do here, projection would duplicate side effect
532
+ else {
533
+ // newInstance is evaluated in place, need to reflect side effects of
534
+ // arguments in the order they were written originally
535
+ def collectImpure (from : Int , end : Int ) =
536
+ (from until end).filterNot(i => isPureExpr(args(i))).toList.map(args)
537
+ val leading = collectImpure(0 , idx)
538
+ val trailing = collectImpure(idx + 1 , args.length)
539
+ val argInPlace =
540
+ if (trailing.isEmpty) arg
541
+ else letBindUnless(TreeInfo .Pure , arg)(seq(trailing, _))
542
+ finish(seq(prefix, seq(leading, argInPlace)))
543
+ }
530
544
}
531
545
else tree
532
546
case Block (stats, expr) if stats.forall(isPureBinding) =>
0 commit comments