diff options
7 files changed, 32 insertions, 27 deletions
diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index 0f3e7648eb..29438278d0 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -60,18 +60,20 @@ abstract class TreeInfo { | DefDef(_, _, _, _, _, _) => true case ValDef(mods, _, _, rhs) => - !mods.isMutable && isPureExpr(rhs) + !mods.isMutable && isExprSafeToInline(rhs) case _ => false } - /** Is tree a stable and pure expression? - * !!! Clarification on what is meant by "pure" here would be appreciated. - * This implementation allows both modules and lazy vals, which are pure in - * the sense that they always return the same result, but which are also - * side effecting. So for now, "pure" != "not side effecting". + /** Is tree an expression which can be inlined without affecting program semantics? + * + * Note that this is not called "isExprSafeToInline" since purity (lack of side-effects) + * is not the litmus test. References to modules and lazy vals are side-effecting, + * both because side-effecting code may be executed and because the first reference + * takes a different code path than all to follow; but they are safe to inline + * because the expression result from evaluating them is always the same. */ - def isPureExpr(tree: Tree): Boolean = tree match { + def isExprSafeToInline(tree: Tree): Boolean = tree match { case EmptyTree | This(_) | Super(_, _) @@ -84,24 +86,27 @@ abstract class TreeInfo { case Select(Literal(const), name) => const.isAnyVal && (const.tpe.member(name) != NoSymbol) case Select(qual, _) => - tree.symbol.isStable && isPureExpr(qual) + tree.symbol.isStable && isExprSafeToInline(qual) case TypeApply(fn, _) => - isPureExpr(fn) + isExprSafeToInline(fn) case Apply(fn, List()) => /* Note: After uncurry, field accesses are represented as Apply(getter, Nil), * so an Apply can also be pure. * However, before typing, applications of nullary functional values are also * Apply(function, Nil) trees. To prevent them from being treated as pure, * we check that the callee is a method. */ - fn.symbol.isMethod && !fn.symbol.isLazy && isPureExpr(fn) + fn.symbol.isMethod && !fn.symbol.isLazy && isExprSafeToInline(fn) case Typed(expr, _) => - isPureExpr(expr) + isExprSafeToInline(expr) case Block(stats, expr) => - (stats forall isPureDef) && isPureExpr(expr) + (stats forall isPureDef) && isExprSafeToInline(expr) case _ => false } + @deprecated("Use isExprSafeToInline instead", "2.10.0") + def isPureExpr(tree: Tree) = isExprSafeToInline(tree) + def zipMethodParamsAndArgs(params: List[Symbol], args: List[Tree]): List[(Symbol, Tree)] = { val plen = params.length val alen = args.length diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 033b049a92..5ba7465928 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -206,7 +206,7 @@ abstract class TreeGen extends reflect.internal.TreeGen { */ def evalOnce(expr: Tree, owner: Symbol, unit: CompilationUnit)(within: (() => Tree) => Tree): Tree = { var used = false - if (treeInfo.isPureExpr(expr)) { + if (treeInfo.isExprSafeToInline(expr)) { within(() => if (used) expr.duplicate else { used = true; expr }) } else { @@ -223,7 +223,7 @@ abstract class TreeGen extends reflect.internal.TreeGen { val used = new Array[Boolean](exprs.length) var i = 0 for (expr <- exprs) { - if (treeInfo.isPureExpr(expr)) { + if (treeInfo.isExprSafeToInline(expr)) { exprs1 += { val idx = i () => if (used(idx)) expr.duplicate else { used(idx) = true; expr } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index c21977cf9c..cc87ea7e06 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -433,7 +433,7 @@ abstract class Erasure extends AddInterfaces case _ => typedPos(tree.pos)(tree.tpe.typeSymbol match { case UnitClass => - if (treeInfo isPureExpr tree) REF(BoxedUnit_UNIT) + if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT) else BLOCK(tree, REF(BoxedUnit_UNIT)) case NothingClass => tree // a non-terminating expression doesn't need boxing case x => @@ -472,7 +472,7 @@ abstract class Erasure extends AddInterfaces case _ => typedPos(tree.pos)(pt.typeSymbol match { case UnitClass => - if (treeInfo isPureExpr tree) UNIT + if (treeInfo isExprSafeToInline tree) UNIT else BLOCK(tree, UNIT) case x => assert(x != ArrayClass) diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 3965359094..864939ebfd 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -195,7 +195,7 @@ abstract class UnCurry extends InfoTransform /** Undo eta expansion for parameterless and nullary methods */ def deEta(fun: Function): Tree = fun match { - case Function(List(), Apply(expr, List())) if treeInfo.isPureExpr(expr) => + case Function(List(), Apply(expr, List())) if treeInfo.isExprSafeToInline(expr) => if (expr hasSymbolWhich (_.isLazy)) fun else @@ -477,7 +477,7 @@ abstract class UnCurry extends InfoTransform case Apply(Select(Function(vparams, body), nme.apply), args) => // if (List.forall2(vparams, args)((vparam, arg) => treeInfo.isAffineIn(body) || -// treeInfo.isPureExpr(arg))) => +// treeInfo.isExprSafeToInline(arg))) => // perform beta-reduction; this helps keep view applications small println("beta-reduce2: "+tree) withNeedLift(true) { diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index ce206b55fd..92ce0e6de4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -64,7 +64,7 @@ trait EtaExpansion { self: Analyzer => */ def liftoutPrefix(tree: Tree): Tree = { def liftout(tree: Tree): Tree = - if (treeInfo.isPureExpr(tree)) tree + if (treeInfo.isExprSafeToInline(tree)) tree else { val vname: Name = freshName() // Problem with ticket #2351 here diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index bf7cc72fab..cc88f471ab 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -217,11 +217,11 @@ trait NamesDefaults { self: Analyzer => case Select(New(tp @ Select(qual, _)), _) if isConstr => // in `new q.C()', q is always stable - assert(treeInfo.isPureExpr(qual), qual) + assert(treeInfo.isExprSafeToInline(qual), qual) // 'moduleQual' fixes #2057 blockWithoutQualifier(moduleQual(tp.pos, tp.tpe)) case Select(TypeApply(New(tp @ Select(qual, _)), _), _) if isConstr => - assert(treeInfo.isPureExpr(qual), qual) + assert(treeInfo.isExprSafeToInline(qual), qual) blockWithoutQualifier(moduleQual(tp.pos, tp.tpe)) // super constructor calls @@ -232,7 +232,7 @@ trait NamesDefaults { self: Analyzer => // self constructor calls (in secondary constructors) case Select(tp, name) if isConstr => - assert(treeInfo.isPureExpr(tp), tp) + assert(treeInfo.isExprSafeToInline(tp), tp) blockWithoutQualifier(moduleQual(tp.pos, tp.tpe)) // other method calls @@ -241,7 +241,7 @@ trait NamesDefaults { self: Analyzer => blockWithoutQualifier(None) case Select(qual, name) => - if (treeInfo.isPureExpr(qual)) + if (treeInfo.isExprSafeToInline(qual)) blockWithoutQualifier(Some(qual.duplicate)) else blockWithQualifier(qual, name) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d01cd829ac..0ecb8efea8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -202,7 +202,7 @@ trait Typers extends Modes with Adaptations { * @return ... */ def checkStable(tree: Tree): Tree = - if (treeInfo.isPureExpr(tree)) tree + if (treeInfo.isExprSafeToInline(tree)) tree else errorTree( tree, "stable identifier required, but "+tree+" found."+ @@ -223,7 +223,7 @@ trait Typers extends Modes with Adaptations { val savedSTABLE = tree.symbol getFlag STABLE tree.symbol setInfo AnyRefClass.tpe tree.symbol setFlag STABLE - val result = treeInfo.isPureExpr(tree) + val result = treeInfo.isExprSafeToInline(tree) tree.symbol setInfo savedTpe tree.symbol setFlag savedSTABLE result @@ -1976,7 +1976,7 @@ trait Typers extends Modes with Adaptations { val stats1 = typedStats(block.stats, context.owner) val expr1 = typed(block.expr, mode & ~(FUNmode | QUALmode), pt) treeCopy.Block(block, stats1, expr1) - .setType(if (treeInfo.isPureExpr(block)) expr1.tpe else expr1.tpe.deconst) + .setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst) } finally { // enable escaping privates checking from the outside and recycle // transient flag @@ -2136,7 +2136,7 @@ trait Typers extends Modes with Adaptations { private def isWarnablePureExpression(tree: Tree) = tree match { case EmptyTree | Literal(Constant(())) => false case _ => - (treeInfo isPureExpr tree) && { + (treeInfo isExprSafeToInline tree) && { val sym = tree.symbol (sym == null) || !(sym.isModule || sym.isLazy) || { debuglog("'Pure' but side-effecting expression in statement position: " + tree) |