Skip to content

Commit 25d54da

Browse files
committed
Add underlying term to know the static value of some ident
1 parent 3bba9e6 commit 25d54da

File tree

11 files changed

+204
-7
lines changed

11 files changed

+204
-7
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
913913
def outerSelect(levels: Int, tp: Type)(implicit ctx: Context): Tree =
914914
untpd.Select(tree, OuterSelectName(EmptyTermName, levels)).withType(SkolemType(tp))
915915

916-
def underlyingArgument(implicit ctx: Context): Tree = mapToUnderlying.transform(tree)
916+
/** Replace Inlined nodes and InlineProxy references to underlying arguments */
917+
def underlyingArgument(implicit ctx: Context): Tree = mapToUnderlyingArgument.transform(tree)
918+
919+
/** Replace Ident nodes references to the underlying tree that defined them */
920+
def underlying(implicit ctx: Context): Tree = mapToUnderlying.transform(tree)
917921

918922
// --- Higher order traversal methods -------------------------------
919923

@@ -942,7 +946,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
942946
}
943947

944948
/** Map Inlined nodes, InlineProxy references and Synthetic val references to underlying arguments */
945-
object mapToUnderlying extends TreeMap {
949+
object mapToUnderlyingArgument extends TreeMap {
946950
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
947951
case tree: Ident if tree.symbol.is(InlineProxy) || (tree.symbol.is(Synthetic) && !tree.symbol.owner.isClass) =>
948952
tree.symbol.defTree match {
@@ -955,6 +959,23 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
955959
}
956960
}
957961

962+
/** Map Ident nodes references to underlying tree that defined them.
963+
* Also drops Inline and Block with no statements
964+
*/
965+
object mapToUnderlying extends TreeMap {
966+
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
967+
case tree: Ident if !tree.symbol.owner.isClass =>
968+
tree.symbol.defTree match {
969+
case defTree: ValOrDefDef => transform(defTree.rhs)
970+
case _ => tree
971+
}
972+
case Inlined(_, _, arg) => transform(arg)
973+
case Block(Nil, arg) => transform(arg)
974+
case NamedArg(_, arg) => transform(arg)
975+
case tree => super.transform(tree)
976+
}
977+
}
978+
958979
implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal {
959980
def tpes: List[Type] = xs map (_.tpe)
960981
}

compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with TastyCoreImpl with He
186186
// ----- Terms ----------------------------------------------------
187187

188188
def TermDeco(term: Term): TermAPI = new TermAPI {
189+
import tpd._
189190
def pos(implicit ctx: Context): Position = term.pos
190191
def tpe(implicit ctx: Context): Type = term.tpe
191-
def underlyingArgument(implicit ctx: Context): Term = {
192-
import tpd._
193-
term.underlyingArgument
194-
}
192+
def underlyingArgument(implicit ctx: Context): Term = term.underlyingArgument
193+
def underlying(implicit ctx: Context): Term = term.underlying
195194
}
196195

197196
object IsTerm extends IsTermExtractor {

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ t7374
5959
t7859
6060
t8133
6161
t8133b
62+
tasty-argument-tree-1
6263
tasty-custom-show
6364
tasty-definitions-1
6465
tasty-definitions-2
@@ -91,4 +92,5 @@ typelevel-patmat.scala
9192
typelevel.scala
9293
typelevel1.scala
9394
typelevel3.scala
94-
xml-interpolation
95+
xml-interpolation-1
96+
xml-interpolation-2

library/src/scala/tasty/reflect/TreeOps.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ trait TreeOps extends TastyCore {
156156
def tpe(implicit ctx: Context): Type
157157
def pos(implicit ctx: Context): Position
158158
def underlyingArgument(implicit ctx: Context): Term
159+
def underlying(implicit ctx: Context): Term
159160
}
160161
implicit def TermDeco(term: Term): TermAPI
161162

tests/run/tasty-argument-tree-1.check

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
tree: Term.Literal(Constant.Int(3))
3+
tree deref. vals: Term.Literal(Constant.Int(3))
4+
5+
tree: Term.Ident("v")
6+
tree deref. vals: Term.Literal(Constant.Int(1))
7+
8+
tree: Term.Ident("x")
9+
tree deref. vals: Term.Literal(Constant.Int(2))
10+
11+
tree: Term.Ident("l")
12+
tree deref. vals: Term.Literal(Constant.Int(3))
13+
14+
tree: Term.Ident("a")
15+
tree deref. vals: Term.Ident("a")
16+
17+
tree: Term.Ident("x")
18+
tree deref. vals: Term.Ident("b")
19+
20+
tree: Term.Ident("vv")
21+
tree deref. vals: Term.Literal(Constant.Int(1))
22+
23+
tree: Term.Ident("x")
24+
tree deref. vals: Term.Literal(Constant.Int(1))
25+
26+
tree: Term.Ident("vd")
27+
tree deref. vals: Term.Literal(Constant.Int(2))
28+
29+
tree: Term.Ident("x")
30+
tree deref. vals: Term.Literal(Constant.Int(2))
31+
32+
tree: Term.Ident("x")
33+
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))))
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import scala.quoted._
2+
import scala.tasty._
3+
4+
object Macros {
5+
6+
inline def inspect[T](x: T): Unit = ~impl('(x))
7+
8+
def impl[T](x: Expr[T])(implicit tasty: Tasty): Expr[Unit] = {
9+
import tasty._
10+
val tree = x.toTasty
11+
'{
12+
println()
13+
println("tree: " + ~tree.show.toExpr)
14+
println("tree deref. vals: " + ~tree.underlying.show.toExpr)
15+
}
16+
}
17+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
import Macros.inspect
3+
4+
object Test {
5+
val a: Int = 4
6+
def b: Int = 5
7+
8+
def main(args: Array[String]): Unit = {
9+
val v: Int = 1
10+
def d: Int = 2
11+
lazy val l: Int = 3
12+
inspect(3)
13+
inspect(v)
14+
inspect(d)
15+
inspect(l)
16+
inspect(a)
17+
inspect(b)
18+
19+
val vv = v
20+
def dv = v
21+
val vd = d
22+
def dd = d
23+
inspect(vv)
24+
inspect(dv)
25+
inspect(vd)
26+
inspect(dd)
27+
28+
inspect((dv, vd))
29+
30+
}
31+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import XmlQuote._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
6+
assert(xml"Hello Allan!" == Xml("Hello Allan!", Nil))
7+
8+
val name = new Object{}
9+
assert(xml"Hello $name!" == Xml("Hello ??!", List(name)))
10+
11+
val ctx0 = new StringContext("Hello !")
12+
assert(ctx0.xml() == Xml("Hello !", Nil))
13+
assert(new SCOps(ctx0).xml() == Xml("Hello !", Nil))
14+
15+
val ctx1 = new StringContext("Hello ", "!")
16+
assert(ctx1.xml(name) == Xml("Hello ??!", List(name)))
17+
assert(new SCOps(ctx1).xml(name) == Xml("Hello ??!", List(name)))
18+
19+
val hello: String = "Hello "
20+
val ctx2 = new StringContext(hello, "!")
21+
assert(ctx2.xml(name) == Xml("Hello ??!", List(name)))
22+
assert(new SCOps(ctx2).xml(name) == Xml("Hello ??!", List(name)))
23+
24+
val args = Seq(name)
25+
assert(new SCOps(ctx2).xml(args: _*) == Xml("Hello ??!", List(name)))
26+
}
27+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import scala.quoted._
2+
3+
import scala.tasty.Tasty
4+
5+
import scala.language.implicitConversions
6+
7+
case class Xml(parts: String, args: List[Any])
8+
9+
object XmlQuote {
10+
11+
class SCOps(ctx: => StringContext) {
12+
inline def xml(args: Any*): Xml = ~XmlQuote.impl('(this), '(args))
13+
}
14+
implicit inline def SCOps(ctx: => StringContext): SCOps = new SCOps(ctx)
15+
16+
def impl(receiver: Expr[SCOps], args: Expr[Seq[Any]])
17+
(implicit tasty: Tasty): Expr[Xml] = {
18+
import tasty._
19+
import Term._
20+
21+
// for debugging purpose
22+
def pp(tree: Tree): Unit = {
23+
println(tree.show)
24+
println(tasty.showSourceCode.showTree(tree))
25+
}
26+
27+
def isSCOpsConversion(tree: Term) =
28+
tree.symbol.fullName == "XmlQuote$.SCOps" ||
29+
tree.symbol.fullName == "XmlQuote$.SCOps.<init>"
30+
31+
def isStringContextApply(tree: Term) =
32+
tree.symbol.fullName == "scala.StringContext$.apply" ||
33+
tree.symbol.fullName == "scala.StringContext.<init>"
34+
35+
// XmlQuote.SCOps(StringContext.apply([p0, ...]: String*)
36+
val parts: List[String] = receiver.toTasty.underlying match {
37+
case Apply(conv, List(ctx1)) if isSCOpsConversion(conv) =>
38+
ctx1 match {
39+
case Apply(fun, List(Typed(Repeated(values), _))) if isStringContextApply(fun) =>
40+
values.iterator.map {
41+
case Literal(Constant.String(value)) => value
42+
case _ => QuoteError("Expected statically known String")
43+
}.toList
44+
case _ => QuoteError("Expected statically known StringContext")
45+
}
46+
case _ =>
47+
QuoteError("Expected statically known SCOps")
48+
}
49+
50+
// [a0, ...]: Any*
51+
val args2: Expr[List[Any]] = args.toTasty.underlyingArgument match {
52+
case Typed(Repeated(args0), _) => // statically known args, make list directly
53+
def liftListOfAny(lst: List[Expr[Any]]): Expr[List[Any]] = lst match {
54+
case x :: xs => '{ ~x :: ~liftListOfAny(xs) }
55+
case Nil => '(Nil)
56+
}
57+
liftListOfAny(args0.map(_.toExpr[Any]))
58+
case _ =>
59+
'((~args).toList)
60+
61+
}
62+
63+
val string = parts.mkString("??")
64+
'(new Xml(~string.toExpr, ~args2))
65+
}
66+
}

0 commit comments

Comments
 (0)