Skip to content

Commit 15d1f84

Browse files
committed
Add underlying term to know the static value of some ident
1 parent 0289c31 commit 15d1f84

File tree

11 files changed

+208
-9
lines changed

11 files changed

+208
-9
lines changed

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

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

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

919923
// --- Higher order traversal methods -------------------------------
920924

@@ -943,12 +947,31 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
943947
}
944948

945949
/** Map Inlined nodes and InlineProxy references to underlying arguments */
946-
object mapToUnderlying extends TreeMap {
950+
object mapToUnderlyingArgument extends TreeMap {
947951
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
948952
case tree: Ident if tree.symbol.is(InlineProxy) =>
949-
tree.symbol.defTree.asInstanceOf[ValOrDefDef].rhs.underlyingArgument
953+
transform(tree.symbol.defTree.asInstanceOf[ValOrDefDef].rhs)
954+
case Inlined(_, _, arg) =>
955+
transform(arg)
956+
case tree =>
957+
super.transform(tree)
958+
}
959+
}
960+
961+
/** Map Ident nodes references to underlying tree that defined them.
962+
* Also drops Inline and Block with no statements
963+
*/
964+
object mapToUnderlying extends TreeMap {
965+
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
966+
case tree: Ident if !tree.symbol.owner.isClass =>
967+
tree.symbol.defTree match {
968+
case defTree: ValOrDefDef => transform(defTree.rhs)
969+
case _ => tree
970+
}
950971
case Inlined(_, _, arg) =>
951-
arg.underlyingArgument
972+
transform(arg)
973+
case Block(Nil, arg) =>
974+
transform(arg)
952975
case tree =>
953976
super.transform(tree)
954977
}

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

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

184184
def TermDeco(term: Term): TermAPI = new TermAPI {
185+
import tpd._
185186
def pos(implicit ctx: Context): Position = term.pos
186187
def tpe(implicit ctx: Context): Types.Type = term.tpe
187-
def underlyingArgument(implicit ctx: Context): Term = {
188-
import tpd._
189-
term.underlyingArgument
190-
}
188+
def underlyingArgument(implicit ctx: Context): Term = term.underlyingArgument
189+
def underlying(implicit ctx: Context): Term = term.underlying
191190
}
192191

193192
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
@@ -50,6 +50,7 @@ t7374
5050
t7859
5151
t8133
5252
t8133b
53+
tasty-argument-tree-1
5354
tasty-custom-show
5455
tasty-definitions-1
5556
tasty-definitions-2
@@ -82,4 +83,5 @@ typelevel-patmat.scala
8283
typelevel.scala
8384
typelevel1.scala
8485
typelevel3.scala
85-
xml-interpolation
86+
xml-interpolation-1
87+
xml-interpolation-2

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ trait TreeOps extends TastyCore {
148148
def tpe(implicit ctx: Context): Type
149149
def pos(implicit ctx: Context): Position
150150
def underlyingArgument(implicit ctx: Context): Term
151+
def underlying(implicit ctx: Context): Term
151152
}
152153
implicit def TermDeco(term: Term): TermAPI
153154

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)