From 40d44d3ad25e91c3ef0f1ce53ed0ececbe042d1e Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Mon, 5 Jun 2023 17:24:36 +0200 Subject: [PATCH 1/2] Set missing expansion span for copied inlined node Check if the copied inlined node's expansion exists, and if not set it to the original node's expansion --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 17 ++++++++++++----- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- .../src/dotty/tools/dotc/inlines/Inlines.scala | 2 +- .../dotty/tools/dotc/quoted/PickledQuotes.scala | 2 +- .../dotty/tools/dotc/transform/BetaReduce.scala | 2 +- .../tools/dotc/transform/PickleQuotes.scala | 2 +- .../dotty/tools/dotc/transform/PostTyper.scala | 4 ++-- .../scala/quoted/runtime/impl/QuotesImpl.scala | 2 +- 8 files changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index b273bbf01e95..f1e97e713742 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1341,10 +1341,17 @@ object Trees { case tree: SeqLiteral if (elems eq tree.elems) && (elemtpt eq tree.elemtpt) => tree case _ => finalize(tree, untpd.SeqLiteral(elems, elemtpt)(sourceFile(tree))) } - def Inlined(tree: Tree)(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = tree match { - case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree - case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) - } + // Positions of trees are automatically pushed down except when we reach an Inlined tree. Therefore, we + // make sure the new expansion has a position by copying the one of the original Inlined tree. + def Inlined(tree: Inlined)(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = + if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) then tree + else + // Copy the span from the original Inlined tree if the new expansion doesn't have a span. + val expansionWithSpan = + if expansion.span.exists then expansion + else expansion.withSpan(tree.expansion.span) + finalize(tree, untpd.Inlined(call, bindings, expansionWithSpan)(sourceFile(tree))) + def Quote(tree: Tree)(body: Tree, tags: List[Tree])(using Context): Quote = tree match { case tree: Quote if (body eq tree.body) && (tags eq tree.tags) => tree case _ => finalize(tree, untpd.Quote(body, tags)(sourceFile(tree))) @@ -1549,7 +1556,7 @@ object Trees { cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer)) case SeqLiteral(elems, elemtpt) => cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) - case Inlined(call, bindings, expansion) => + case tree @ Inlined(call, bindings, expansion) => cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)(using inlineContext(call))) case TypeTree() => tree diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 0d3085f13f1e..d3760e553767 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -748,7 +748,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } - override def Inlined(tree: Tree)(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = { + override def Inlined(tree: Inlined)(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = { val tree1 = untpdCpy.Inlined(tree)(call, bindings, expansion) tree match { case tree: Inlined if sameTypes(bindings, tree.bindings) && (expansion.tpe eq tree.expansion.tpe) => diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index bcc10ffa6db8..d0ef66880388 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -117,7 +117,7 @@ object Inlines: case Block(stats, expr) => bindings ++= stats.map(liftPos) liftBindings(expr, liftPos) - case Inlined(call, stats, expr) => + case tree @ Inlined(call, stats, expr) => bindings ++= stats.map(liftPos) val lifter = liftFromInlined(call) cpy.Inlined(tree)(call, Nil, liftBindings(expr, liftFromInlined(call).transform(_))) diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala index 7596549fe401..89ca711c18e6 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -81,7 +81,7 @@ object PickledQuotes { /** Unpickle the tree contained in the TastyExpr */ def unpickleTerm(pickled: String | List[String], typeHole: TypeHole, termHole: ExprHole)(using Context): Tree = { - val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = false)) + val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = false)).asInstanceOf[Inlined] val Inlined(call, Nil, expansion) = unpickled: @unchecked val inlineCtx = inlineContext(call) val expansion1 = spliceTypes(expansion, typeHole)(using inlineCtx) diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index aafb64b33444..ad36544cdec0 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -82,7 +82,7 @@ object BetaReduce: case _ => None case Block(stats, expr) if stats.forall(isPureBinding) => recur(expr, argss).map(cpy.Block(fn)(stats, _)) - case Inlined(call, bindings, expr) if bindings.forall(isPureBinding) => + case fn @ Inlined(call, bindings, expr) if bindings.forall(isPureBinding) => recur(expr, argss).map(cpy.Inlined(fn)(call, bindings, _)) case Typed(expr, tpt) => recur(expr, argss) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 8b58f18bca52..b368e47bf0b3 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -115,7 +115,7 @@ class PickleQuotes extends MacroTransform { holeContents += content val holeType = getPicklableHoleType(tree.tpe, stagedClasses) val hole = untpd.cpy.Hole(tree)(content = EmptyTree).withType(holeType) - cpy.Inlined(tree)(EmptyTree, Nil, hole) + Inlined(EmptyTree, Nil, hole).withSpan(tree.span) case tree: DefTree => if tree.symbol.isClass then stagedClasses += tree.symbol diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 1d9493e6b1f7..9981a0999ef8 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -250,7 +250,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => private object dropInlines extends TreeMap { override def transform(tree: Tree)(using Context): Tree = tree match { - case Inlined(call, _, expansion) => + case tree @ Inlined(call, _, expansion) => val newExpansion = PruneErasedDefs.trivialErasedTree(tree) cpy.Inlined(tree)(call, Nil, newExpansion) case _ => super.transform(tree) @@ -363,7 +363,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => case _ => super.transform(tree1) } - case Inlined(call, bindings, expansion) if !call.isEmpty => + case tree @ Inlined(call, bindings, expansion) if !call.isEmpty => val pos = call.sourcePos CrossVersionChecks.checkExperimentalRef(call.symbol, pos) withMode(Mode.InlinedCall)(transform(call)) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 7df98d57b5ff..f5a818e21070 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -1005,7 +1005,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def apply(call: Option[Tree], bindings: List[Definition], expansion: Term): Inlined = withDefaultPos(tpd.Inlined(call.getOrElse(tpd.EmptyTree), bindings.map { case b: tpd.MemberDef => b }, xCheckMacroValidExpr(expansion))) def copy(original: Tree)(call: Option[Tree], bindings: List[Definition], expansion: Term): Inlined = - tpd.cpy.Inlined(original)(call.getOrElse(tpd.EmptyTree), bindings.asInstanceOf[List[tpd.MemberDef]], xCheckMacroValidExpr(expansion)) + tpd.cpy.Inlined(original.asInstanceOf[Inlined])(call.getOrElse(tpd.EmptyTree), bindings.asInstanceOf[List[tpd.MemberDef]], xCheckMacroValidExpr(expansion)) def unapply(x: Inlined): (Option[Tree /* Term | TypeTree */], List[Definition], Term) = (optional(x.call), x.bindings, x.body) end Inlined From 66ddf486aadc7d4eff79480727b1949be3e33805 Mon Sep 17 00:00:00 2001 From: Tetchki Date: Tue, 16 May 2023 18:19:14 +0200 Subject: [PATCH 2/2] Keep inlined nodes until backend phase Refactoring of inlined nodes to simplify the work on #17055. This work is based on #18229. --- .../tools/backend/jvm/BCodeBodyBuilder.scala | 5 +++ .../dotty/tools/backend/sjs/JSCodeGen.scala | 4 ++ .../tools/dotc/transform/ArrayApply.scala | 43 +++++++++++++------ .../dotty/tools/dotc/transform/Erasure.scala | 5 --- .../dotty/tools/dotc/transform/Mixin.scala | 2 + 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index e7b5a0dad1bf..db5ec3648977 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -25,6 +25,7 @@ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Phases._ import dotty.tools.dotc.core.Decorators.em import dotty.tools.dotc.report +import dotty.tools.dotc.inlines.Inlines /* * @@ -479,6 +480,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case t: TypeApply => // dotty specific generatedType = genTypeApply(t) + case inlined @ Inlined(_, _, _) => + genLoadTo(Inlines.dropInlined(inlined) , expectedType, dest) + generatedDest = dest + case _ => abort(s"Unexpected tree in genLoad: $tree/${tree.getClass} at: ${tree.span}") } diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index eee791852fde..5fc03d828acb 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -36,6 +36,7 @@ import dotty.tools.dotc.transform.sjs.JSSymUtils._ import JSEncoding._ import ScopedVar.withScopedVars +import dotty.tools.dotc.inlines.Inlines /** Main codegen for Scala.js IR. * @@ -1930,6 +1931,9 @@ class JSCodeGen()(using genCtx: Context) { case EmptyTree => js.Skip() + case inlined @ Inlined(_, _, _) => + genStatOrExpr(Inlines.dropInlined(inlined), isStat) + case _ => throw new FatalError("Unexpected tree in genExpr: " + tree + "/" + tree.getClass + " at: " + (tree.span: Position)) diff --git a/compiler/src/dotty/tools/dotc/transform/ArrayApply.scala b/compiler/src/dotty/tools/dotc/transform/ArrayApply.scala index 872c7cc897de..ee63740afdc2 100644 --- a/compiler/src/dotty/tools/dotc/transform/ArrayApply.scala +++ b/compiler/src/dotty/tools/dotc/transform/ArrayApply.scala @@ -15,7 +15,7 @@ import dotty.tools.dotc.ast.tpd * * Transforms `scala.Array.apply([....])` and `scala.Array.apply(..., [....])` into `[...]` */ -class ArrayApply extends MiniPhase { +class ArrayApply extends MiniPhase: import tpd._ override def phaseName: String = ArrayApply.name @@ -25,14 +25,18 @@ class ArrayApply extends MiniPhase { override def transformApply(tree: tpd.Apply)(using Context): tpd.Tree = if isArrayModuleApply(tree.symbol) then tree.args match { - case StripAscription(Apply(wrapRefArrayMeth, (seqLit: tpd.JavaSeqLiteral) :: Nil)) :: ct :: Nil - if defn.WrapArrayMethods().contains(wrapRefArrayMeth.symbol) && elideClassTag(ct) => + case AppliedLiterals(seqLit) :: ct :: Nil if elideClassTag(ct) => seqLit - case elem0 :: StripAscription(Apply(wrapRefArrayMeth, (seqLit: tpd.JavaSeqLiteral) :: Nil)) :: Nil - if defn.WrapArrayMethods().contains(wrapRefArrayMeth.symbol) => + case InlinedSplice(inlined, seqLit) :: ct :: Nil if elideClassTag(ct) => + tpd.cpy.Inlined(inlined)(inlined.call, inlined.bindings, seqLit) + + case elem0 :: AppliedLiterals(seqLit) :: Nil => tpd.JavaSeqLiteral(elem0 :: seqLit.elems, seqLit.elemtpt) + case elem0 :: InlinedSplice(inlined, seqLit) :: Nil => + tpd.cpy.Inlined(inlined)(inlined.call, inlined.bindings, tpd.JavaSeqLiteral(elem0 :: seqLit.elems, seqLit.elemtpt)) + case _ => tree } @@ -49,6 +53,7 @@ class ArrayApply extends MiniPhase { * - `ClassTag.XYZ` for primitive types */ private def elideClassTag(ct: Tree)(using Context): Boolean = ct match { + case Inlined(_, _, expansion) => elideClassTag(expansion) case Apply(_, rc :: Nil) if ct.symbol == defn.ClassTagModule_apply => rc match { case _: Literal => true // ClassTag.apply(classOf[XYZ]) @@ -63,13 +68,27 @@ class ArrayApply extends MiniPhase { case _ => false } - object StripAscription { - def unapply(tree: Tree)(using Context): Some[Tree] = tree match { - case Typed(expr, _) => unapply(expr) - case _ => Some(tree) - } - } -} + // Match a sequence of literal arguments passed to an Array constructor + private object AppliedLiterals: + + def unapply(tree: Tree)(using Context): Option[tpd.JavaSeqLiteral] = tree match + case Apply(wrapRefArrayMeth, (seqLit: tpd.JavaSeqLiteral) :: Nil) + if defn.WrapArrayMethods().contains(wrapRefArrayMeth.symbol) => + Some(seqLit) + case _ => None + + end AppliedLiterals + + // Match an inlined sequence splice + private object InlinedSplice: + def unapply(tree: Tree)(using Context): Option[(Inlined, tpd.JavaSeqLiteral)] = tree match + case inlined @ Inlined(_, _, Typed(AppliedLiterals(seqLit), _)) => + Some((inlined, seqLit)) + case _ => None + + end InlinedSplice + +end ArrayApply object ArrayApply: val name: String = "arrayApply" diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 3ed024429bb6..49a60a24def4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -895,11 +895,6 @@ object Erasure { tree.typeOpt else valueErasure(tree.typeOpt) - override def typedInlined(tree: untpd.Inlined, pt: Type)(using Context): Tree = - super.typedInlined(tree, pt) match { - case tree: Inlined => Inlines.dropInlined(tree) - } - override def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree = if (sym.isEffectivelyErased) erasedDef(sym) else diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 5ca09dd6188f..8361946adb63 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -17,6 +17,7 @@ import Names._ import NameKinds._ import NameOps._ import ast.Trees._ +import dotty.tools.dotc.inlines.Inlines object Mixin { val name: String = "mixin" @@ -221,6 +222,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => case _ => } (scall, stats ::: inits, args) + case inlined @ Inlined(_, _, _) => transformConstructor(Inlines.dropInlined(inlined) ) case _ => val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree: @unchecked val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil)