Skip to content

Commit c159361

Browse files
committed
Fix #9751: Fix Inline purity checks
We wrongly identified most inline references as pure as they are effectively erased. Only erased terms are always pure. Most of the purity of inline references follows from the normal purity rules except for inline parameters. These parameters lose their binding when inlined and no purity guaranty can be ensured from the reference.
1 parent ce48f5a commit c159361

File tree

7 files changed

+44
-5
lines changed

7 files changed

+44
-5
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,12 +454,14 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
454454
def refPurity(tree: Tree)(using Context): PurityLevel = {
455455
val sym = tree.symbol
456456
if (!tree.hasType) Impure
457-
else if (!tree.tpe.widen.isParameterless || sym.isEffectivelyErased) PurePath
457+
else if !tree.tpe.widen.isParameterless then PurePath
458+
else if sym.is(Erased) then PurePath
458459
else if tree.tpe.isInstanceOf[ConstantType] then PurePath
459460
else if (!sym.isStableMember) Impure
460461
else if (sym.is(Module))
461462
if (sym.moduleClass.isNoInitsClass) PurePath else IdempotentPath
462463
else if (sym.is(Lazy)) IdempotentPath
464+
else if sym.isAllOf(Inline | Param) then Impure
463465
else PurePath
464466
}
465467

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ abstract class TransformByNameApply extends MiniPhase { thisPhase: DenotTransfor
4747
ref(defn.cbnArg).appliedToType(argType).appliedTo(arg).withSpan(arg.span)
4848
arg match {
4949
case Apply(Select(qual, nme.apply), Nil)
50-
if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) =>
50+
if qual.tpe.derivesFrom(defn.FunctionClass(0)) && (isPureExpr(qual) || qual.symbol.isAllOf(Inline | Param)) =>
5151
wrap(qual)
5252
case _ =>
5353
if (isByNameRef(arg) || arg.symbol == defn.cbnArg) arg

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
521521
| Literal(_) =>
522522
true
523523
case Ident(_) =>
524-
isPureRef(tree)
524+
isPureRef(tree) || tree.symbol.isAllOf(Inline | Param)
525525
case Select(qual, _) =>
526526
if (tree.symbol.is(Erased)) true
527527
else isPureRef(tree) && apply(qual)

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3629,8 +3629,12 @@ class Typer extends Namer
36293629
}
36303630

36313631
private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol)(using Context): Unit =
3632-
if (!tree.tpe.isErroneous && !ctx.isAfterTyper && isPureExpr(tree) &&
3633-
!tree.tpe.isRef(defn.UnitClass) && !isSelfOrSuperConstrCall(tree))
3632+
if !tree.tpe.isErroneous
3633+
&& !ctx.isAfterTyper
3634+
&& !tree.isInstanceOf[Inlined]
3635+
&& isPureExpr(tree)
3636+
&& !isSelfOrSuperConstrCall(tree)
3637+
then
36343638
report.warning(PureExpressionInStatementPosition(original, exprOwner), original.srcPos)
36353639

36363640
/** Types the body Scala 2 macro declaration `def f = macro <body>` */
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
def f(): Unit = {
2+
() // error
3+
()
4+
}
5+
6+
inline def g(): Unit = {
7+
() // error
8+
()
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object Test {
2+
extension (x: Int)
3+
inline def times(inline op: Unit): Unit = {
4+
var count = 0
5+
while count < x do
6+
op
7+
count += 1
8+
}
9+
10+
10.times { println("hello") }
11+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object Test {
2+
inline def f(inline x: Boolean): Unit =
3+
inline if x then println()
4+
5+
f(true)
6+
f(false)
7+
8+
inline def g(inline x: => Boolean): Unit =
9+
inline if x then println()
10+
11+
g(true)
12+
g(false)
13+
}

0 commit comments

Comments
 (0)