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/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 08151243ca20..1deb402586e4 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1344,10 +1344,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))) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index eac7c98f2199..4ff5c4c8c41d 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 6c9e387b5c8c..0e2ff4d3ebc3 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/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/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/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index ca441fe9e799..a5654c9e22ed 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) 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 4b92582a612c..83efabf541de 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) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 6c87c7302ab8..19aaaaa6a71a 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -1009,7 +1009,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