From 3467ebc33d72f5ed26d4ca45a7d1dacf531a4b4c Mon Sep 17 00:00:00 2001
From: "Paolo G. Giarrusso"
Date: Mon, 27 Aug 2018 14:52:52 +0200
Subject: [PATCH 1/7] Demonstrate regression in #4949 for positions of
quote-inlined code
---
tests/run/i4947e.check | 7 +++++++
tests/run/i4947e/Macro_1.scala | 11 +++++++++++
tests/run/i4947e/Test_2.scala | 11 +++++++++++
3 files changed, 29 insertions(+)
create mode 100644 tests/run/i4947e.check
create mode 100644 tests/run/i4947e/Macro_1.scala
create mode 100644 tests/run/i4947e/Test_2.scala
diff --git a/tests/run/i4947e.check b/tests/run/i4947e.check
new file mode 100644
index 000000000000..8e25e775d012
--- /dev/null
+++ b/tests/run/i4947e.check
@@ -0,0 +1,7 @@
+assertImpl: Test$.main(Test_2.scala:7)
+true
+assertImpl: Test$.main(Test_2.scala:8)
+false
+assertImpl: Test$.main(Test_2.scala:9)
+hi: Test$.main(Test_2.scala:9)
+false
diff --git a/tests/run/i4947e/Macro_1.scala b/tests/run/i4947e/Macro_1.scala
new file mode 100644
index 000000000000..175a642be819
--- /dev/null
+++ b/tests/run/i4947e/Macro_1.scala
@@ -0,0 +1,11 @@
+import scala.quoted._
+
+object Macros {
+ def printStack(tag: String): Unit = {
+ println(tag + ": "+ new Exception().getStackTrace().apply(1))
+ }
+ def assertImpl(expr: Expr[Boolean]) = '{
+ printStack("assertImpl")
+ println(~expr)
+ }
+}
diff --git a/tests/run/i4947e/Test_2.scala b/tests/run/i4947e/Test_2.scala
new file mode 100644
index 000000000000..71e671b15ac1
--- /dev/null
+++ b/tests/run/i4947e/Test_2.scala
@@ -0,0 +1,11 @@
+object Test {
+
+ rewrite def assert2(expr: => Boolean): Unit = ~Macros.assertImpl('(expr))
+
+ def main(args: Array[String]): Unit = {
+ val x = 1
+ assert2(x != 0)
+ assert2(x == 0)
+ assert2 { Macros.printStack("hi"); x == 0 }
+ }
+}
From 31b0bf5565d22872ee14260a70e2d8cc94b90bd9 Mon Sep 17 00:00:00 2001
From: Nicolas Stucki
Date: Tue, 28 Aug 2018 14:39:40 +0200
Subject: [PATCH 2/7] Remove deadcode
---
.../src/dotty/tools/dotc/core/quoted/PickledQuotes.scala | 6 ------
1 file changed, 6 deletions(-)
diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
index bedcd8c05724..eaa3684d03c8 100644
--- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
+++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
@@ -22,12 +22,6 @@ import scala.reflect.ClassTag
object PickledQuotes {
import tpd._
- /** Pickle the tree of the quoted.Expr */
- def pickleExpr(tree: Tree)(implicit ctx: Context): scala.quoted.Expr[Any] = {
- val pickled = pickleQuote(tree)
- scala.runtime.quoted.Unpickler.unpickleExpr(pickled, Nil)
- }
-
/** Pickle the tree of the quote into strings */
def pickleQuote(tree: Tree)(implicit ctx: Context): scala.runtime.quoted.Unpickler.Pickled = {
if (ctx.reporter.hasErrors) Nil
From 45cee034fbb28eb7d2fa4465d1464bec59d0fd3a Mon Sep 17 00:00:00 2001
From: Nicolas Stucki
Date: Tue, 28 Aug 2018 15:12:37 +0200
Subject: [PATCH 3/7] Add missing Inlined nodes on quotes
---
.../tools/dotc/printing/RefinedPrinter.scala | 4 ++--
.../dotty/tools/dotc/transform/ReifyQuotes.scala | 15 ++++++++++++++-
.../src/dotty/tools/dotc/transform/Splicer.scala | 8 ++++----
tests/run/i4947e.check | 3 ++-
tests/run/i4947e/Test_2.scala | 6 +++++-
5 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index 5f42fa346f27..38a3c315f75a 100644
--- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -391,8 +391,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case SeqLiteral(elems, elemtpt) =>
"[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]"
case tree @ Inlined(call, bindings, body) =>
- (("/* inlined from " ~ toText(call) ~ " */ ") `provided`
- !call.isEmpty && !homogenizedView && !ctx.settings.YshowNoInline.value) ~
+ (("/* inlined from " ~ (if (call.isEmpty) "outside" else toText(call)) ~ " */ ") `provided`
+ !homogenizedView && !ctx.settings.YshowNoInline.value) ~
blockText(bindings :+ body)
case tpt: untpd.DerivedTypeTree =>
""
diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
index e7622d6d0af7..1ae8e9d19f2f 100644
--- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
+++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
@@ -372,7 +372,20 @@ class ReifyQuotes extends MacroTransformWithImplicits {
capturers(body.symbol)(body)
case _=>
val (body1, splices) = nested(isQuote = true).split(body)
- if (level == 0 && !ctx.inRewriteMethod) pickledQuote(body1, splices, body.tpe, isType).withPos(quote.pos)
+ if (level == 0 && !ctx.inRewriteMethod) {
+ val body2 =
+ if (body1.isType) body1
+ else {
+ // Leave only a call trace consisting of
+ // - a reference to the top-level class from which the call was inlined,
+ // - the call's position
+ // in the call field of an Inlined node.
+ // The trace has enough info to completely reconstruct positions.
+ val callTrace = Ident(ctx.owner.topLevelClass.typeRef).withPos(body1.pos)
+ Inlined(callTrace, Nil, body1)
+ }
+ pickledQuote(body2, splices, body.tpe, isType).withPos(quote.pos)
+ }
else {
// In top-level splice in a rewrite def. Keep the tree as it is, it will be transformed at inline site.
body
diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala
index 27cb275e7e4c..005569c87bf1 100644
--- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala
@@ -40,8 +40,10 @@ object Splicer {
val interpreter = new Interpreter(pos, classLoader)
try {
// Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
- val interpreted = interpreter.interpret[scala.quoted.Expr[Any]](tree)
- interpreted.fold(tree)(x => PickledQuotes.quotedExprToTree(x))
+ val interpretedExpr = interpreter.interpret[scala.quoted.Expr[Any]](tree)
+ val interpretedTree = interpretedExpr.fold(tree)(x => PickledQuotes.quotedExprToTree(x))
+ // Cancel the current inline trace. The interpreted tree will start with it's own trace.
+ Inlined(EmptyTree, Nil, interpretedTree)
}
catch {
case ex: scala.quoted.QuoteError =>
@@ -286,8 +288,6 @@ object Splicer {
case NamedArg(_, arg) => interpretTree(arg)
case Ident(name) if env.contains(name) => env(name)
- case Inlined(EmptyTree, Nil, expansion) => interpretTree(expansion)
-
case _ => unexpectedTree(tree)
}
diff --git a/tests/run/i4947e.check b/tests/run/i4947e.check
index 8e25e775d012..1e67df692f1e 100644
--- a/tests/run/i4947e.check
+++ b/tests/run/i4947e.check
@@ -3,5 +3,6 @@ true
assertImpl: Test$.main(Test_2.scala:8)
false
assertImpl: Test$.main(Test_2.scala:9)
-hi: Test$.main(Test_2.scala:9)
+hi: Test$.main(Test_2.scala:10)
+hi again: Test$.main(Test_2.scala:11)
false
diff --git a/tests/run/i4947e/Test_2.scala b/tests/run/i4947e/Test_2.scala
index 71e671b15ac1..75509fcc59e1 100644
--- a/tests/run/i4947e/Test_2.scala
+++ b/tests/run/i4947e/Test_2.scala
@@ -6,6 +6,10 @@ object Test {
val x = 1
assert2(x != 0)
assert2(x == 0)
- assert2 { Macros.printStack("hi"); x == 0 }
+ assert2 {
+ Macros.printStack("hi")
+ Macros.printStack("hi again")
+ x == 0
+ }
}
}
From 99e911dd68984b1d8fa0b2d7f53140a2d177f519 Mon Sep 17 00:00:00 2001
From: Nicolas Stucki
Date: Tue, 4 Sep 2018 16:22:23 +0200
Subject: [PATCH 4/7] Fix placement of Inlined cancelation
---
.../dotty/tools/dotc/transform/PostTyper.scala | 2 ++
.../dotty/tools/dotc/transform/ReifyQuotes.scala | 7 +++++--
.../src/dotty/tools/dotc/transform/Splicer.scala | 4 +---
tests/run/i4947f.check | 8 ++++++++
tests/run/i4947f/Macro_1.scala | 14 ++++++++++++++
tests/run/i4947f/Test_2.scala | 15 +++++++++++++++
6 files changed, 45 insertions(+), 5 deletions(-)
create mode 100644 tests/run/i4947f.check
create mode 100644 tests/run/i4947f/Macro_1.scala
create mode 100644 tests/run/i4947f/Test_2.scala
diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
index d12420f1313b..3658cda1c43a 100644
--- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
+++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
@@ -244,6 +244,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
// be duplicated
// 2. To enable correct pickling (calls can share symbols with the inlined code, which
// would trigger an assertion when pickling).
+ //
+ // This is the same trace that is inserted in ReifyQuotes.quotation
val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(inlineContext(call)))
case tree: Template =>
diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
index 1ae8e9d19f2f..e1cd6adfc4c3 100644
--- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
+++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
@@ -381,7 +381,9 @@ class ReifyQuotes extends MacroTransformWithImplicits {
// - the call's position
// in the call field of an Inlined node.
// The trace has enough info to completely reconstruct positions.
- val callTrace = Ident(ctx.owner.topLevelClass.typeRef).withPos(body1.pos)
+ //
+ // This is the same trace that is inserted in PostTyper.transform
+ val callTrace = Ident(ctx.owner.topLevelClass.typeRef).withPos(quote.pos)
Inlined(callTrace, Nil, body1)
}
pickledQuote(body2, splices, body.tpe, isType).withPos(quote.pos)
@@ -435,7 +437,8 @@ class ReifyQuotes extends MacroTransformWithImplicits {
}
else if (level == 1) {
val (body1, quotes) = nested(isQuote = false).split(splice.qualifier)
- makeHole(body1, quotes, splice.tpe).withPos(splice.pos)
+ val hole = makeHole(body1, quotes, splice.tpe).withPos(splice.pos)
+ if (splice.isType) hole else Inlined(EmptyTree, Nil, hole)
}
else if (enclosingInlineds.nonEmpty) { // level 0 in an inlined call
val spliceCtx = ctx.outer // drop the last `inlineContext`
diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala
index 005569c87bf1..c3ef2e6a549d 100644
--- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala
+++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala
@@ -41,9 +41,7 @@ object Splicer {
try {
// Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
val interpretedExpr = interpreter.interpret[scala.quoted.Expr[Any]](tree)
- val interpretedTree = interpretedExpr.fold(tree)(x => PickledQuotes.quotedExprToTree(x))
- // Cancel the current inline trace. The interpreted tree will start with it's own trace.
- Inlined(EmptyTree, Nil, interpretedTree)
+ interpretedExpr.fold(tree)(x => PickledQuotes.quotedExprToTree(x))
}
catch {
case ex: scala.quoted.QuoteError =>
diff --git a/tests/run/i4947f.check b/tests/run/i4947f.check
new file mode 100644
index 000000000000..1e67df692f1e
--- /dev/null
+++ b/tests/run/i4947f.check
@@ -0,0 +1,8 @@
+assertImpl: Test$.main(Test_2.scala:7)
+true
+assertImpl: Test$.main(Test_2.scala:8)
+false
+assertImpl: Test$.main(Test_2.scala:9)
+hi: Test$.main(Test_2.scala:10)
+hi again: Test$.main(Test_2.scala:11)
+false
diff --git a/tests/run/i4947f/Macro_1.scala b/tests/run/i4947f/Macro_1.scala
new file mode 100644
index 000000000000..ea9205f19fac
--- /dev/null
+++ b/tests/run/i4947f/Macro_1.scala
@@ -0,0 +1,14 @@
+import scala.quoted._
+
+object Macros {
+ def printStack(tag: String): Unit = {
+ println(tag + ": "+ new Exception().getStackTrace().apply(1))
+ }
+ def assertImpl(expr: Expr[Boolean]) = '{
+ printStack("assertImpl")
+ println(~expr)
+ }
+
+ rewrite def assert2(expr: => Boolean): Unit = ~Macros.assertImpl('(expr))
+
+}
diff --git a/tests/run/i4947f/Test_2.scala b/tests/run/i4947f/Test_2.scala
new file mode 100644
index 000000000000..6a96b58b4d34
--- /dev/null
+++ b/tests/run/i4947f/Test_2.scala
@@ -0,0 +1,15 @@
+object Test {
+
+ import Macros._
+
+ def main(args: Array[String]): Unit = {
+ val x = 1
+ assert2(x != 0)
+ assert2(x == 0)
+ assert2 {
+ Macros.printStack("hi")
+ Macros.printStack("hi again")
+ x == 0
+ }
+ }
+}
From 21555cb2d7383a829629b0df30639372ecc8959e Mon Sep 17 00:00:00 2001
From: Nicolas Stucki
Date: Tue, 4 Sep 2018 16:33:30 +0200
Subject: [PATCH 5/7] Factor out inline trace code
---
.../dotty/tools/dotc/transform/PostTyper.scala | 17 +++--------------
.../tools/dotc/transform/ReifyQuotes.scala | 13 ++-----------
.../src/dotty/tools/dotc/typer/Inliner.scala | 9 +++++++++
3 files changed, 14 insertions(+), 25 deletions(-)
diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
index 3658cda1c43a..828c509493d1 100644
--- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
+++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
@@ -4,7 +4,8 @@ package transform
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
import scala.collection.mutable
import core._
-import typer.Checking
+import dotty.tools.dotc.typer.Checking
+import dotty.tools.dotc.typer.Inliner
import Types._, Contexts._, Names._, Flags._, DenotTransformers._
import SymDenotations._, StdNames._, Annotations._, Trees._, Scopes._
import Decorators._
@@ -234,19 +235,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
super.transform(tree1)
}
case Inlined(call, bindings, expansion) if !call.isEmpty =>
- // Leave only a call trace consisting of
- // - a reference to the top-level class from which the call was inlined,
- // - the call's position
- // in the call field of an Inlined node.
- // The trace has enough info to completely reconstruct positions.
- // The minimization is done for two reasons:
- // 1. To save space (calls might contain large inline arguments, which would otherwise
- // be duplicated
- // 2. To enable correct pickling (calls can share symbols with the inlined code, which
- // would trigger an assertion when pickling).
- //
- // This is the same trace that is inserted in ReifyQuotes.quotation
- val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
+ val callTrace = Inliner.inlineCallTrace(call.symbol, call.pos)
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(inlineContext(call)))
case tree: Template =>
withNoCheckNews(tree.parents.flatMap(newPart)) {
diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
index e1cd6adfc4c3..35923aefefda 100644
--- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
+++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
@@ -15,6 +15,7 @@ import typer.Implicits.SearchFailureType
import scala.collection.mutable
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.quoted._
+import dotty.tools.dotc.typer.Inliner
import dotty.tools.dotc.util.SourcePosition
@@ -375,17 +376,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
if (level == 0 && !ctx.inRewriteMethod) {
val body2 =
if (body1.isType) body1
- else {
- // Leave only a call trace consisting of
- // - a reference to the top-level class from which the call was inlined,
- // - the call's position
- // in the call field of an Inlined node.
- // The trace has enough info to completely reconstruct positions.
- //
- // This is the same trace that is inserted in PostTyper.transform
- val callTrace = Ident(ctx.owner.topLevelClass.typeRef).withPos(quote.pos)
- Inlined(callTrace, Nil, body1)
- }
+ else Inlined(Inliner.inlineCallTrace(ctx.owner, quote.pos), Nil, body1)
pickledQuote(body2, splices, body.tpe, isType).withPos(quote.pos)
}
else {
diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala
index ed09a387f7dd..e8e28a2e95f6 100644
--- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala
@@ -171,6 +171,15 @@ object Inliner {
(new Reposition).transformInline(inlined)
}
}
+
+ /** Leave only a call trace consisting of
+ * - a reference to the top-level class from which the call was inlined,
+ * - the call's position
+ * in the call field of an Inlined node.
+ * The trace has enough info to completely reconstruct positions.
+ */
+ def inlineCallTrace(callSym: Symbol, pos: Position)(implicit ctx: Context): Tree =
+ Ident(callSym.topLevelClass.typeRef).withPos(pos)
}
/** Produces an inlined version of `call` via its `inlined` method.
From 84666e757c90df2a7b33d66992e6a1f5c4e4efbd Mon Sep 17 00:00:00 2001
From: Nicolas Stucki
Date: Tue, 4 Sep 2018 17:35:39 +0200
Subject: [PATCH 6/7] Add to pickling blacklist
---
compiler/test/dotc/run-test-pickling.blacklist | 2 ++
1 file changed, 2 insertions(+)
diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist
index 42607d1c408f..085061ffa842 100644
--- a/compiler/test/dotc/run-test-pickling.blacklist
+++ b/compiler/test/dotc/run-test-pickling.blacklist
@@ -20,6 +20,8 @@ i4803d
i4803e
i4803f
i4947b
+i4947e
+i4947f
implicitMatch.scala
implicitShortcut
lazy-implicit-lists.scala
From accc8fb04497d13d19e934b7e36271acc4ddd753 Mon Sep 17 00:00:00 2001
From: Nicolas Stucki
Date: Wed, 5 Sep 2018 13:30:55 +0200
Subject: [PATCH 7/7] Do not print inlined calls tag in Expr.show
Also move logic that flattens blocks to the decompiler and make it stricter
---
.../dotty/tools/dotc/quoted/TreeCleaner.scala | 11 --
.../src/scala/tasty/util/ShowSourceCode.scala | 107 +++++++++++++-----
tests/pos/i2104b.decompiled | 10 +-
tests/pos/i4526b.decompiled | 12 +-
tests/pos/lambda.decompiled | 6 +-
tests/pos/simpleDoWhile.decompiled | 4 +-
tests/pos/simpleInline.decompiled | 4 +-
tests/pos/simpleMatchCase.decompiled | 10 +-
tests/pos/simpleWhile.decompiled | 4 +-
tests/pos/t3869.decompiled | 4 +-
tests/pos/t704.decompiled | 7 +-
tests/run-with-compiler/i3847-b.check | 4 +-
tests/run-with-compiler/i4350.check | 8 +-
.../quote-impure-by-name.check | 4 +-
tests/run-with-compiler/quote-nested-3.check | 1 -
.../quote-show-blocks-raw.check | 36 ------
.../quote-show-blocks-raw.scala | 25 ----
.../quote-unrolled-foreach.check | 2 -
tests/run-with-compiler/shonan-hmm.check | 16 +--
tests/run/literals.decompiled | 10 +-
20 files changed, 106 insertions(+), 179 deletions(-)
delete mode 100644 tests/run-with-compiler/quote-show-blocks-raw.check
delete mode 100644 tests/run-with-compiler/quote-show-blocks-raw.scala
diff --git a/compiler/src/dotty/tools/dotc/quoted/TreeCleaner.scala b/compiler/src/dotty/tools/dotc/quoted/TreeCleaner.scala
index 6035d8900c6c..1562bda08d90 100644
--- a/compiler/src/dotty/tools/dotc/quoted/TreeCleaner.scala
+++ b/compiler/src/dotty/tools/dotc/quoted/TreeCleaner.scala
@@ -30,17 +30,6 @@ class TreeCleaner extends tpd.TreeMap {
}
super.transform(tree0) match {
- case Block(Nil, expr1) => expr1
- case Block(stats1, expr1) =>
- val flatStats = stats1.flatMap {
- case Block(stats2, expr2) => stats2 ::: expr2 :: Nil
- case Literal(Constant(())) => Nil
- case stat => stat :: Nil
- }
- expr1 match {
- case Block(stats3, expr3) => Block(flatStats ::: stats3, expr3)
- case expr3 => Block(flatStats, expr3)
- }
case tree1: TypeTree => TypeTree(tree1.tpe.subst(aliasesSyms, aliasesTypes))
case tree1: Ident => aliases.get(tree1.symbol).getOrElse(tree1)
case tree1 => tree1
diff --git a/library/src/scala/tasty/util/ShowSourceCode.scala b/library/src/scala/tasty/util/ShowSourceCode.scala
index f67d8c8bc8dc..8bc0fa47c3a5 100644
--- a/library/src/scala/tasty/util/ShowSourceCode.scala
+++ b/library/src/scala/tasty/util/ShowSourceCode.scala
@@ -237,6 +237,17 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
printTree(body) += " while "
inParens(printTree(cond))
+ case IsDefDef(ddef @ DefDef(name, targs, argss, _, rhsOpt)) if name.startsWith("$anonfun") =>
+ // Decompile lambda definition
+ assert(targs.isEmpty)
+ val args :: Nil = argss
+ val Some(rhs) = rhsOpt
+ inParens {
+ printArgsDefs(args)
+ this += " => "
+ printTree(rhs)
+ }
+
case IsDefDef(ddef @ DefDef(name, targs, argss, tpt, rhs)) =>
printDefAnnotations(ddef)
@@ -363,34 +374,13 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
case IsValDef(tree) => !tree.symbol.flags.isObject
case _ => true
}
+ printFlatBlock(stats, expr)
- expr match {
- case Term.Lambda(_, _) =>
- // Decompile lambda from { def annon$(...) = ...; closure(annon$, ...)}
- assert(stats.size == 1)
- val DefDef(_, _, args :: Nil, _, Some(rhs)) :: Nil = stats
- inParens {
- printArgsDefs(args)
- this += " => "
- printTree(rhs)
- }
- case _ =>
- this += "{"
- indented {
- printStats(stats, expr)
- }
- this += lineBreak() += "}"
- }
-
- case Term.Inlined(call, bindings, expansion) => // FIXME: Don't print Inlined with empty calls?
- this += "{ // inlined"
- indented {
- printStats(bindings, expansion)
- }
- this += lineBreak() += "}"
+ case Term.Inlined(_, bindings, expansion) =>
+ printFlatBlock(bindings, expansion)
case Term.Lambda(meth, tpt) =>
- // Printed in Term.Block branch
+ // Printed in by it's DefDef
this
case Term.If(cond, thenp, elsep) =>
@@ -433,15 +423,72 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
}
+ def flatBlock(stats: List[Statement], expr: Term): (List[Statement], Term) = {
+ val flatStats = List.newBuilder[Statement]
+ def extractFlatStats(stat: Statement): Unit = stat match {
+ case Term.Block(stats1, expr1) =>
+ stats1.foreach(extractFlatStats)
+ extractFlatStats(expr1)
+ case Term.Inlined(_, bindings, expansion) =>
+ bindings.foreach(extractFlatStats)
+ extractFlatStats(expansion)
+ case Term.Literal(Constant.Unit()) => // ignore
+ case stat => flatStats += stat
+ }
+ def extractFlatExpr(term: Term): Term = term match {
+ case Term.Block(stats1, expr1) =>
+ stats1.foreach(extractFlatStats)
+ extractFlatExpr(expr1)
+ case Term.Inlined(_, bindings, expansion) =>
+ bindings.foreach(extractFlatStats)
+ extractFlatExpr(expansion)
+ case term => term
+ }
+ stats.foreach(extractFlatStats)
+ val flatExpr = extractFlatExpr(expr)
+ (flatStats.result(), flatExpr)
+ }
+
+ def printFlatBlock(stats: List[Statement], expr: Term): Buffer = {
+ val (stats1, expr1) = flatBlock(stats, expr)
+
+ // Remove Term.Lambda nodes, lambdas are printed by their definition
+ val stats2 = stats1.filter { case Term.Lambda(_, _) => false; case _ => true }
+ val (stats3, expr3) = expr1 match {
+ case Term.Lambda(_, _) =>
+ val init :+ last = stats2
+ (init, last)
+ case _ => (stats2, expr1)
+ }
+
+ if (stats3.isEmpty) {
+ printTree(expr3)
+ } else {
+ this += "{"
+ indented {
+ printStats(stats3, expr3)
+ }
+ this += lineBreak() += "}"
+ }
+ }
+
def printStats(stats: List[Tree], expr: Tree): Unit = {
def printSeparator(next: Tree): Unit = {
// Avoid accidental application of opening `{` on next line with a double break
+ def rec(next: Tree): Unit = next match {
+ case Term.Block(stats, _) if stats.nonEmpty => this += doubleLineBreak()
+ case Term.Inlined(_, bindings, _) if bindings.nonEmpty => this += doubleLineBreak()
+ case Term.Select(qual, _, _) => rec(qual)
+ case Term.Apply(fn, _) => rec(fn)
+ case Term.TypeApply(fn, _) => rec(fn)
+ case _ => this += lineBreak()
+ }
next match {
- case Term.Block(_, _) => this += doubleLineBreak()
- case Term.Inlined(_, _, _) => this += doubleLineBreak()
- case Term.Select(qual, _, _) => printSeparator(qual)
- case Term.Apply(fn, _) => printSeparator(fn)
- case Term.TypeApply(fn, _) => printSeparator(fn)
+ case IsTerm(term) =>
+ flatBlock(Nil, term) match {
+ case (next :: _, _) => rec(next)
+ case (Nil, next) => rec(next)
+ }
case _ => this += lineBreak()
}
}
diff --git a/tests/pos/i2104b.decompiled b/tests/pos/i2104b.decompiled
index 9be9677bea4a..9d240a3391a3 100644
--- a/tests/pos/i2104b.decompiled
+++ b/tests/pos/i2104b.decompiled
@@ -34,10 +34,8 @@ case class Pair[A, B](_1: A, _2: B) {
object Pair extends scala.AnyRef()
/** Decompiled from out/posTestFromTasty/pos/i2104b/Test.class */
object Test {
- def main(args: scala.Array[scala.Predef.String]): scala.Unit = {
- Cons.apply[scala.Option[scala.Int], scala.None.type](scala.Option.apply[scala.Int](1), scala.None) match {
- case Cons(scala.Some(i), scala.None) =>
- ()
- }
+ def main(args: scala.Array[scala.Predef.String]): scala.Unit = Cons.apply[scala.Option[scala.Int], scala.None.type](scala.Option.apply[scala.Int](1), scala.None) match {
+ case Cons(scala.Some(i), scala.None) =>
+ ()
}
-}
\ No newline at end of file
+}
diff --git a/tests/pos/i4526b.decompiled b/tests/pos/i4526b.decompiled
index d16c935f11f4..a4958478ff11 100644
--- a/tests/pos/i4526b.decompiled
+++ b/tests/pos/i4526b.decompiled
@@ -1,11 +1,9 @@
/** Decompiled from out/posTestFromTasty/pos/i4526b/Foo.class */
class Foo() {
- def justdoit(f: scala.Either[scala.Int, scala.Predef.String]): scala.Predef.String = {
- f match {
- case scala.Left(i) =>
- i.toString()
- case scala.Right(s) =>
- (s: java.lang.String)
- }
+ def justdoit(f: scala.Either[scala.Int, scala.Predef.String]): scala.Predef.String = f match {
+ case scala.Left(i) =>
+ i.toString()
+ case scala.Right(s) =>
+ (s: java.lang.String)
}
}
diff --git a/tests/pos/lambda.decompiled b/tests/pos/lambda.decompiled
index ddcdfd5fe5f4..fc1fcf67c6d6 100644
--- a/tests/pos/lambda.decompiled
+++ b/tests/pos/lambda.decompiled
@@ -1,11 +1,7 @@
/** Decompiled from out/posTestFromTasty/pos/lambda/foo/Foo.class */
package foo {
class Foo() {
- {
- ((x: scala.Int) => {
- 2
- })
- }
+ ((x: scala.Int) => 2)
val a: scala.Function1[scala.Int, scala.Int] = ((x: scala.Int) => x.*(x))
}
}
diff --git a/tests/pos/simpleDoWhile.decompiled b/tests/pos/simpleDoWhile.decompiled
index 10a498fc026f..90fbe2fb3d41 100644
--- a/tests/pos/simpleDoWhile.decompiled
+++ b/tests/pos/simpleDoWhile.decompiled
@@ -2,8 +2,6 @@
class Foo() {
def foo: scala.Unit = {
var i: scala.Int = 1
- do {
- i = 0
- } while (i.!=(0))
+ do i = 0 while (i.!=(0))
}
}
diff --git a/tests/pos/simpleInline.decompiled b/tests/pos/simpleInline.decompiled
index 4efaa78ffa2f..24241160e2d3 100644
--- a/tests/pos/simpleInline.decompiled
+++ b/tests/pos/simpleInline.decompiled
@@ -1,7 +1,5 @@
/** Decompiled from out/posTestFromTasty/pos/simpleInline/Foo.class */
class Foo() {
rewrite def foo: scala.Int = 9
- def bar: scala.Int = { // inlined
- 9
- }
+ def bar: scala.Int = 9
}
diff --git a/tests/pos/simpleMatchCase.decompiled b/tests/pos/simpleMatchCase.decompiled
index 4b7727b34ebe..265134cc19e4 100644
--- a/tests/pos/simpleMatchCase.decompiled
+++ b/tests/pos/simpleMatchCase.decompiled
@@ -1,10 +1,8 @@
/** Decompiled from out/posTestFromTasty/pos/simpleMatchCase/Foo.class */
class Foo() {
- def foo: scala.Unit = {
- "c" match {
- case x =>
- scala.Predef.println("a")
- scala.Predef.println("b")
- }
+ def foo: scala.Unit = "c" match {
+ case x =>
+ scala.Predef.println("a")
+ scala.Predef.println("b")
}
}
diff --git a/tests/pos/simpleWhile.decompiled b/tests/pos/simpleWhile.decompiled
index 269c17ab1aa3..12c319714704 100644
--- a/tests/pos/simpleWhile.decompiled
+++ b/tests/pos/simpleWhile.decompiled
@@ -2,8 +2,6 @@
class Foo() {
def foo: scala.Unit = {
var i: scala.Int = 1
- while (i.!=(0)) {
- i = 0
- }
+ while (i.!=(0)) i = 0
}
}
\ No newline at end of file
diff --git a/tests/pos/t3869.decompiled b/tests/pos/t3869.decompiled
index ec1fe8ed28f5..873660e2f03f 100644
--- a/tests/pos/t3869.decompiled
+++ b/tests/pos/t3869.decompiled
@@ -1,7 +1,5 @@
/** Decompiled from out/posTestFromTasty/pos/t3869/Test.class */
object Test {
def f: scala.Unit = try return () finally while (true) ()
- def main(args: scala.Array[scala.Predef.String]): scala.Unit = {
- Test.f
- }
+ def main(args: scala.Array[scala.Predef.String]): scala.Unit = Test.f
}
diff --git a/tests/pos/t704.decompiled b/tests/pos/t704.decompiled
index 64f50b9e9d27..f1cd2fce613a 100644
--- a/tests/pos/t704.decompiled
+++ b/tests/pos/t704.decompiled
@@ -16,11 +16,8 @@ trait E() extends java.lang.Object with D {
val x1: scala.AnyRef = E.this.get_xxxx
scala.Console.println(y)
}
-
- {
- yyyy
- ()
- }
+ yyyy
+ ()
}
}
/** Decompiled from out/posTestFromTasty/pos/t704/Go.class */
diff --git a/tests/run-with-compiler/i3847-b.check b/tests/run-with-compiler/i3847-b.check
index 49278cfb1655..8158391f2453 100644
--- a/tests/run-with-compiler/i3847-b.check
+++ b/tests/run-with-compiler/i3847-b.check
@@ -1,3 +1 @@
-{
- new scala.Array[scala.List[scala.Int]](1)
-}
\ No newline at end of file
+new scala.Array[scala.List[scala.Int]](1)
diff --git a/tests/run-with-compiler/i4350.check b/tests/run-with-compiler/i4350.check
index 7d5fd0304324..45b028ad64cb 100644
--- a/tests/run-with-compiler/i4350.check
+++ b/tests/run-with-compiler/i4350.check
@@ -1,6 +1,2 @@
-{
- null.asInstanceOf[lang.Object]
-}
-{
- null.asInstanceOf[scala.Predef.String]
-}
+null.asInstanceOf[lang.Object]
+null.asInstanceOf[scala.Predef.String]
diff --git a/tests/run-with-compiler/quote-impure-by-name.check b/tests/run-with-compiler/quote-impure-by-name.check
index d29945c196fa..6616ccfa5430 100644
--- a/tests/run-with-compiler/quote-impure-by-name.check
+++ b/tests/run-with-compiler/quote-impure-by-name.check
@@ -1,3 +1 @@
-1 + {{ // inlined
- Index.zero["bar", scala.Tuple2["baz", scala.Unit]]
-}}
+1 + {Index.zero["bar", scala.Tuple2["baz", scala.Unit]]}
diff --git a/tests/run-with-compiler/quote-nested-3.check b/tests/run-with-compiler/quote-nested-3.check
index 1dc1b00982a4..a441ab9cc3e3 100644
--- a/tests/run-with-compiler/quote-nested-3.check
+++ b/tests/run-with-compiler/quote-nested-3.check
@@ -2,6 +2,5 @@
type T = scala.Predef.String
val x: java.lang.String = "foo"
val z: T = x
- ()
(x: java.lang.String)
}
diff --git a/tests/run-with-compiler/quote-show-blocks-raw.check b/tests/run-with-compiler/quote-show-blocks-raw.check
deleted file mode 100644
index 2e67f4f8c2a5..000000000000
--- a/tests/run-with-compiler/quote-show-blocks-raw.check
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- scala.Predef.println(1)
-
- {
- scala.Predef.println(2)
-
- {
- scala.Predef.println(3)
-
- {
- scala.Predef.println(4)
-
- {
- scala.Predef.println(5)
- ()
- }
- }
- }
- }
-}
-{
- {
- {
- {
- {
- ()
- scala.Predef.println(5)
- }
- scala.Predef.println(4)
- }
- scala.Predef.println(3)
- }
- scala.Predef.println(2)
- }
- scala.Predef.println(1)
-}
diff --git a/tests/run-with-compiler/quote-show-blocks-raw.scala b/tests/run-with-compiler/quote-show-blocks-raw.scala
deleted file mode 100644
index 20c142f4144e..000000000000
--- a/tests/run-with-compiler/quote-show-blocks-raw.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import scala.quoted.Toolbox.Default._
-import scala.quoted.Toolbox
-
-import scala.quoted._
-
-object Test {
- def main(args: Array[String]): Unit = {
- implicit val settings: Toolbox.Settings = Toolbox.Settings.make(showRawTree = true)
-
- def a(n: Int, x: Expr[Unit]): Expr[Unit] =
- if (n == 0) x
- else a(n - 1, '{ println(~n.toExpr); ~x })
-
- println(a(5, '()).show)
-
-
- def b(n: Int, x: Expr[Unit]): Expr[Unit] =
- if (n == 0) x
- else b(n - 1, '{ ~x; println(~n.toExpr) })
-
- println(b(5, '()).show)
- }
-
-}
diff --git a/tests/run-with-compiler/quote-unrolled-foreach.check b/tests/run-with-compiler/quote-unrolled-foreach.check
index 6b892dd442bf..a97a6f1d93fe 100644
--- a/tests/run-with-compiler/quote-unrolled-foreach.check
+++ b/tests/run-with-compiler/quote-unrolled-foreach.check
@@ -33,7 +33,6 @@
var i: scala.Int = 0
while (i.<(size)) {
val element: scala.Int = arr.apply(i)
-
((i: scala.Int) => java.lang.System.out.println(i)).apply(element)
i = i.+(1)
}
@@ -86,7 +85,6 @@
var i: scala.Int = 0
while (i.<(size)) {
val element: scala.Int = arr1.apply(i)
-
((x: scala.Int) => scala.Predef.println(x)).apply(element)
i = i.+(1)
}
diff --git a/tests/run-with-compiler/shonan-hmm.check b/tests/run-with-compiler/shonan-hmm.check
index c4f1579e7497..29b8c12e99ec 100644
--- a/tests/run-with-compiler/shonan-hmm.check
+++ b/tests/run-with-compiler/shonan-hmm.check
@@ -45,9 +45,7 @@ List(25, 30, 20, 43, 44)
{
val arr: scala.Array[scala.Array[scala.Int]] = {
- val array: scala.Array[scala.Array[scala.Int]] = dotty.runtime.Arrays.newGenericArray[scala.Array[scala.Int]](5)({
- scala.reflect.ClassTag.apply[scala.Array[scala.Int]](scala.Predef.classOf[scala.Array[scala.Int]])
- })
+ val array: scala.Array[scala.Array[scala.Int]] = dotty.runtime.Arrays.newGenericArray[scala.Array[scala.Int]](5)(scala.reflect.ClassTag.apply[scala.Array[scala.Int]](scala.Predef.classOf[scala.Array[scala.Int]]))
array.update(0, {
val array$2: scala.Array[scala.Int] = new scala.Array[scala.Int](5)
array$2.update(0, 5)
@@ -95,7 +93,6 @@ List(25, 30, 20, 43, 44)
})
array
}
-
((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => {
if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else ()
if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else ()
@@ -111,9 +108,7 @@ List(25, 30, 20, 43, 44)
{
val arr: scala.Array[scala.Array[scala.Int]] = {
- val array: scala.Array[scala.Array[scala.Int]] = dotty.runtime.Arrays.newGenericArray[scala.Array[scala.Int]](5)({
- scala.reflect.ClassTag.apply[scala.Array[scala.Int]](scala.Predef.classOf[scala.Array[scala.Int]])
- })
+ val array: scala.Array[scala.Array[scala.Int]] = dotty.runtime.Arrays.newGenericArray[scala.Array[scala.Int]](5)(scala.reflect.ClassTag.apply[scala.Array[scala.Int]](scala.Predef.classOf[scala.Array[scala.Int]]))
array.update(0, {
val array$2: scala.Array[scala.Int] = new scala.Array[scala.Int](5)
array$2.update(0, 5)
@@ -161,7 +156,6 @@ List(25, 30, 20, 43, 44)
})
array
}
-
((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => {
if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else ()
if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else ()
@@ -177,9 +171,7 @@ List(25, 30, 20, 43, 44)
{
val arr: scala.Array[scala.Array[scala.Int]] = {
- val array: scala.Array[scala.Array[scala.Int]] = dotty.runtime.Arrays.newGenericArray[scala.Array[scala.Int]](5)({
- scala.reflect.ClassTag.apply[scala.Array[scala.Int]](scala.Predef.classOf[scala.Array[scala.Int]])
- })
+ val array: scala.Array[scala.Array[scala.Int]] = dotty.runtime.Arrays.newGenericArray[scala.Array[scala.Int]](5)(scala.reflect.ClassTag.apply[scala.Array[scala.Int]](scala.Predef.classOf[scala.Array[scala.Int]]))
array.update(0, {
val array$2: scala.Array[scala.Int] = new scala.Array[scala.Int](5)
array$2.update(0, 5)
@@ -227,7 +219,6 @@ List(25, 30, 20, 43, 44)
})
array
}
-
((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => {
if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else ()
if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else ()
@@ -287,7 +278,6 @@ List(25, 30, 20, 43, 44)
array.update(4, 5)
array
}
-
((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => {
if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else ()
if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else ()
diff --git a/tests/run/literals.decompiled b/tests/run/literals.decompiled
index 5ede2bfa50ec..0515ab0223c7 100644
--- a/tests/run/literals.decompiled
+++ b/tests/run/literals.decompiled
@@ -30,16 +30,10 @@ object Test {
scala.Predef.print("test ".+(name))
try {
val actual: a = closure
- if (actual.==(expected)) {
- scala.Predef.print(" was successful")
- } else {
- scala.Predef.print(" failed: expected ".+(expected).+(", found ").+(actual))
- }
+ if (actual.==(expected)) scala.Predef.print(" was successful") else scala.Predef.print(" failed: expected ".+(expected).+(", found ").+(actual))
} catch {
case exception: scala.Throwable =>
- {
- scala.Predef.print(" raised exception ".+(exception))
- }
+ scala.Predef.print(" raised exception ".+(exception))
}
scala.Predef.println()
}