@@ -366,31 +366,27 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
366
366
// But if we do that the repl/vars test break. Need to figure out why that's the case.
367
367
}
368
368
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
374
370
*
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
377
374
* takes a different code path than all to follow; but they are idempotent
378
375
* because running the expression a second time gives the cached result.
379
376
*/
380
377
def exprPurity (tree : Tree )(implicit ctx : Context ): PurityLevel = unsplice(tree) match {
381
378
case EmptyTree
382
379
| This (_)
383
380
| Super (_, _)
384
- | Literal (_)
385
- | Closure (_, _, _) =>
386
- SimplyPure
381
+ | Literal (_) =>
382
+ PurePath
387
383
case Ident (_) =>
388
384
refPurity(tree)
389
385
case Select (qual, _) =>
390
386
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
394
390
case TypeApply (fn, _) =>
395
391
if (fn.symbol.is(Erased )) Pure else exprPurity(fn)
396
392
case Apply (fn, args) =>
@@ -416,37 +412,49 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
416
412
Impure
417
413
}
418
414
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
+ }
420
429
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
424
432
425
433
def isPureBinding (tree : Tree )(implicit ctx : Context ): Boolean = statPurity(tree) >= Pure
426
434
427
435
/** The purity level of this reference.
428
436
* @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
432
440
* @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable
433
441
* flags set.
434
442
*/
435
443
def refPurity (tree : Tree )(implicit ctx : Context ): PurityLevel = {
436
444
val sym = tree.symbol
437
445
if (! tree.hasType) Impure
438
- else if (! tree.tpe.widen.isParameterless || sym.isEffectivelyErased) SimplyPure
446
+ else if (! tree.tpe.widen.isParameterless || sym.isEffectivelyErased) PurePath
439
447
else if (! sym.isStableMember) Impure
440
448
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
444
452
}
445
453
446
454
def isPureRef (tree : Tree )(implicit ctx : Context ): Boolean =
447
- refPurity(tree) == SimplyPure
455
+ refPurity(tree) == PurePath
448
456
def isIdempotentRef (tree : Tree )(implicit ctx : Context ): Boolean =
449
- refPurity(tree) >= Idempotent
457
+ refPurity(tree) >= IdempotentPath
450
458
451
459
/** (1) If `tree` is a constant expression, its value as a Literal,
452
460
* or `tree` itself otherwise.
@@ -840,13 +848,29 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
840
848
}
841
849
842
850
object TreeInfo {
851
+ /** A purity level is represented as a bitset (expressed as an Int) */
843
852
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)
846
858
}
847
859
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`. */
850
867
val Idempotent : PurityLevel = new PurityLevel (1 )
868
+
851
869
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)
852
876
}
0 commit comments