Skip to content

Commit 10ce2e8

Browse files
committed
Add bytecode tests and fix position of transitivly inlined trees
1 parent 21f7aa3 commit 10ce2e8

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ object Inliner {
151151
def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = {
152152
if (enclosingInlineds.nonEmpty) inlined // Remove in the outer most inlined call
153153
else {
154+
// Position used for any tree that was inlined (inlcuding recursive inlines)
155+
val inlinedAtPos = inlined.call.pos
156+
154157
/** Removes all Inlined trees, replacing them with blocks.
155158
* Repositions all trees directly inside an inlined expantion of a non empty call to the position of the call.
156159
* Any tree directly inside an empty call (inlined in the inlined code) retains their position.
@@ -162,7 +165,7 @@ object Inliner {
162165
case _ =>
163166
val transformed = super.transform(tree)
164167
enclosingInlineds match {
165-
case call :: _ if !call.isEmpty => transformed.withPos(call.pos)
168+
case call :: _ if !call.isEmpty => transformed.withPos(inlinedAtPos)
166169
case _ => transformed
167170
}
168171
}
@@ -171,6 +174,7 @@ object Inliner {
171174
tpd.seq(transformSub(tree.bindings), transform(tree.expansion)(inlineContext(tree.call)))
172175
}
173176
}
177+
174178
(new Reposition).transformInline(inlined)
175179
}
176180
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,4 +335,5 @@ class TestBCode extends DottyBytecodeTest {
335335
assert(!fooInvoke, "foo should not be called\n")
336336
}
337337
}
338+
338339
}

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

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ package dotty.tools.backend.jvm
33
import org.junit.Assert._
44
import org.junit.Test
55

6+
import scala.tools.asm.Opcodes._
7+
8+
import scala.collection.JavaConverters._
9+
610
class InlineBytecodeTests extends DottyBytecodeTest {
711
import ASMConverters._
812
@Test def inlineUnit = {
@@ -37,4 +41,116 @@ class InlineBytecodeTests extends DottyBytecodeTest {
3741
diffInstructions(instructions2, instructions3))
3842
}
3943
}
44+
45+
@Test def i4947 = {
46+
val source = """class Foo {
47+
| transparent def track[T](f: => T): T = {
48+
| println("tracking") // line 3
49+
| f // line 4
50+
| }
51+
| def main(args: Array[String]): Unit = { // line 6
52+
| track { // line 7
53+
| println("abc") // line 8
54+
| track { // line 9
55+
| println("inner") // line 10
56+
| }
57+
| } // line 11
58+
| }
59+
|}
60+
""".stripMargin
61+
62+
checkBCode(source) { dir =>
63+
val clsIn = dir.lookupName("Foo.class", directory = false).input
64+
val clsNode = loadClassNode(clsIn, skipDebugInfo = false)
65+
66+
val track = clsNode.methods.asScala.find(_.name == "track")
67+
assert(track.isEmpty, "method `track` should have been erased")
68+
69+
val main = getMethod(clsNode, "main")
70+
val instructions = instructionsFromMethod(main)
71+
val expected =
72+
List(
73+
Label(0),
74+
LineNumber(6, Label(0)), // Position of the method start
75+
LineNumber(7, Label(0)), // Position of the call to `track`
76+
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"),
77+
Ldc(LDC, "tracking"),
78+
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
79+
Label(6),
80+
LineNumber(8, Label(6)), // Actual position
81+
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"),
82+
Ldc(LDC, "abc"),
83+
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false),
84+
Label(11),
85+
LineNumber(9, Label(11)), // Position of the call to `track`
86+
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"),
87+
Ldc(LDC, "tracking"),
88+
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false),
89+
Label(16),
90+
LineNumber(10, Label(16)), // Actual position
91+
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"),
92+
Ldc(LDC, "inner"),
93+
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false),
94+
Op(RETURN),
95+
Label(22)
96+
)
97+
assert(instructions == expected,
98+
"`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected))
99+
100+
}
101+
}
102+
103+
@Test def i4947b = {
104+
val source = """class Foo {
105+
| transparent def track2[T](f: => T): T = {
106+
| println("tracking2") // line 3
107+
| f // line 4
108+
| }
109+
| transparent def track[T](f: => T): T = {
110+
| println("tracking") // line 7
111+
| track2 { // line 8
112+
| f // line 9
113+
| }
114+
| }
115+
| def main(args: Array[String]): Unit = { // line 12
116+
| track { // line 13
117+
| println("abc") // line 14
118+
| }
119+
| }
120+
|}
121+
""".stripMargin
122+
123+
checkBCode(source) { dir =>
124+
val clsIn = dir.lookupName("Foo.class", directory = false).input
125+
val clsNode = loadClassNode(clsIn, skipDebugInfo = false)
126+
127+
val track = clsNode.methods.asScala.find(_.name == "track")
128+
assert(track.isEmpty, "method `track` should have been erased")
129+
130+
val main = getMethod(clsNode, "main")
131+
val instructions = instructionsFromMethod(main)
132+
val expected =
133+
List(
134+
Label(0),
135+
LineNumber(12, Label(0)), // Position of the method start
136+
LineNumber(13, Label(0)), // Position of the call to `track`
137+
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"),
138+
Ldc(LDC, "tracking"),
139+
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
140+
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"),
141+
Ldc(LDC, "tracking2"),
142+
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false),
143+
Label(9),
144+
LineNumber(14, Label(9)), // Actual position
145+
Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"),
146+
Ldc(LDC, "abc"),
147+
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false),
148+
Op(RETURN),
149+
Label(15)
150+
)
151+
assert(instructions == expected,
152+
"`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected))
153+
154+
}
155+
}
40156
}

0 commit comments

Comments
 (0)