Skip to content

Commit 99be135

Browse files
committed
Fix #9246 and #6800: Remove pure statements
1 parent 8c5a58f commit 99be135

File tree

3 files changed

+101
-5
lines changed

3 files changed

+101
-5
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ class Compiler {
9494
new LetOverApply, // Lift blocks from receivers of applications
9595
new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify.
9696
List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements.
97-
List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types
97+
List(new PureStats, // Remove pure stats from blocks
98+
new ElimErasedValueType, // Expand erased value types to their underlying implmementation types
9899
new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations
99100
new ArrayApply, // Optimize `scala.Array.apply([....])` and `scala.Array.apply(..., [....])` into `[...]`
100101
new ElimPolyFunction, // Rewrite PolyFunction subclasses to FunctionN subclasses
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import ast.{Trees, tpd}
5+
import core._, core.Decorators._
6+
import MegaPhase._
7+
import Types._, Contexts._, Flags._, DenotTransformers._
8+
import Symbols._, StdNames._, Trees._
9+
10+
object PureStats {
11+
val name: String = "pureStats"
12+
}
13+
14+
/** Remove pure statements in blocks */
15+
class PureStats extends MiniPhase {
16+
17+
import tpd._
18+
19+
override def phaseName: String = PureStats.name
20+
21+
override def runsAfter: Set[String] = Set(Erasure.name)
22+
23+
override def transformBlock(tree: Block)(implicit ctx: Context): Tree =
24+
val stats = tree.stats.flatMap { stat =>
25+
if isPureExpr(stat) then Nil
26+
else stat match
27+
case Typed(Block(stats, expr), tpt) if isPureExpr(expr) => stats
28+
case _ => List(stat)
29+
}
30+
cpy.Block(tree)(stats, tree.expr)
31+
32+
}

compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,7 @@ class InlineBytecodeTests extends DottyBytecodeTest {
8282
| a
8383
| }
8484
|
85-
| def meth2: Unit = {
86-
| val a = 5
87-
| a
88-
| }
85+
| def meth2: Unit = ()
8986
|}
9087
""".stripMargin
9188

@@ -418,4 +415,70 @@ class InlineBytecodeTests extends DottyBytecodeTest {
418415

419416
}
420417
}
418+
419+
@Test def i6800a = {
420+
val source = """class Foo:
421+
| inline def inlined(f: => Unit): Unit = f
422+
| def test: Unit = inlined { println("") }
423+
""".stripMargin
424+
425+
checkBCode(source) { dir =>
426+
val clsIn = dir.lookupName("Foo.class", directory = false).input
427+
val clsNode = loadClassNode(clsIn)
428+
429+
val fun = getMethod(clsNode, "test")
430+
val instructions = instructionsFromMethod(fun)
431+
val expected = List(Invoke(INVOKESTATIC, "Foo", "f$1", "()V", false), Op(RETURN))
432+
assert(instructions == expected,
433+
"`inlined` was not properly inlined in `test`\n" + diffInstructions(instructions, expected))
434+
435+
}
436+
}
437+
438+
@Test def i6800b = {
439+
val source = """class Foo:
440+
| inline def printIfZero(x: Int): Unit = inline x match
441+
| case 0 => println("zero")
442+
| case _ => ()
443+
| def test: Unit = printIfZero(0)
444+
""".stripMargin
445+
446+
checkBCode(source) { dir =>
447+
val clsIn = dir.lookupName("Foo.class", directory = false).input
448+
val clsNode = loadClassNode(clsIn)
449+
450+
val fun = getMethod(clsNode, "test")
451+
val instructions = instructionsFromMethod(fun)
452+
val expected = List(
453+
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"),
454+
Ldc(LDC, "zero"),
455+
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
456+
Op(RETURN)
457+
)
458+
assert(instructions == expected,
459+
"`printIfZero` was not properly inlined in `test`\n" + diffInstructions(instructions, expected))
460+
}
461+
}
462+
463+
464+
@Test def i9246 = {
465+
val source = """class Foo:
466+
| inline def check(v:Double): Unit = if(v==0) throw new Exception()
467+
| inline def divide(v: Double, d: Double): Double = { check(d); v / d }
468+
| def test = divide(10,2)
469+
""".stripMargin
470+
471+
checkBCode(source) { dir =>
472+
val clsIn = dir.lookupName("Foo.class", directory = false).input
473+
val clsNode = loadClassNode(clsIn)
474+
475+
val fun = getMethod(clsNode, "test")
476+
val instructions = instructionsFromMethod(fun)
477+
val expected = List(Ldc(LDC, 5.0), Op(DRETURN))
478+
assert(instructions == expected,
479+
"`divide` was not properly inlined in `test`\n" + diffInstructions(instructions, expected))
480+
481+
}
482+
}
483+
421484
}

0 commit comments

Comments
 (0)