Skip to content

Commit 9a52dbd

Browse files
committed
Better purity predictions
Four improvments: 1. Applications of stable and pure functions to pure arguments are pure. This is important since constructors of pure classes are marked Stable. 2. New(...) expressions are pure (any side effects are caused by the constructor application). 3. Module values are pure of their module classes are pure. 4. scala.Product and scala.Serializable are assumed to be pure. Therefore, case classes can also be pure. These predictons remove most remaining unused bindings in the run/typelevel.scala test.
1 parent b84d17d commit 9a52dbd

File tree

2 files changed

+22
-10
lines changed

2 files changed

+22
-10
lines changed

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,8 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
369369
refPurity(tree)
370370
case Select(qual, _) =>
371371
refPurity(tree).min(exprPurity(qual))
372+
case New(_) =>
373+
SimplyPure
372374
case TypeApply(fn, _) =>
373375
exprPurity(fn)
374376
/*
@@ -380,13 +382,12 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
380382
case Apply(fn, args) =>
381383
def isKnownPureOp(sym: Symbol) =
382384
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
383-
// Note: After uncurry, field accesses are represented as Apply(getter, Nil),
384-
// so an Apply can also be pure.
385-
if (args.isEmpty && fn.symbol.is(Stable)) exprPurity(fn)
386-
else if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol))
387-
// A constant expression with pure arguments is pure.
385+
if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol)
386+
// A constant expression with pure arguments is pure.
387+
|| fn.symbol.isStable)
388388
minOf(exprPurity(fn), args.map(exprPurity)) `min` Pure
389-
else Impure
389+
else
390+
Impure
390391
case Typed(expr, _) =>
391392
exprPurity(expr)
392393
case Block(stats, expr) =>
@@ -413,11 +414,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
413414
* @DarkDimius: need to make sure that lazy accessor methods have Lazy and Stable
414415
* flags set.
415416
*/
416-
def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel =
417-
if (!tree.tpe.widen.isParameterless || tree.symbol.is(Erased)) SimplyPure
418-
else if (!tree.symbol.isStable) Impure
419-
else if (tree.symbol.is(Lazy)) Idempotent // TODO add Module flag, sinxce Module vals or not Lazy from the start.
417+
def refPurity(tree: Tree)(implicit ctx: Context): PurityLevel = {
418+
val sym = tree.symbol
419+
if (!tree.tpe.widen.isParameterless || sym.is(Erased)) SimplyPure
420+
else if (!sym.isStable) Impure
421+
else if (sym.is(Module))
422+
if (sym.moduleClass.isNoInitsClass) Pure else Idempotent
423+
else if (sym.is(Lazy)) Idempotent
420424
else SimplyPure
425+
}
421426

422427
def isPureRef(tree: Tree)(implicit ctx: Context) =
423428
refPurity(tree) == SimplyPure

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,13 @@ class Definitions {
12161216
for (m <- ScalaShadowingPackageClass.info.decls)
12171217
ScalaPackageClass.enter(m)
12181218

1219+
// Temporary measure, as long as we do not read these classes from Tasty.
1220+
// Scala-2 classes don't have NoInits set even if they are pure. We override this
1221+
// for Product and Serializable so that case classes can be pure. A full solution
1222+
// requiers that we read all Scala code from Tasty.
1223+
ProductClass.setFlag(NoInits)
1224+
SerializableClass.setFlag(NoInits)
1225+
12191226
// force initialization of every symbol that is synthesized or hijacked by the compiler
12201227
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses()
12211228

0 commit comments

Comments
 (0)