Skip to content

Commit ae72183

Browse files
committed
Sharpen condition in Inliner
1 parent 89e84d2 commit ae72183

File tree

4 files changed

+43
-28
lines changed

4 files changed

+43
-28
lines changed

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -367,13 +367,19 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
367367
}
368368

369369
/** The purity level of this expression.
370-
* @return SimplyPure if expression has no side effects is a path
370+
* @return A possibly combination of
371+
*
372+
* Path if expression is at least idempotent and is a path
373+
*
371374
* Pure if expression has no side effects
372375
* Idempotent if running the expression a second time has no side effects
373-
* Impure otherwise
374376
*
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
377+
* Pure implies Idempotent.
378+
* Impure designates the empty combination.
379+
*
380+
* Note that purity and idempotency are treated differently.
381+
* References to modules and lazy vals are impure (side-effecting) both because
382+
* side-effecting code may be executed and because the first reference
377383
* takes a different code path than all to follow; but they are idempotent
378384
* because running the expression a second time gives the cached result.
379385
*/
@@ -382,12 +388,12 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
382388
| This(_)
383389
| Super(_, _)
384390
| Literal(_) =>
385-
SimplyPure
391+
PurePath
386392
case Ident(_) =>
387393
refPurity(tree)
388394
case Select(qual, _) =>
389395
if (tree.symbol.is(Erased)) Pure
390-
else refPurity(tree).min(exprPurity(qual))
396+
else refPurity(tree) `min` exprPurity(qual)
391397
case New(_) | Closure(_, _, _) =>
392398
Pure
393399
case TypeApply(fn, _) =>
@@ -415,37 +421,38 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
415421
Impure
416422
}
417423

418-
private def minOf(l0: PurityLevel, ls: List[PurityLevel]) = (l0 /: ls)(_ min _)
424+
private def minOf(l0: PurityLevel, ls: List[PurityLevel]) = (l0 /: ls)(_ `min` _)
419425

420-
def isSimplyPure(tree: Tree)(implicit ctx: Context): Boolean = exprPurity(tree) == SimplyPure
426+
def isPurePath(tree: Tree)(implicit ctx: Context): Boolean = exprPurity(tree) == PurePath
421427
def isPureExpr(tree: Tree)(implicit ctx: Context): Boolean = exprPurity(tree) >= Pure
422428
def isIdempotentExpr(tree: Tree)(implicit ctx: Context): Boolean = exprPurity(tree) >= Idempotent
429+
def isIdempotentPath(tree: Tree)(implicit ctx: Context): Boolean = exprPurity(tree) >= IdempotentPath
423430

424431
def isPureBinding(tree: Tree)(implicit ctx: Context): Boolean = statPurity(tree) >= Pure
425432

426433
/** The purity level of this reference.
427434
* @return
428-
* SimplyPure if reference is (nonlazy and stable) or to a parameterized function
429-
* Idempotent if reference is lazy and stable
430-
* Impure otherwise
435+
* PurePath if reference is (nonlazy and stable) or to a parameterized function
436+
* IdempotentPath if reference is lazy and stable
437+
* Impure otherwise
431438
* @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable
432439
* flags set.
433440
*/
434441
def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = {
435442
val sym = tree.symbol
436443
if (!tree.hasType) Impure
437-
else if (!tree.tpe.widen.isParameterless || sym.isEffectivelyErased) SimplyPure
444+
else if (!tree.tpe.widen.isParameterless || sym.isEffectivelyErased) PurePath
438445
else if (!sym.isStableMember) Impure
439446
else if (sym.is(Module))
440-
if (sym.moduleClass.isNoInitsClass) Pure else Idempotent
441-
else if (sym.is(Lazy)) Idempotent
442-
else SimplyPure
447+
if (sym.moduleClass.isNoInitsClass) PurePath else IdempotentPath
448+
else if (sym.is(Lazy)) IdempotentPath
449+
else PurePath
443450
}
444451

445452
def isPureRef(tree: Tree)(implicit ctx: Context): Boolean =
446-
refPurity(tree) == SimplyPure
453+
refPurity(tree) == PurePath
447454
def isIdempotentRef(tree: Tree)(implicit ctx: Context): Boolean =
448-
refPurity(tree) >= Idempotent
455+
refPurity(tree) >= IdempotentPath
449456

450457
/** (1) If `tree` is a constant expression, its value as a Literal,
451458
* or `tree` itself otherwise.
@@ -840,12 +847,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
840847

841848
object TreeInfo {
842849
class PurityLevel(val x: Int) extends AnyVal {
843-
def >= (that: PurityLevel): Boolean = x >= that.x
844-
def min(that: PurityLevel): PurityLevel = new PurityLevel(x min that.x)
850+
def >= (that: PurityLevel): Boolean = (x & that.x) == that.x
851+
def min(that: PurityLevel): PurityLevel = new PurityLevel(x & that.x)
845852
}
846853

847-
val SimplyPure: PurityLevel = new PurityLevel(3)
848-
val Pure: PurityLevel = new PurityLevel(2)
854+
val Path: PurityLevel = new PurityLevel(4)
855+
val Pure: PurityLevel = new PurityLevel(3)
849856
val Idempotent: PurityLevel = new PurityLevel(1)
850857
val Impure: PurityLevel = new PurityLevel(0)
858+
859+
val PurePath: PurityLevel = new PurityLevel(7)
860+
val IdempotentPath: PurityLevel = new PurityLevel(5)
851861
}

compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,10 @@ abstract class Lifter {
121121
* val x0 = pre
122122
* x0.f(...)
123123
*
124-
* unless `pre` is a `New` or `pre` is idempotent.
124+
* unless `pre` is idempotent.
125125
*/
126-
def liftPrefix(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match {
127-
case New(_) => tree
128-
case _ => if (isIdempotentExpr(tree)) tree else lift(defs, tree)
129-
}
126+
def liftPrefix(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree =
127+
if (isIdempotentExpr(tree)) tree else lift(defs, tree)
130128
}
131129

132130
/** No lifting at all */
@@ -142,7 +140,7 @@ object LiftImpure extends LiftImpure
142140

143141
/** Lift all impure or complex arguments */
144142
class LiftComplex extends Lifter {
145-
def noLift(expr: tpd.Tree)(implicit ctx: Context): Boolean = tpd.isSimplyPure(expr)
143+
def noLift(expr: tpd.Tree)(implicit ctx: Context): Boolean = tpd.isPurePath(expr)
146144
override def exprLifter: Lifter = LiftToDefs
147145
}
148146
object LiftComplex extends LiftComplex

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
285285
(tp.paramNames, tp.paramInfos, argss.head).zipped.foreach { (name, paramtp, arg) =>
286286
paramSpan(name) = arg.span
287287
paramBinding(name) = arg.tpe.dealias match {
288-
case _: SingletonType if isIdempotentExpr(arg) => arg.tpe
288+
case _: SingletonType if isIdempotentPath(arg) => arg.tpe
289289
case _ => paramBindingDef(name, paramtp, arg, bindingsBuf).symbol.termRef
290290
}
291291
}

tests/run/i6341.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object Test extends App {
2+
class Config(val t1: Int)
3+
4+
inline def m(t2:Int) = t2
5+
6+
m(new Config(3).t1)
7+
}

0 commit comments

Comments
 (0)