diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index e8c542362fa4..fcde37fabe8f 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -919,7 +919,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def outerSelect(levels: Int, tp: Type)(implicit ctx: Context): Tree = untpd.Select(tree, OuterSelectName(EmptyTermName, levels)).withType(SkolemType(tp)) - def underlyingArgument(implicit ctx: Context): Tree = mapToUnderlying.transform(tree) + /** Replace Inlined nodes and InlineProxy references to underlying arguments */ + def underlyingArgument(implicit ctx: Context): Tree = { + val mapToUnderlying = new MapToUnderlying { + override def skipLocal(sym: Symbol): Boolean = + sym.is(InlineProxy) || sym.is(Synthetic) + } + mapToUnderlying.transform(tree) + } + + /** Replace Ident nodes references to the underlying tree that defined them */ + def underlying(implicit ctx: Context): Tree = new MapToUnderlying().transform(tree) // --- Higher order traversal methods ------------------------------- @@ -947,18 +957,22 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } - /** Map Inlined nodes, InlineProxy references and Synthetic val references to underlying arguments */ - object mapToUnderlying extends TreeMap { + /** Map Inlined nodes, NamedArgs, Blocks with no statements and local references to underlying arguments. + * Also drops Inline and Block with no statements. + */ + class MapToUnderlying extends TreeMap { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case tree: Ident if tree.symbol.is(InlineProxy) || (tree.symbol.is(Synthetic) && !tree.symbol.owner.isClass) => + case tree: Ident if !tree.symbol.owner.isClass && skipLocal(tree.symbol) => tree.symbol.defTree match { case defTree: ValOrDefDef => transform(defTree.rhs) case _ => tree } case Inlined(_, _, arg) => transform(arg) + case Block(Nil, arg) => transform(arg) case NamedArg(_, arg) => transform(arg) case tree => super.transform(tree) } + def skipLocal(sym: Symbol): Boolean = true } implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal { diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala index bc94a4ac21d5..6c92380b84f9 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala @@ -186,12 +186,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with TastyCoreImpl with He // ----- Terms ---------------------------------------------------- def TermDeco(term: Term): TermAPI = new TermAPI { + import tpd._ def pos(implicit ctx: Context): Position = term.pos def tpe(implicit ctx: Context): Type = term.tpe - def underlyingArgument(implicit ctx: Context): Term = { - import tpd._ - term.underlyingArgument - } + def underlyingArgument(implicit ctx: Context): Term = term.underlyingArgument + def underlying(implicit ctx: Context): Term = term.underlying } object IsTerm extends IsTermExtractor { diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index a325fa8a995d..61f0bf32becf 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -59,6 +59,7 @@ t7374 t7859 t8133 t8133b +tasty-argument-tree-1 tasty-custom-show tasty-definitions-1 tasty-definitions-2 @@ -91,4 +92,5 @@ typelevel-patmat.scala typelevel.scala typelevel1.scala typelevel3.scala -xml-interpolation +xml-interpolation-1 +xml-interpolation-2 diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index 1cd035e34bc4..dcea72e3eb75 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -156,6 +156,7 @@ trait TreeOps extends TastyCore { def tpe(implicit ctx: Context): Type def pos(implicit ctx: Context): Position def underlyingArgument(implicit ctx: Context): Term + def underlying(implicit ctx: Context): Term } implicit def TermDeco(term: Term): TermAPI diff --git a/tests/run/tasty-argument-tree-1.check b/tests/run/tasty-argument-tree-1.check new file mode 100644 index 000000000000..f848f6960473 --- /dev/null +++ b/tests/run/tasty-argument-tree-1.check @@ -0,0 +1,33 @@ + +tree: Term.Literal(Constant.Int(3)) +tree deref. vals: Term.Literal(Constant.Int(3)) + +tree: Term.Ident("v") +tree deref. vals: Term.Literal(Constant.Int(1)) + +tree: Term.Ident("x") +tree deref. vals: Term.Literal(Constant.Int(2)) + +tree: Term.Ident("l") +tree deref. vals: Term.Literal(Constant.Int(3)) + +tree: Term.Ident("a") +tree deref. vals: Term.Ident("a") + +tree: Term.Ident("x") +tree deref. vals: Term.Ident("b") + +tree: Term.Ident("vv") +tree deref. vals: Term.Literal(Constant.Int(1)) + +tree: Term.Ident("x") +tree deref. vals: Term.Literal(Constant.Int(1)) + +tree: Term.Ident("vd") +tree deref. vals: Term.Literal(Constant.Int(2)) + +tree: Term.Ident("x") +tree deref. vals: Term.Literal(Constant.Int(2)) + +tree: Term.Ident("x") +tree deref. vals: Term.Apply(Term.TypeApply(Term.Select(Term.Ident("Tuple2"), "apply", Some(Signature(List(java.lang.Object, java.lang.Object), scala.Tuple2))), List(TypeTree.Synthetic(), TypeTree.Synthetic())), List(Term.Literal(Constant.Int(1)), Term.Literal(Constant.Int(2)))) diff --git a/tests/run/tasty-argument-tree-1/quoted_1.scala b/tests/run/tasty-argument-tree-1/quoted_1.scala new file mode 100644 index 000000000000..4bd8ece04268 --- /dev/null +++ b/tests/run/tasty-argument-tree-1/quoted_1.scala @@ -0,0 +1,17 @@ +import scala.quoted._ +import scala.tasty._ + +object Macros { + + inline def inspect[T](x: T): Unit = ~impl('(x)) + + def impl[T](x: Expr[T])(implicit tasty: Tasty): Expr[Unit] = { + import tasty._ + val tree = x.toTasty + '{ + println() + println("tree: " + ~tree.show.toExpr) + println("tree deref. vals: " + ~tree.underlying.show.toExpr) + } + } +} diff --git a/tests/run/tasty-argument-tree-1/quoted_2.scala b/tests/run/tasty-argument-tree-1/quoted_2.scala new file mode 100644 index 000000000000..7fb439612218 --- /dev/null +++ b/tests/run/tasty-argument-tree-1/quoted_2.scala @@ -0,0 +1,31 @@ + +import Macros.inspect + +object Test { + val a: Int = 4 + def b: Int = 5 + + def main(args: Array[String]): Unit = { + val v: Int = 1 + def d: Int = 2 + lazy val l: Int = 3 + inspect(3) + inspect(v) + inspect(d) + inspect(l) + inspect(a) + inspect(b) + + val vv = v + def dv = v + val vd = d + def dd = d + inspect(vv) + inspect(dv) + inspect(vd) + inspect(dd) + + inspect((dv, vd)) + + } +} diff --git a/tests/run/xml-interpolation/Test_2.scala b/tests/run/xml-interpolation-1/Test_2.scala similarity index 100% rename from tests/run/xml-interpolation/Test_2.scala rename to tests/run/xml-interpolation-1/Test_2.scala diff --git a/tests/run/xml-interpolation/XmlQuote_1.scala b/tests/run/xml-interpolation-1/XmlQuote_1.scala similarity index 100% rename from tests/run/xml-interpolation/XmlQuote_1.scala rename to tests/run/xml-interpolation-1/XmlQuote_1.scala diff --git a/tests/run/xml-interpolation-2/Test_2.scala b/tests/run/xml-interpolation-2/Test_2.scala new file mode 100644 index 000000000000..2223052ee24a --- /dev/null +++ b/tests/run/xml-interpolation-2/Test_2.scala @@ -0,0 +1,27 @@ +import XmlQuote._ + +object Test { + def main(args: Array[String]): Unit = { + + assert(xml"Hello Allan!" == Xml("Hello Allan!", Nil)) + + val name = new Object{} + assert(xml"Hello $name!" == Xml("Hello ??!", List(name))) + + val ctx0 = new StringContext("Hello !") + assert(ctx0.xml() == Xml("Hello !", Nil)) + assert(new SCOps(ctx0).xml() == Xml("Hello !", Nil)) + + val ctx1 = new StringContext("Hello ", "!") + assert(ctx1.xml(name) == Xml("Hello ??!", List(name))) + assert(new SCOps(ctx1).xml(name) == Xml("Hello ??!", List(name))) + + val hello: String = "Hello " + val ctx2 = new StringContext(hello, "!") + assert(ctx2.xml(name) == Xml("Hello ??!", List(name))) + assert(new SCOps(ctx2).xml(name) == Xml("Hello ??!", List(name))) + + val args = Seq(name) + assert(new SCOps(ctx2).xml(args: _*) == Xml("Hello ??!", List(name))) + } +} diff --git a/tests/run/xml-interpolation-2/XmlQuote_1.scala b/tests/run/xml-interpolation-2/XmlQuote_1.scala new file mode 100644 index 000000000000..f8e726acd1ff --- /dev/null +++ b/tests/run/xml-interpolation-2/XmlQuote_1.scala @@ -0,0 +1,66 @@ +import scala.quoted._ + +import scala.tasty.Tasty + +import scala.language.implicitConversions + +case class Xml(parts: String, args: List[Any]) + +object XmlQuote { + + class SCOps(ctx: => StringContext) { + inline def xml(args: Any*): Xml = ~XmlQuote.impl('(this), '(args)) + } + implicit inline def SCOps(ctx: => StringContext): SCOps = new SCOps(ctx) + + def impl(receiver: Expr[SCOps], args: Expr[Seq[Any]]) + (implicit tasty: Tasty): Expr[Xml] = { + import tasty._ + import Term._ + + // for debugging purpose + def pp(tree: Tree): Unit = { + println(tree.show) + println(tasty.showSourceCode.showTree(tree)) + } + + def isSCOpsConversion(tree: Term) = + tree.symbol.fullName == "XmlQuote$.SCOps" || + tree.symbol.fullName == "XmlQuote$.SCOps." + + def isStringContextApply(tree: Term) = + tree.symbol.fullName == "scala.StringContext$.apply" || + tree.symbol.fullName == "scala.StringContext." + + // XmlQuote.SCOps(StringContext.apply([p0, ...]: String*) + val parts: List[String] = receiver.toTasty.underlying match { + case Apply(conv, List(ctx1)) if isSCOpsConversion(conv) => + ctx1 match { + case Apply(fun, List(Typed(Repeated(values), _))) if isStringContextApply(fun) => + values.iterator.map { + case Literal(Constant.String(value)) => value + case _ => QuoteError("Expected statically known String") + }.toList + case _ => QuoteError("Expected statically known StringContext") + } + case _ => + QuoteError("Expected statically known SCOps") + } + + // [a0, ...]: Any* + val args2: Expr[List[Any]] = args.toTasty.underlyingArgument match { + case Typed(Repeated(args0), _) => // statically known args, make list directly + def liftListOfAny(lst: List[Expr[Any]]): Expr[List[Any]] = lst match { + case x :: xs => '{ ~x :: ~liftListOfAny(xs) } + case Nil => '(Nil) + } + liftListOfAny(args0.map(_.toExpr[Any])) + case _ => + '((~args).toList) + + } + + val string = parts.mkString("??") + '(new Xml(~string.toExpr, ~args2)) + } +}