From 24b0d825fb1d24e6293101cc00c15129832b170c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 15 Aug 2018 17:39:50 +0200 Subject: [PATCH 01/19] Fix #4947: Do not replace positions of inlined arguments --- .../src/dotty/tools/dotc/typer/Inliner.scala | 22 +++++++++++++++---- tests/run/i4947.check | 2 ++ tests/run/i4947.scala | 18 +++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 tests/run/i4947.check create mode 100644 tests/run/i4947.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 10bf1c2032db..bed43b32d70b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -149,12 +149,26 @@ object Inliner { /** Replace `Inlined` node by a block that contains its bindings and expansion */ def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = { - val reposition = new TreeMap { - override def transform(tree: Tree)(implicit ctx: Context): Tree = { - super.transform(tree).withPos(inlined.call.pos) + if (enclosingInlineds.nonEmpty) inlined // remove in the outer inlined call + else { + val reposition = new TreeMap { + private[this] var pos: Position = inlined.pos + override def transform(tree: Tree)(implicit ctx: Context): Tree = { + tree match { + case inlined: Inlined => + val last = pos + pos = inlined.call.pos + val res = tpd.seq(inlined.bindings.map(transform), transform(inlined.expansion)).withPos(last) + pos = last + res + case tree => + if (pos.exists) super.transform(tree).withPos(pos) + else super.transform(tree) + } + } } + reposition.transform(inlined) } - tpd.seq(inlined.bindings, reposition.transform(inlined.expansion)) } } diff --git a/tests/run/i4947.check b/tests/run/i4947.check new file mode 100644 index 000000000000..48f406e5005a --- /dev/null +++ b/tests/run/i4947.check @@ -0,0 +1,2 @@ +failed +Test$.main(i4947.scala:9) diff --git a/tests/run/i4947.scala b/tests/run/i4947.scala new file mode 100644 index 000000000000..4bd8d2d57d2d --- /dev/null +++ b/tests/run/i4947.scala @@ -0,0 +1,18 @@ +object Test { + + transparent def track[T](f: => T): T = f + + def main(args: Array[String]): Unit = { + try { + track { + val a = 9 + throw new Exception("failed") + } + } catch { + case ex: Throwable => + println(ex.getMessage) + println(ex.getStackTrace.head) + } + } + +} From 3a6b56b988e880d4fb1a7872bdcf3393bdf967c5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 16 Aug 2018 17:37:38 +0200 Subject: [PATCH 02/19] Use enclosingInlineds directly to know the position --- .../src/dotty/tools/dotc/typer/Inliner.scala | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index bed43b32d70b..9bce4da8759c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -151,23 +151,25 @@ object Inliner { def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = { if (enclosingInlineds.nonEmpty) inlined // remove in the outer inlined call else { - val reposition = new TreeMap { - private[this] var pos: Position = inlined.pos + class Reposition extends TreeMap { override def transform(tree: Tree)(implicit ctx: Context): Tree = { tree match { - case inlined: Inlined => - val last = pos - pos = inlined.call.pos - val res = tpd.seq(inlined.bindings.map(transform), transform(inlined.expansion)).withPos(last) - pos = last - res - case tree => - if (pos.exists) super.transform(tree).withPos(pos) - else super.transform(tree) + case tree: Inlined => transformInline(tree) + case _ => + val transformed = super.transform(tree) + enclosingInlineds match { + case call :: _ if !call.isEmpty => + // This tree was inlined and will have the position of the call that was inlined + transformed.withPos(call.pos) + case _ => transformed + } } } + def transformInline(tree: tpd.Inlined)(implicit ctx: Context): Tree = { + tpd.seq(transformSub(tree.bindings), transform(tree.expansion)(inlineContext(tree.call))) + } } - reposition.transform(inlined) + (new Reposition).transformInline(inlined) } } } From 21f7aa3916812563bb42a65bbb0efd301f97266c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 16 Aug 2018 17:53:18 +0200 Subject: [PATCH 03/19] Add docs --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 9bce4da8759c..44b139fd82ec 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -149,8 +149,12 @@ object Inliner { /** Replace `Inlined` node by a block that contains its bindings and expansion */ def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = { - if (enclosingInlineds.nonEmpty) inlined // remove in the outer inlined call + if (enclosingInlineds.nonEmpty) inlined // Remove in the outer most inlined call else { + /** Removes all Inlined trees, replacing them with blocks. + * Repositions all trees directly inside an inlined expantion of a non empty call to the position of the call. + * Any tree directly inside an empty call (inlined in the inlined code) retains their position. + */ class Reposition extends TreeMap { override def transform(tree: Tree)(implicit ctx: Context): Tree = { tree match { @@ -158,9 +162,7 @@ object Inliner { case _ => val transformed = super.transform(tree) enclosingInlineds match { - case call :: _ if !call.isEmpty => - // This tree was inlined and will have the position of the call that was inlined - transformed.withPos(call.pos) + case call :: _ if !call.isEmpty => transformed.withPos(call.pos) case _ => transformed } } From 10ce2e85d88ceda2a04d2556757e91a248a121e2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Aug 2018 09:23:22 +0200 Subject: [PATCH 04/19] Add bytecode tests and fix position of transitivly inlined trees --- .../src/dotty/tools/dotc/typer/Inliner.scala | 6 +- .../backend/jvm/DottyBytecodeTests.scala | 1 + .../backend/jvm/InlineBytecodeTests.scala | 116 ++++++++++++++++++ 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 44b139fd82ec..a3739867bb90 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -151,6 +151,9 @@ object Inliner { def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = { if (enclosingInlineds.nonEmpty) inlined // Remove in the outer most inlined call else { + // Position used for any tree that was inlined (inlcuding recursive inlines) + val inlinedAtPos = inlined.call.pos + /** Removes all Inlined trees, replacing them with blocks. * Repositions all trees directly inside an inlined expantion of a non empty call to the position of the call. * Any tree directly inside an empty call (inlined in the inlined code) retains their position. @@ -162,7 +165,7 @@ object Inliner { case _ => val transformed = super.transform(tree) enclosingInlineds match { - case call :: _ if !call.isEmpty => transformed.withPos(call.pos) + case call :: _ if !call.isEmpty => transformed.withPos(inlinedAtPos) case _ => transformed } } @@ -171,6 +174,7 @@ object Inliner { tpd.seq(transformSub(tree.bindings), transform(tree.expansion)(inlineContext(tree.call))) } } + (new Reposition).transformInline(inlined) } } diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index d46a91bd1b74..6d6385497949 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -335,4 +335,5 @@ class TestBCode extends DottyBytecodeTest { assert(!fooInvoke, "foo should not be called\n") } } + } diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index b639adbfc66b..97da39421e1f 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -3,6 +3,10 @@ package dotty.tools.backend.jvm import org.junit.Assert._ import org.junit.Test +import scala.tools.asm.Opcodes._ + +import scala.collection.JavaConverters._ + class InlineBytecodeTests extends DottyBytecodeTest { import ASMConverters._ @Test def inlineUnit = { @@ -37,4 +41,116 @@ class InlineBytecodeTests extends DottyBytecodeTest { diffInstructions(instructions2, instructions3)) } } + + @Test def i4947 = { + val source = """class Foo { + | transparent def track[T](f: => T): T = { + | println("tracking") // line 3 + | f // line 4 + | } + | def main(args: Array[String]): Unit = { // line 6 + | track { // line 7 + | println("abc") // line 8 + | track { // line 9 + | println("inner") // line 10 + | } + | } // line 11 + | } + |} + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Foo.class", directory = false).input + val clsNode = loadClassNode(clsIn, skipDebugInfo = false) + + val track = clsNode.methods.asScala.find(_.name == "track") + assert(track.isEmpty, "method `track` should have been erased") + + val main = getMethod(clsNode, "main") + val instructions = instructionsFromMethod(main) + val expected = + List( + Label(0), + LineNumber(6, Label(0)), // Position of the method start + LineNumber(7, Label(0)), // Position of the call to `track` + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "tracking"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Label(6), + LineNumber(8, Label(6)), // Actual position + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "abc"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Label(11), + LineNumber(9, Label(11)), // Position of the call to `track` + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "tracking"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Label(16), + LineNumber(10, Label(16)), // Actual position + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "inner"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Op(RETURN), + Label(22) + ) + assert(instructions == expected, + "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) + + } + } + + @Test def i4947b = { + val source = """class Foo { + | transparent def track2[T](f: => T): T = { + | println("tracking2") // line 3 + | f // line 4 + | } + | transparent def track[T](f: => T): T = { + | println("tracking") // line 7 + | track2 { // line 8 + | f // line 9 + | } + | } + | def main(args: Array[String]): Unit = { // line 12 + | track { // line 13 + | println("abc") // line 14 + | } + | } + |} + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Foo.class", directory = false).input + val clsNode = loadClassNode(clsIn, skipDebugInfo = false) + + val track = clsNode.methods.asScala.find(_.name == "track") + assert(track.isEmpty, "method `track` should have been erased") + + val main = getMethod(clsNode, "main") + val instructions = instructionsFromMethod(main) + val expected = + List( + Label(0), + LineNumber(12, Label(0)), // Position of the method start + LineNumber(13, Label(0)), // Position of the call to `track` + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "tracking"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "tracking2"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Label(9), + LineNumber(14, Label(9)), // Actual position + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "abc"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Op(RETURN), + Label(15) + ) + assert(instructions == expected, + "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) + + } + } } From 0ff3fd7dfbe1a0f6de1527c75caf8ac8698379d2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Aug 2018 09:38:34 +0200 Subject: [PATCH 05/19] Add more tests --- .../backend/jvm/InlineBytecodeTests.scala | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index 97da39421e1f..f1a7840ee87e 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -153,4 +153,113 @@ class InlineBytecodeTests extends DottyBytecodeTest { } } + + @Test def i4947c = { + val source = """class Foo { + | transparent def track2[T](f: => T): T = { + | println("tracking2") // line 3 + | f // line 4 + | } + | transparent def track[T](f: => T): T = { + | track2 { // line 7 + | println("fgh") // line 8 + | f // line 9 + | } + | } + | def main(args: Array[String]): Unit = { // line 12 + | track { // line 13 + | println("abc") // line 14 + | } + | } + |} + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Foo.class", directory = false).input + val clsNode = loadClassNode(clsIn, skipDebugInfo = false) + + val track = clsNode.methods.asScala.find(_.name == "track") + assert(track.isEmpty, "method `track` should have been erased") + + val main = getMethod(clsNode, "main") + val instructions = instructionsFromMethod(main) + val expected = + List( + Label(0), + LineNumber(12, Label(0)), // Position of the method start + LineNumber(13, Label(0)), // Position of the call to `track` + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "tracking2"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "fgh"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Label(9), + LineNumber(14, Label(9)), // Actual position + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "abc"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Op(RETURN), + Label(15) + ) + assert(instructions == expected, + "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) + + } + } + + @Test def i4947d = { + val source = """class Foo { + | transparent def track2[T](f: => T): T = { + | println("tracking2") // line 3 + | f // line 4 + | } + | transparent def track[T](f: => T): T = { + | track2 { // line 7 + | track2 { // line 8 + | f // line 9 + | } + | } + | } + | def main(args: Array[String]): Unit = { // line 13 + | track { // line 14 + | println("abc") // line 15 + | } + | } + |} + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Foo.class", directory = false).input + val clsNode = loadClassNode(clsIn, skipDebugInfo = false) + + val track = clsNode.methods.asScala.find(_.name == "track") + assert(track.isEmpty, "method `track` should have been erased") + + val main = getMethod(clsNode, "main") + val instructions = instructionsFromMethod(main) + val expected = + List( + Label(0), + LineNumber(13, Label(0)), // Position of the method start + LineNumber(14, Label(0)), // Position of the call to `track` + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "tracking2"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "tracking2"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Label(9), + LineNumber(15, Label(9)), // Actual position + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "abc"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Op(RETURN), + Label(15) + ) + assert(instructions == expected, + "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) + + } + } } From 283736552791cc5cd8a8f7f6c522511f2a282e96 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Aug 2018 10:00:05 +0200 Subject: [PATCH 06/19] Fix typo --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index a3739867bb90..978748951763 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -151,7 +151,7 @@ object Inliner { def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = { if (enclosingInlineds.nonEmpty) inlined // Remove in the outer most inlined call else { - // Position used for any tree that was inlined (inlcuding recursive inlines) + // Position used for any tree that was inlined (including recursive inlines) val inlinedAtPos = inlined.call.pos /** Removes all Inlined trees, replacing them with blocks. From 857f53e30b738408632fa99c220f0e5294911288 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Aug 2018 10:11:10 +0200 Subject: [PATCH 07/19] Add more tests --- tests/run/i4947.check | 6 ++++-- tests/run/i4947.scala | 22 ++++++++++++---------- tests/run/i4947b.check | 4 ++++ tests/run/i4947b/Lib_1.scala | 10 ++++++++++ tests/run/i4947b/Test_2.scala | 9 +++++++++ 5 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 tests/run/i4947b.check create mode 100644 tests/run/i4947b/Lib_1.scala create mode 100644 tests/run/i4947b/Test_2.scala diff --git a/tests/run/i4947.check b/tests/run/i4947.check index 48f406e5005a..a445d1cb83c6 100644 --- a/tests/run/i4947.check +++ b/tests/run/i4947.check @@ -1,2 +1,4 @@ -failed -Test$.main(i4947.scala:9) +track: Test$.main(i4947.scala:14) +track: Test$.main(i4947.scala:14) +main1: Test$.main(i4947.scala:15) +main2: Test$.main(i4947.scala:16) diff --git a/tests/run/i4947.scala b/tests/run/i4947.scala index 4bd8d2d57d2d..08648487f7fa 100644 --- a/tests/run/i4947.scala +++ b/tests/run/i4947.scala @@ -1,17 +1,19 @@ object Test { - transparent def track[T](f: => T): T = f + transparent def track[T](f: => T): T = { + printStack("track") + printStack("track") + f + } + + def printStack(tag: String): Unit = { + println(tag + ": "+ new Exception().getStackTrace().apply(1)) + } def main(args: Array[String]): Unit = { - try { - track { - val a = 9 - throw new Exception("failed") - } - } catch { - case ex: Throwable => - println(ex.getMessage) - println(ex.getStackTrace.head) + track { + printStack("main1") + printStack("main2") } } diff --git a/tests/run/i4947b.check b/tests/run/i4947b.check new file mode 100644 index 000000000000..4a279ef4aeb3 --- /dev/null +++ b/tests/run/i4947b.check @@ -0,0 +1,4 @@ +track: Test$.main(Test_2.scala:3) +track: Test$.main(Test_2.scala:3) +main1: Test$.main(Test_2.scala:4) +main2: Test$.main(Test_2.scala:5) diff --git a/tests/run/i4947b/Lib_1.scala b/tests/run/i4947b/Lib_1.scala new file mode 100644 index 000000000000..d7eca0bba0b8 --- /dev/null +++ b/tests/run/i4947b/Lib_1.scala @@ -0,0 +1,10 @@ +object Lib { + transparent def track[T](f: => T): T = { + printStack("track") + printStack("track") + f + } + def printStack(tag: String): Unit = { + println(tag + ": "+ new Exception().getStackTrace().apply(1)) + } +} diff --git a/tests/run/i4947b/Test_2.scala b/tests/run/i4947b/Test_2.scala new file mode 100644 index 000000000000..3941f930f813 --- /dev/null +++ b/tests/run/i4947b/Test_2.scala @@ -0,0 +1,9 @@ +object Test { + def main(args: Array[String]): Unit = { + Lib.track { + Lib.printStack("main1") + Lib.printStack("main2") + } + } + +} From a11db37c837f055f9e51f0eb1bac6b8002ab7c55 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Aug 2018 10:25:12 +0200 Subject: [PATCH 08/19] Retain positions of trees inlined from the same file Only reposition positions of inlined trees when they come from another file. --- .../src/dotty/tools/dotc/typer/Inliner.scala | 4 +- .../backend/jvm/InlineBytecodeTests.scala | 46 +++++++++++-------- tests/run/i4947.check | 4 +- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 978748951763..22fb498c602c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -165,7 +165,9 @@ object Inliner { case _ => val transformed = super.transform(tree) enclosingInlineds match { - case call :: _ if !call.isEmpty => transformed.withPos(inlinedAtPos) + case call :: _ if !call.isEmpty && call.symbol != ctx.owner.topLevelClass => + // reposition tree inlined from some other file + transformed.withPos(inlinedAtPos) case _ => transformed } } diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index f1a7840ee87e..550624dfaa40 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -71,23 +71,23 @@ class InlineBytecodeTests extends DottyBytecodeTest { val expected = List( Label(0), - LineNumber(6, Label(0)), // Position of the method start - LineNumber(7, Label(0)), // Position of the call to `track` + LineNumber(6, Label(0)), + LineNumber(3, Label(0)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "tracking"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), Label(6), - LineNumber(8, Label(6)), // Actual position + LineNumber(8, Label(6)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), Label(11), - LineNumber(9, Label(11)), // Position of the call to `track` + LineNumber(3, Label(11)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "tracking"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), Label(16), - LineNumber(10, Label(16)), // Actual position + LineNumber(10, Label(16)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "inner"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), @@ -132,21 +132,23 @@ class InlineBytecodeTests extends DottyBytecodeTest { val expected = List( Label(0), - LineNumber(12, Label(0)), // Position of the method start - LineNumber(13, Label(0)), // Position of the call to `track` + LineNumber(12, Label(0)), + LineNumber(7, Label(0)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "tracking"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Label(6), + LineNumber(3, Label(6)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "tracking2"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), - Label(9), - LineNumber(14, Label(9)), // Actual position + Label(11), + LineNumber(14, Label(11)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), Op(RETURN), - Label(15) + Label(17) ) assert(instructions == expected, "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) @@ -186,21 +188,23 @@ class InlineBytecodeTests extends DottyBytecodeTest { val expected = List( Label(0), - LineNumber(12, Label(0)), // Position of the method start - LineNumber(13, Label(0)), // Position of the call to `track` + LineNumber(12, Label(0)), + LineNumber(3, Label(0)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "tracking2"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Label(6), + LineNumber(8, Label(6)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "fgh"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), - Label(9), - LineNumber(14, Label(9)), // Actual position + Label(11), + LineNumber(14, Label(11)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), Op(RETURN), - Label(15) + Label(17) ) assert(instructions == expected, "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) @@ -241,21 +245,23 @@ class InlineBytecodeTests extends DottyBytecodeTest { val expected = List( Label(0), - LineNumber(13, Label(0)), // Position of the method start - LineNumber(14, Label(0)), // Position of the call to `track` + LineNumber(13, Label(0)), + LineNumber(3, Label(0)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "tracking2"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Label(6), + LineNumber(3, Label(6)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "tracking2"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), - Label(9), - LineNumber(15, Label(9)), // Actual position + Label(11), + LineNumber(15, Label(11)), Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), Op(RETURN), - Label(15) + Label(17) ) assert(instructions == expected, "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) diff --git a/tests/run/i4947.check b/tests/run/i4947.check index a445d1cb83c6..35c8075066c3 100644 --- a/tests/run/i4947.check +++ b/tests/run/i4947.check @@ -1,4 +1,4 @@ -track: Test$.main(i4947.scala:14) -track: Test$.main(i4947.scala:14) +track: Test$.main(i4947.scala:4) +track: Test$.main(i4947.scala:5) main1: Test$.main(i4947.scala:15) main2: Test$.main(i4947.scala:16) From a12688a069a094e2dac852c2538fcdbc23f8154d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Aug 2018 10:33:27 +0200 Subject: [PATCH 09/19] Check that all trasnparent methods have been erased --- .../dotty/tools/backend/jvm/InlineBytecodeTests.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index 550624dfaa40..901936cde5a6 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -127,6 +127,9 @@ class InlineBytecodeTests extends DottyBytecodeTest { val track = clsNode.methods.asScala.find(_.name == "track") assert(track.isEmpty, "method `track` should have been erased") + val track2 = clsNode.methods.asScala.find(_.name == "track2") + assert(track2.isEmpty, "method `track2` should have been erased") + val main = getMethod(clsNode, "main") val instructions = instructionsFromMethod(main) val expected = @@ -183,6 +186,9 @@ class InlineBytecodeTests extends DottyBytecodeTest { val track = clsNode.methods.asScala.find(_.name == "track") assert(track.isEmpty, "method `track` should have been erased") + val track2 = clsNode.methods.asScala.find(_.name == "track2") + assert(track2.isEmpty, "method `track2` should have been erased") + val main = getMethod(clsNode, "main") val instructions = instructionsFromMethod(main) val expected = @@ -240,6 +246,9 @@ class InlineBytecodeTests extends DottyBytecodeTest { val track = clsNode.methods.asScala.find(_.name == "track") assert(track.isEmpty, "method `track` should have been erased") + val track2 = clsNode.methods.asScala.find(_.name == "track2") + assert(track2.isEmpty, "method `track2` should have been erased") + val main = getMethod(clsNode, "main") val instructions = instructionsFromMethod(main) val expected = From 53e85d66bb450d58c539b6c921c9e594742b8754 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Aug 2018 11:17:35 +0200 Subject: [PATCH 10/19] Remove useless check --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 22fb498c602c..5954f005064b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -165,7 +165,7 @@ object Inliner { case _ => val transformed = super.transform(tree) enclosingInlineds match { - case call :: _ if !call.isEmpty && call.symbol != ctx.owner.topLevelClass => + case call :: _ if call.symbol != ctx.owner.topLevelClass => // reposition tree inlined from some other file transformed.withPos(inlinedAtPos) case _ => transformed From d52c3b2502e5ab55c33b62bc59cea205cf7d20ff Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Aug 2018 11:49:35 +0200 Subject: [PATCH 11/19] Add topLevelClass and do not assume transformation of posttyper --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 5954f005064b..4350ab98bdb9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -165,7 +165,7 @@ object Inliner { case _ => val transformed = super.transform(tree) enclosingInlineds match { - case call :: _ if call.symbol != ctx.owner.topLevelClass => + case call :: _ if call.symbol.topLevelClass != ctx.owner.topLevelClass => // reposition tree inlined from some other file transformed.withPos(inlinedAtPos) case _ => transformed From 27ab7eeeabde65f24e2cb53f2f57a2bed77be390 Mon Sep 17 00:00:00 2001 From: "Paolo G. Giarrusso" Date: Wed, 22 Aug 2018 19:03:59 +0200 Subject: [PATCH 12/19] Test recursive transparent method --- tests/run/i4947a.check | 12 ++++++++++++ tests/run/i4947a.scala | 25 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/run/i4947a.check create mode 100644 tests/run/i4947a.scala diff --git a/tests/run/i4947a.check b/tests/run/i4947a.check new file mode 100644 index 000000000000..02a21a990766 --- /dev/null +++ b/tests/run/i4947a.check @@ -0,0 +1,12 @@ +track (i = 2): Test$.main(i4947a.scala:4) +track (i = 2): Test$.main(i4947a.scala:5) +main1 (i = -1): Test$.main(i4947a.scala:20) +main2 (i = -1): Test$.main(i4947a.scala:21) +track (i = 1): Test$.main(i4947a.scala:4) +track (i = 1): Test$.main(i4947a.scala:5) +main1 (i = -1): Test$.main(i4947a.scala:20) +main2 (i = -1): Test$.main(i4947a.scala:21) +track (i = 0): Test$.main(i4947a.scala:4) +track (i = 0): Test$.main(i4947a.scala:5) +main1 (i = -1): Test$.main(i4947a.scala:20) +main2 (i = -1): Test$.main(i4947a.scala:21) diff --git a/tests/run/i4947a.scala b/tests/run/i4947a.scala new file mode 100644 index 000000000000..418b2fed601e --- /dev/null +++ b/tests/run/i4947a.scala @@ -0,0 +1,25 @@ +object Test { + + transparent def fact[T](transparent i: Int)(f: => T): Int = { + printStack("track", i) + printStack("track", i) + f + if (i == 0) + 1 + else { + i * fact(i-1)(f) + } + } + + def printStack(tag: String, i: Int): Unit = { + println(s"$tag (i = $i): ${new Exception().getStackTrace().apply(1)}") + } + + def main(args: Array[String]): Unit = { + fact(2) { + printStack("main1", -1) + printStack("main2", -1) + } + } + +} From f0435990aeda4f6c32c02069ed9459c49ecfa65d Mon Sep 17 00:00:00 2001 From: "Paolo G. Giarrusso" Date: Wed, 22 Aug 2018 19:04:23 +0200 Subject: [PATCH 13/19] Only reposition trees from other files, not other classes --- .../src/dotty/tools/dotc/typer/Inliner.scala | 2 +- tests/run/i4947c.check | 4 ++++ tests/run/i4947c.scala | 23 +++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/run/i4947c.check create mode 100644 tests/run/i4947c.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 4350ab98bdb9..df2ac8f90126 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -165,7 +165,7 @@ object Inliner { case _ => val transformed = super.transform(tree) enclosingInlineds match { - case call :: _ if call.symbol.topLevelClass != ctx.owner.topLevelClass => + case call :: _ if call.symbol.sourceFile != ctx.owner.sourceFile => // reposition tree inlined from some other file transformed.withPos(inlinedAtPos) case _ => transformed diff --git a/tests/run/i4947c.check b/tests/run/i4947c.check new file mode 100644 index 000000000000..56a401547ae3 --- /dev/null +++ b/tests/run/i4947c.check @@ -0,0 +1,4 @@ +track: Test$.main(i4947c.scala:4) +track: Test$.main(i4947c.scala:5) +main1: Test$.main(i4947c.scala:18) +main2: Test$.main(i4947c.scala:19) diff --git a/tests/run/i4947c.scala b/tests/run/i4947c.scala new file mode 100644 index 000000000000..be1efe8cdb8e --- /dev/null +++ b/tests/run/i4947c.scala @@ -0,0 +1,23 @@ +object Aux { + + transparent def track[T](f: => T): T = { + printStack("track") + printStack("track") + f + } + + def printStack(tag: String): Unit = { + println(tag + ": "+ new Exception().getStackTrace().apply(1)) + } +} + +object Test { + import Aux._ + def main(args: Array[String]): Unit = { + track { + printStack("main1") + printStack("main2") + } + } + +} From e21ae3ff8fd6e34008e06476a4b450c1d2b698d1 Mon Sep 17 00:00:00 2001 From: "Paolo G. Giarrusso" Date: Wed, 22 Aug 2018 19:27:22 +0200 Subject: [PATCH 14/19] Typo --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index df2ac8f90126..340edb5cc01d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -155,7 +155,7 @@ object Inliner { val inlinedAtPos = inlined.call.pos /** Removes all Inlined trees, replacing them with blocks. - * Repositions all trees directly inside an inlined expantion of a non empty call to the position of the call. + * Repositions all trees directly inside an inlined expansion of a non empty call to the position of the call. * Any tree directly inside an empty call (inlined in the inlined code) retains their position. */ class Reposition extends TreeMap { From cc6ca5fc966c0e61aff84cf4e085fac29510d9b1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 Aug 2018 11:37:52 +0200 Subject: [PATCH 15/19] Use local method --- .../backend/jvm/InlineBytecodeTests.scala | 78 ++++++++++--------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index 901936cde5a6..2ab838ac271e 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -45,17 +45,18 @@ class InlineBytecodeTests extends DottyBytecodeTest { @Test def i4947 = { val source = """class Foo { | transparent def track[T](f: => T): T = { - | println("tracking") // line 3 + | foo("tracking") // line 3 | f // line 4 | } | def main(args: Array[String]): Unit = { // line 6 | track { // line 7 - | println("abc") // line 8 + | foo("abc") // line 8 | track { // line 9 - | println("inner") // line 10 + | foo("inner") // line 10 | } | } // line 11 | } + | def foo(str: String): Unit = () |} """.stripMargin @@ -73,24 +74,24 @@ class InlineBytecodeTests extends DottyBytecodeTest { Label(0), LineNumber(6, Label(0)), LineNumber(3, Label(0)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "tracking"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(6), LineNumber(8, Label(6)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "abc"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(11), LineNumber(3, Label(11)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "tracking"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(16), LineNumber(10, Label(16)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "inner"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Op(RETURN), Label(22) ) @@ -103,20 +104,21 @@ class InlineBytecodeTests extends DottyBytecodeTest { @Test def i4947b = { val source = """class Foo { | transparent def track2[T](f: => T): T = { - | println("tracking2") // line 3 + | foo("tracking2") // line 3 | f // line 4 | } | transparent def track[T](f: => T): T = { - | println("tracking") // line 7 + | foo("tracking") // line 7 | track2 { // line 8 | f // line 9 | } | } | def main(args: Array[String]): Unit = { // line 12 | track { // line 13 - | println("abc") // line 14 + | foo("abc") // line 14 | } | } + | def foo(str: String): Unit = () |} """.stripMargin @@ -137,19 +139,19 @@ class InlineBytecodeTests extends DottyBytecodeTest { Label(0), LineNumber(12, Label(0)), LineNumber(7, Label(0)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "tracking"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(6), LineNumber(3, Label(6)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "tracking2"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(11), LineNumber(14, Label(11)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "abc"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Op(RETURN), Label(17) ) @@ -162,20 +164,21 @@ class InlineBytecodeTests extends DottyBytecodeTest { @Test def i4947c = { val source = """class Foo { | transparent def track2[T](f: => T): T = { - | println("tracking2") // line 3 + | foo("tracking2") // line 3 | f // line 4 | } | transparent def track[T](f: => T): T = { | track2 { // line 7 - | println("fgh") // line 8 + | foo("fgh") // line 8 | f // line 9 | } | } | def main(args: Array[String]): Unit = { // line 12 | track { // line 13 - | println("abc") // line 14 + | foo("abc") // line 14 | } | } + | def foo(str: String): Unit = () |} """.stripMargin @@ -196,19 +199,19 @@ class InlineBytecodeTests extends DottyBytecodeTest { Label(0), LineNumber(12, Label(0)), LineNumber(3, Label(0)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "tracking2"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(6), LineNumber(8, Label(6)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "fgh"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(11), LineNumber(14, Label(11)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "abc"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Op(RETURN), Label(17) ) @@ -221,7 +224,7 @@ class InlineBytecodeTests extends DottyBytecodeTest { @Test def i4947d = { val source = """class Foo { | transparent def track2[T](f: => T): T = { - | println("tracking2") // line 3 + | foo("tracking2") // line 3 | f // line 4 | } | transparent def track[T](f: => T): T = { @@ -233,9 +236,10 @@ class InlineBytecodeTests extends DottyBytecodeTest { | } | def main(args: Array[String]): Unit = { // line 13 | track { // line 14 - | println("abc") // line 15 + | foo("abc") // line 15 | } | } + | def foo(str: String): Unit = () |} """.stripMargin @@ -256,19 +260,19 @@ class InlineBytecodeTests extends DottyBytecodeTest { Label(0), LineNumber(13, Label(0)), LineNumber(3, Label(0)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "tracking2"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(6), LineNumber(3, Label(6)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "tracking2"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Label(11), LineNumber(15, Label(11)), - Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + VarOp(ALOAD, 0), Ldc(LDC, "abc"), - Invoke(INVOKEVIRTUAL, "scala/Predef$", "println","(Ljava/lang/Object;)V", false), + Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Op(RETURN), Label(17) ) From dc66cce95f37cd9dc640562ff67444ef2b2b1019 Mon Sep 17 00:00:00 2001 From: "Paolo G. Giarrusso" Date: Thu, 23 Aug 2018 13:55:22 +0200 Subject: [PATCH 16/19] Improve some docs --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 340edb5cc01d..183e58113423 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -151,7 +151,6 @@ object Inliner { def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = { if (enclosingInlineds.nonEmpty) inlined // Remove in the outer most inlined call else { - // Position used for any tree that was inlined (including recursive inlines) val inlinedAtPos = inlined.call.pos /** Removes all Inlined trees, replacing them with blocks. @@ -166,7 +165,8 @@ object Inliner { val transformed = super.transform(tree) enclosingInlineds match { case call :: _ if call.symbol.sourceFile != ctx.owner.sourceFile => - // reposition tree inlined from some other file + // Until we implement JSR-45, we cannot represent in output positions in other source files. + // So, reposition inlined code from other files with the call position: transformed.withPos(inlinedAtPos) case _ => transformed } From bf1ae9353ae6a24f08ab29d50fb23d499fcaf0d1 Mon Sep 17 00:00:00 2001 From: "Paolo G. Giarrusso" Date: Thu, 23 Aug 2018 13:59:48 +0200 Subject: [PATCH 17/19] Complicate tests --- tests/run/i4947a.check | 14 ++++++++------ tests/run/i4947a.scala | 8 +++++--- tests/run/i4947b.check | 20 ++++++++++++++++++++ tests/run/i4947b/Lib_1.scala | 15 +++++++++++++++ tests/run/i4947b/Test_2.scala | 12 ++++++++++++ 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/tests/run/i4947a.check b/tests/run/i4947a.check index 02a21a990766..0ff7f8edcf9f 100644 --- a/tests/run/i4947a.check +++ b/tests/run/i4947a.check @@ -1,12 +1,14 @@ +track (i = 0): Test$.main(i4947a.scala:4) +track (i = 0): Test$.main(i4947a.scala:5) track (i = 2): Test$.main(i4947a.scala:4) track (i = 2): Test$.main(i4947a.scala:5) -main1 (i = -1): Test$.main(i4947a.scala:20) -main2 (i = -1): Test$.main(i4947a.scala:21) +main1 (i = -1): Test$.main(i4947a.scala:21) +main2 (i = -1): Test$.main(i4947a.scala:22) track (i = 1): Test$.main(i4947a.scala:4) track (i = 1): Test$.main(i4947a.scala:5) -main1 (i = -1): Test$.main(i4947a.scala:20) -main2 (i = -1): Test$.main(i4947a.scala:21) +main1 (i = -1): Test$.main(i4947a.scala:21) +main2 (i = -1): Test$.main(i4947a.scala:22) track (i = 0): Test$.main(i4947a.scala:4) track (i = 0): Test$.main(i4947a.scala:5) -main1 (i = -1): Test$.main(i4947a.scala:20) -main2 (i = -1): Test$.main(i4947a.scala:21) +main1 (i = -1): Test$.main(i4947a.scala:21) +main2 (i = -1): Test$.main(i4947a.scala:22) diff --git a/tests/run/i4947a.scala b/tests/run/i4947a.scala index 418b2fed601e..aa0484b04e29 100644 --- a/tests/run/i4947a.scala +++ b/tests/run/i4947a.scala @@ -16,9 +16,11 @@ object Test { } def main(args: Array[String]): Unit = { - fact(2) { - printStack("main1", -1) - printStack("main2", -1) + fact(0) { + fact(2) { + printStack("main1", -1) + printStack("main2", -1) + } } } diff --git a/tests/run/i4947b.check b/tests/run/i4947b.check index 4a279ef4aeb3..be892da11010 100644 --- a/tests/run/i4947b.check +++ b/tests/run/i4947b.check @@ -2,3 +2,23 @@ track: Test$.main(Test_2.scala:3) track: Test$.main(Test_2.scala:3) main1: Test$.main(Test_2.scala:4) main2: Test$.main(Test_2.scala:5) +track: Test$.main(Test_2.scala:7) +track: Test$.main(Test_2.scala:7) +track: Test$.main(Test_2.scala:8) +track: Test$.main(Test_2.scala:8) +main3: Test$.main(Test_2.scala:9) +main4: Test$.main(Test_2.scala:10) +track (i = 0): Test$.main(Test_2.scala:13) +track (i = 0): Test$.main(Test_2.scala:13) +track (i = 2): Test$.main(Test_2.scala:14) +track (i = 2): Test$.main(Test_2.scala:14) +main1 (i = -1): Test$.main(Test_2.scala:15) +main2 (i = -1): Test$.main(Test_2.scala:16) +track (i = 1): Test$.main(Test_2.scala:14) +track (i = 1): Test$.main(Test_2.scala:14) +main1 (i = -1): Test$.main(Test_2.scala:15) +main2 (i = -1): Test$.main(Test_2.scala:16) +track (i = 0): Test$.main(Test_2.scala:14) +track (i = 0): Test$.main(Test_2.scala:14) +main1 (i = -1): Test$.main(Test_2.scala:15) +main2 (i = -1): Test$.main(Test_2.scala:16) diff --git a/tests/run/i4947b/Lib_1.scala b/tests/run/i4947b/Lib_1.scala index d7eca0bba0b8..778a40cd51be 100644 --- a/tests/run/i4947b/Lib_1.scala +++ b/tests/run/i4947b/Lib_1.scala @@ -7,4 +7,19 @@ object Lib { def printStack(tag: String): Unit = { println(tag + ": "+ new Exception().getStackTrace().apply(1)) } + + def printStack(tag: String, i: Int): Unit = { + println(s"$tag (i = $i): ${new Exception().getStackTrace().apply(1)}") + } + + transparent def fact[T](transparent i: Int)(f: => T): Int = { + printStack("track", i) + printStack("track", i) + f + if (i == 0) + 1 + else { + i * fact(i-1)(f) + } + } } diff --git a/tests/run/i4947b/Test_2.scala b/tests/run/i4947b/Test_2.scala index 3941f930f813..2ca1cf2e77f1 100644 --- a/tests/run/i4947b/Test_2.scala +++ b/tests/run/i4947b/Test_2.scala @@ -4,6 +4,18 @@ object Test { Lib.printStack("main1") Lib.printStack("main2") } + Lib.track { + Lib.track { + Lib.printStack("main3") + Lib.printStack("main4") + } + } + Lib.fact(0) { + Lib.fact(2) { + Lib.printStack("main1", -1) + Lib.printStack("main2", -1) + } + } } } From 7850b1ea6a41c0c7a1baf8e25b591b5305d25b8a Mon Sep 17 00:00:00 2001 From: "Paolo G. Giarrusso" Date: Thu, 23 Aug 2018 14:43:55 +0200 Subject: [PATCH 18/19] Test parameters from other files --- tests/run/i4947b.check | 12 ++++++++++++ tests/run/i4947b/Lib_1.scala | 3 +++ 2 files changed, 15 insertions(+) diff --git a/tests/run/i4947b.check b/tests/run/i4947b.check index be892da11010..183c31bdfc5d 100644 --- a/tests/run/i4947b.check +++ b/tests/run/i4947b.check @@ -10,15 +10,27 @@ main3: Test$.main(Test_2.scala:9) main4: Test$.main(Test_2.scala:10) track (i = 0): Test$.main(Test_2.scala:13) track (i = 0): Test$.main(Test_2.scala:13) +track: Test$.main(Test_2.scala:13) +track: Test$.main(Test_2.scala:13) +fact: Test$.main(Test_2.scala:13) track (i = 2): Test$.main(Test_2.scala:14) track (i = 2): Test$.main(Test_2.scala:14) +track: Test$.main(Test_2.scala:14) +track: Test$.main(Test_2.scala:14) +fact: Test$.main(Test_2.scala:14) main1 (i = -1): Test$.main(Test_2.scala:15) main2 (i = -1): Test$.main(Test_2.scala:16) track (i = 1): Test$.main(Test_2.scala:14) track (i = 1): Test$.main(Test_2.scala:14) +track: Test$.main(Test_2.scala:14) +track: Test$.main(Test_2.scala:14) +fact: Test$.main(Test_2.scala:14) main1 (i = -1): Test$.main(Test_2.scala:15) main2 (i = -1): Test$.main(Test_2.scala:16) track (i = 0): Test$.main(Test_2.scala:14) track (i = 0): Test$.main(Test_2.scala:14) +track: Test$.main(Test_2.scala:14) +track: Test$.main(Test_2.scala:14) +fact: Test$.main(Test_2.scala:14) main1 (i = -1): Test$.main(Test_2.scala:15) main2 (i = -1): Test$.main(Test_2.scala:16) diff --git a/tests/run/i4947b/Lib_1.scala b/tests/run/i4947b/Lib_1.scala index 778a40cd51be..8f0799b9cc18 100644 --- a/tests/run/i4947b/Lib_1.scala +++ b/tests/run/i4947b/Lib_1.scala @@ -15,6 +15,9 @@ object Lib { transparent def fact[T](transparent i: Int)(f: => T): Int = { printStack("track", i) printStack("track", i) + track { + printStack("fact") + } f if (i == 0) 1 From 4c51bc337d50faba3fed7e8f28cb9394d5cf73dc Mon Sep 17 00:00:00 2001 From: "Paolo G. Giarrusso" Date: Thu, 23 Aug 2018 17:20:08 +0200 Subject: [PATCH 19/19] Use correct API ctx.source --- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 183e58113423..5f8865ceb451 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -152,6 +152,7 @@ object Inliner { if (enclosingInlineds.nonEmpty) inlined // Remove in the outer most inlined call else { val inlinedAtPos = inlined.call.pos + val callSourceFile = ctx.source.file /** Removes all Inlined trees, replacing them with blocks. * Repositions all trees directly inside an inlined expansion of a non empty call to the position of the call. @@ -164,7 +165,7 @@ object Inliner { case _ => val transformed = super.transform(tree) enclosingInlineds match { - case call :: _ if call.symbol.sourceFile != ctx.owner.sourceFile => + case call :: _ if call.symbol.sourceFile != callSourceFile => // Until we implement JSR-45, we cannot represent in output positions in other source files. // So, reposition inlined code from other files with the call position: transformed.withPos(inlinedAtPos)