@@ -366,31 +366,27 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
366366 // But if we do that the repl/vars test break. Need to figure out why that's the case.
367367 }
368368
369- /** The purity level of this expression.
370- * @return SimplyPure if expression has no side effects and cannot contain local definitions
371- * Pure if expression has no side effects
372- * Idempotent if running the expression a second time has no side effects
373- * Impure otherwise
369+ /** The purity level of this expression. See docs for PurityLevel for what that means
374370 *
375- * Note that purity and idempotency are different. References to modules and lazy
376- * vals are impure (side-effecting) both because side-effecting code may be executed and because the first reference
371+ * Note that purity and idempotency are treated differently.
372+ * References to modules and lazy vals are impure (side-effecting) both because
373+ * side-effecting code may be executed and because the first reference
377374 * takes a different code path than all to follow; but they are idempotent
378375 * because running the expression a second time gives the cached result.
379376 */
380377 def exprPurity (tree : Tree )(implicit ctx : Context ): PurityLevel = unsplice(tree) match {
381378 case EmptyTree
382379 | This (_)
383380 | Super (_, _)
384- | Literal (_)
385- | Closure (_, _, _) =>
386- SimplyPure
381+ | Literal (_) =>
382+ PurePath
387383 case Ident (_) =>
388384 refPurity(tree)
389385 case Select (qual, _) =>
390386 if (tree.symbol.is(Erased )) Pure
391- else refPurity(tree). min( exprPurity(qual) )
392- case New (_) =>
393- SimplyPure
387+ else refPurity(tree) ` min` exprPurity(qual)
388+ case New (_) | Closure (_, _, _) =>
389+ Pure
394390 case TypeApply (fn, _) =>
395391 if (fn.symbol.is(Erased )) Pure else exprPurity(fn)
396392 case Apply (fn, args) =>
@@ -416,37 +412,49 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
416412 Impure
417413 }
418414
419- private def minOf (l0 : PurityLevel , ls : List [PurityLevel ]) = (l0 /: ls)(_ min _)
415+ private def minOf (l0 : PurityLevel , ls : List [PurityLevel ]) = (l0 /: ls)(_ `min` _)
416+
417+ def isPurePath (tree : Tree )(implicit ctx : Context ): Boolean = tree.tpe match {
418+ case tpe : ConstantType => exprPurity(tree) >= Pure
419+ case _ => exprPurity(tree) == PurePath
420+ }
421+
422+ def isPureExpr (tree : Tree )(implicit ctx : Context ): Boolean =
423+ exprPurity(tree) >= Pure
424+
425+ def isIdempotentPath (tree : Tree )(implicit ctx : Context ): Boolean = tree.tpe match {
426+ case tpe : ConstantType => exprPurity(tree) >= Idempotent
427+ case _ => exprPurity(tree) >= IdempotentPath
428+ }
420429
421- def isSimplyPure (tree : Tree )(implicit ctx : Context ): Boolean = exprPurity(tree) == SimplyPure
422- def isPureExpr (tree : Tree )(implicit ctx : Context ): Boolean = exprPurity(tree) >= Pure
423- def isIdempotentExpr (tree : Tree )(implicit ctx : Context ): Boolean = exprPurity(tree) >= Idempotent
430+ def isIdempotentExpr (tree : Tree )(implicit ctx : Context ): Boolean =
431+ exprPurity(tree) >= Idempotent
424432
425433 def isPureBinding (tree : Tree )(implicit ctx : Context ): Boolean = statPurity(tree) >= Pure
426434
427435 /** The purity level of this reference.
428436 * @return
429- * SimplyPure if reference is (nonlazy and stable) or to a parameterized function
430- * Idempotent if reference is lazy and stable
431- * Impure otherwise
437+ * PurePath if reference is (nonlazy and stable) or to a parameterized function
438+ * IdempotentPath if reference is lazy and stable
439+ * Impure otherwise
432440 * @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable
433441 * flags set.
434442 */
435443 def refPurity (tree : Tree )(implicit ctx : Context ): PurityLevel = {
436444 val sym = tree.symbol
437445 if (! tree.hasType) Impure
438- else if (! tree.tpe.widen.isParameterless || sym.isEffectivelyErased) SimplyPure
446+ else if (! tree.tpe.widen.isParameterless || sym.isEffectivelyErased) PurePath
439447 else if (! sym.isStableMember) Impure
440448 else if (sym.is(Module ))
441- if (sym.moduleClass.isNoInitsClass) Pure else Idempotent
442- else if (sym.is(Lazy )) Idempotent
443- else SimplyPure
449+ if (sym.moduleClass.isNoInitsClass) PurePath else IdempotentPath
450+ else if (sym.is(Lazy )) IdempotentPath
451+ else PurePath
444452 }
445453
446454 def isPureRef (tree : Tree )(implicit ctx : Context ): Boolean =
447- refPurity(tree) == SimplyPure
455+ refPurity(tree) == PurePath
448456 def isIdempotentRef (tree : Tree )(implicit ctx : Context ): Boolean =
449- refPurity(tree) >= Idempotent
457+ refPurity(tree) >= IdempotentPath
450458
451459 /** (1) If `tree` is a constant expression, its value as a Literal,
452460 * or `tree` itself otherwise.
@@ -840,13 +848,29 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
840848}
841849
842850object TreeInfo {
851+ /** A purity level is represented as a bitset (expressed as an Int) */
843852 class PurityLevel (val x : Int ) extends AnyVal {
844- def >= (that : PurityLevel ): Boolean = x >= that.x
845- def min (that : PurityLevel ): PurityLevel = new PurityLevel (x min that.x)
853+ /** `this` contains the bits of `that` */
854+ def >= (that : PurityLevel ): Boolean = (x & that.x) == that.x
855+
856+ /** The intersection of the bits of `this` and `that` */
857+ def min (that : PurityLevel ): PurityLevel = new PurityLevel (x & that.x)
846858 }
847859
848- val SimplyPure : PurityLevel = new PurityLevel (3 )
849- val Pure : PurityLevel = new PurityLevel (2 )
860+ /** An expression is a stable path. Requires that expression is at least idempotent */
861+ val Path : PurityLevel = new PurityLevel (4 )
862+
863+ /** The expression has no side effects */
864+ val Pure : PurityLevel = new PurityLevel (3 )
865+
866+ /** Running the expression a second time has no side effects. Implied by `Pure`. */
850867 val Idempotent : PurityLevel = new PurityLevel (1 )
868+
851869 val Impure : PurityLevel = new PurityLevel (0 )
870+
871+ /** A stable path that is evaluated without side effects */
872+ val PurePath : PurityLevel = new PurityLevel (Pure .x | Path .x)
873+
874+ /** A stable path that is also idempotent */
875+ val IdempotentPath : PurityLevel = new PurityLevel (Idempotent .x | Path .x)
852876}
0 commit comments