Skip to content

Commit ac1e0b5

Browse files
committed
Fix isPureExpr test
We assumed all type applications were pure, which is claerly false. E.g. def fn[T] = println("hi") fn[Int] This triggered a problem in coverage tests which now assumes a use of an erased function is not erased (probably because it now shows up in the coverage info). The problem was fixed by mapping the erased defs to inline defs. I found out the problem with coverage: If an argument is not a pure expression, it gets lifted into a prefix val. And the prefix val is not erased even if the parameter is erased. So that way things escape an erased context. I believe this is a fundamental problem that would also affect lifting due to other reasons (e.g. eta expansion or handling default arguments). We either have to fix the lifting to mainain erased status, or restrict erased arguments to pure expressions that don't need lifting.
1 parent f68bd83 commit ac1e0b5

File tree

3 files changed

+22
-16
lines changed

3 files changed

+22
-16
lines changed

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -588,14 +588,18 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
588588
case New(_) | Closure(_, _, _) =>
589589
Pure
590590
case TypeApply(fn, _) =>
591-
if (fn.symbol.is(Erased) || fn.symbol == defn.QuotedTypeModule_of || fn.symbol == defn.Predef_classOf) Pure else exprPurity(fn)
591+
if tree.tpe.isInstanceOf[MethodOrPoly] then exprPurity(fn)
592+
else if fn.symbol == defn.QuotedTypeModule_of || fn.symbol == defn.Predef_classOf then Pure
593+
else if fn.symbol == defn.Compiletime_erasedValue && tree.tpe.dealias.isInstanceOf[ConstantType] then Pure
594+
else Impure
592595
case Apply(fn, args) =>
593-
if isPureApply(tree, fn) then
594-
minOf(exprPurity(fn), args.map(exprPurity)) `min` Pure
595-
else if fn.symbol.is(Erased) then
596-
Pure
596+
val factorPurity = minOf(exprPurity(fn), args.map(exprPurity))
597+
if tree.tpe.isInstanceOf[MethodOrPoly] then // no evaluation
598+
factorPurity `min` Pure
599+
else if isPureApply(tree, fn) then
600+
factorPurity `min` Pure
597601
else if fn.symbol.isStableMember /* && fn.symbol.is(Lazy) */ then
598-
minOf(exprPurity(fn), args.map(exprPurity)) `min` Idempotent
602+
factorPurity `min` Idempotent
599603
else
600604
Impure
601605
case Typed(expr, _) =>

compiler/src/dotty/tools/dotc/transform/InlineVals.scala

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package dotty.tools
22
package dotc
33
package transform
44

5-
import dotty.tools.dotc.core.Contexts.*
6-
import dotty.tools.dotc.core.Decorators.*
7-
import dotty.tools.dotc.core.Symbols.*
8-
import dotty.tools.dotc.core.Flags.*
9-
import dotty.tools.dotc.core.Types.*
10-
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
11-
import dotty.tools.dotc.inlines.Inlines
5+
import core.*
6+
import Contexts.*, Decorators.*, Symbols.*, Flags.*, Types.*
7+
import MegaPhase.MiniPhase
8+
import inlines.Inlines
9+
import ast.tpd
10+
1211

1312
/** Check that `tree.rhs` can be right hand-side of an `inline` value definition. */
1413
class InlineVals extends MiniPhase:
@@ -38,7 +37,10 @@ class InlineVals extends MiniPhase:
3837
tpt.tpe.widenTermRefExpr.dealiasKeepOpaques.normalized match
3938
case tp: ConstantType =>
4039
if !isPureExpr(rhs) then
41-
def details = if enclosingInlineds.isEmpty then "" else i"but was: $rhs"
40+
def details =
41+
if enclosingInlineds.nonEmpty || rhs.isInstanceOf[tpd.Inlined]
42+
then i" but was: $rhs"
43+
else ""
4244
report.error(em"inline value must be pure$details", rhs.srcPos)
4345
case tp =>
4446
if tp.typeSymbol.is(Opaque) then

tests/coverage/run/erased/test.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import scala.language.experimental.erasedDefinitions
22

3-
erased def parameterless: String = "y"
3+
inline def parameterless: String = "y"
44

5-
erased def e(erased x: String): String = "x"
5+
inline def e(erased x: String): String = "x"
66
def foo(erased a: String)(b: String): String =
77
println(s"foo(a)($b)")
88
b

0 commit comments

Comments
 (0)