diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala index d26557d1f338..1f82f55f0dc0 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala @@ -374,9 +374,31 @@ class TastyImpl(val rootContext: Contexts.Context) extends scala.tasty.Tasty { s } object Block extends BlockExtractor { - def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = x match { - case x: tpd.Block @unchecked => Some((x.stats, x.expr)) - case _ => None + def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = normalizedLoops(x) match { + case Trees.Block(_, expr) if expr.symbol.is(Flags.Label) => None // while or doWhile loops + case Trees.Block(stats, expr) => Some((stats, expr)) + case _ => None + } + /** Normilizes non Blocks. + * i) Put `while` and `doWhile` loops in thier own blocks: `{ def while$() = ...; while$() }` + */ + private def normalizedLoops(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { + case block: tpd.Block if block.stats.size > 1 => + def normalizeInnerLoops(stats: List[tpd.Tree]): List[tpd.Tree] = stats match { + case (x: tpd.DefDef) :: y :: xs if y.symbol.is(Flags.Label) => + tpd.Block(x :: Nil, y) :: normalizeInnerLoops(xs) + case x :: xs => x :: normalizeInnerLoops(xs) + case Nil => Nil + } + if (block.expr.symbol.is(Flags.Label)) { + val stats1 = normalizeInnerLoops(block.stats.init) + val normalLoop = tpd.Block(block.stats.last :: Nil, block.expr) + tpd.Block(stats1, normalLoop) + } else { + val stats1 = normalizeInnerLoops(block.stats) + tpd.cpy.Block(block)(stats1, block.expr) + } + case _ => tree } } @@ -441,6 +463,28 @@ class TastyImpl(val rootContext: Contexts.Context) extends scala.tasty.Tasty { s } } + object While extends WhileExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match { + case Trees.Block((ddef: tpd.DefDef) :: Nil, expr) if expr.symbol.is(Flags.Label) && expr.symbol.name == nme.WHILE_PREFIX => + val Trees.If(cond, Trees.Block(bodyStats, _), _) = ddef.rhs + Some((cond, loopBody(bodyStats))) + case _ => None + } + } + + object DoWhile extends DoWhileExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match { + case Trees.Block((ddef: tpd.DefDef) :: Nil, expr) if expr.symbol.is(Flags.Label) && expr.symbol.name == nme.DO_WHILE_PREFIX => + val Trees.Block(bodyStats, Trees.If(cond, _, _)) = ddef.rhs + Some((loopBody(bodyStats), cond)) + case _ => None + } + } + + private def loopBody(stats: List[tpd.Tree])(implicit ctx: Context): tpd.Tree = stats match { + case body :: Nil => body + case stats => tpd.Block(stats.init, stats.last) + } } // ----- CaseDef -------------------------------------------------- diff --git a/library/src/scala/tasty/Tasty.scala b/library/src/scala/tasty/Tasty.scala index d5dda31f3dd8..a59ee6bad34a 100644 --- a/library/src/scala/tasty/Tasty.scala +++ b/library/src/scala/tasty/Tasty.scala @@ -339,6 +339,17 @@ abstract class Tasty { tasty => def unapply(x: Term)(implicit ctx: Context): Option[(Term, Int, Type)] } + val While: WhileExtractor + abstract class WhileExtractor { + /** Extractor for while loops. Matches `while () ` and returns (, ) */ + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] + } + + val DoWhile: DoWhileExtractor + abstract class DoWhileExtractor { + /** Extractor for do while loops. Matches `do while ()` and returns (, ) */ + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] + } } // ----- CaseDef -------------------------------------------------- diff --git a/library/src/scala/tasty/util/ShowSourceCode.scala b/library/src/scala/tasty/util/ShowSourceCode.scala index b9f613c69716..8b963bdc1c4b 100644 --- a/library/src/scala/tasty/util/ShowSourceCode.scala +++ b/library/src/scala/tasty/util/ShowSourceCode.scala @@ -204,33 +204,15 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty this } - case While(cond, stats) => + case Term.While(cond, body) => this += "while (" printTree(cond) this += ") " - stats match { - case stat :: Nil => - printTree(stat) - case stats => - this += "{" - indented { - printStats(stats.init, stats.last) - } - this += lineBreak() += "}" - } + printTree(body) - case DoWhile(stats, cond) => + case Term.DoWhile(body, cond) => this += "do " - stats match { - case stat :: Nil => - printTree(stat) - case stats => - this += "{" - indented { - printStats(stats.init, stats.last) - } - this += lineBreak() += "}" - } + printTree(body) this += " while (" printTree(cond) this += ")" @@ -349,14 +331,11 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty printTree(rhs) case Term.Block(stats0, expr) => - def shouldNotBePrinted(tree: Tree): Boolean = tree match { - case Term.Apply(Term.Ident("while$" | "doWhile$"), _) => true - case tree @ ValDef(_, _, _) => tree.flags.isObject - case _ => false + val stats = stats0.filter { + case tree @ ValDef(_, _, _) => !tree.flags.isObject + case _ => true } - val stats = stats0.filterNot(shouldNotBePrinted) - expr match { case Term.Lambda(_, _) => // Decompile lambda from { def annon$(...) = ...; closure(annon$, ...)} @@ -368,11 +347,8 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty this += ")" case _ => this += "{" - val (stats1, expr1) = - if (shouldNotBePrinted(expr)) (stats.init, stats.last) - else (stats, expr) indented { - printStats(stats1, expr1) + printStats(stats, expr) } this += lineBreak() += "}" } @@ -441,7 +417,6 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty // Avoid accidental application of opening `{` on next line with a double break val next = if (nextStats.isEmpty) expr else nextStats.head next match { - case Term.Block(DefDef("while$" | "doWhile$", _, _, _, _) :: Nil, _) => this += lineBreak() case Term.Block(_, _) => this += doubleLineBreak() case Term.Inlined(_, _, _) => this += doubleLineBreak() case _ => this += lineBreak() @@ -1073,7 +1048,6 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty private def escapedString(str: String): String = str flatMap escapedChar } - private object SpecialOp { def unapply(arg: Term)(implicit ctx: Context): Option[(String, List[Term])] = arg match { case arg@Term.Apply(fn, args) => @@ -1086,22 +1060,6 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty } } - private object While { - def unapply(arg: Tree)(implicit ctx: Context): Option[(Term, List[Statement])] = arg match { - case DefDef("while$", _, _, _, Some(Term.If(cond, Term.Block(bodyStats, _), _))) => Some((cond, bodyStats)) - case Term.Block(List(tree), _) => unapply(tree) - case _ => None - } - } - - private object DoWhile { - def unapply(arg: Tree)(implicit ctx: Context): Option[(List[Statement], Term)] = arg match { - case DefDef("doWhile$", _, _, _, Some(Term.Block(body, Term.If(cond, _, _)))) => Some((body, cond)) - case Term.Block(List(tree), _) => unapply(tree) - case _ => None - } - } - private object Annotation { def unapply(arg: Tree)(implicit ctx: Context): Option[(TypeTree, List[Term])] = arg match { case Term.New(annot) => Some((annot, Nil)) diff --git a/tests/pos/tasty/definitions.scala b/tests/pos/tasty/definitions.scala index 823bd9be7c1a..c5bc08dbad86 100644 --- a/tests/pos/tasty/definitions.scala +++ b/tests/pos/tasty/definitions.scala @@ -85,6 +85,8 @@ object definitions { case Return(expr: Term) case Repeated(args: List[Term]) case SelectOuter(from: Term, levels: Int, target: Type) // can be generated by inlining + case While(cond: Term, body: Term) + case DoWhile(body: Term, cond: Term) } /** Trees denoting types */