Skip to content

Commit 0fde9fa

Browse files
committed
TASTy reflect load complete definition trees
Currently only works with -Yretain-tree. Also fix an issue with TASTy reflect
1 parent e77a47f commit 0fde9fa

25 files changed

+184
-50
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class Compiler {
5252
/** Phases dealing with TASTY tree pickling and unpickling */
5353
protected def picklerPhases: List[List[Phase]] =
5454
List(new Pickler) :: // Generate TASTY info
55+
List(new RetainTrees) :: // Retain trees of the compilation units if -Yretain-trees is set
5556
List(new ReifyQuotes) :: // Turn quoted trees into explicit run-time data structures
5657
Nil
5758

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

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Types._
1010
object FromSymbol {
1111

1212
def definitionFromSym(sym: Symbol)(implicit ctx: Context): tpd.Tree = {
13+
assert(sym.exists)
1314
if (sym.is(Package)) packageDefFromSym(sym)
1415
else if (sym.isClass) classDef(sym.asClass)
1516
else if (sym.isType) typeDefFromSym(sym.asType)
@@ -19,21 +20,52 @@ object FromSymbol {
1920

2021
def packageDefFromSym(sym: Symbol)(implicit ctx: Context): PackageDefinition = PackageDefinitionImpl(sym)
2122

22-
def classDef(cls: ClassSymbol)(implicit ctx: Context): tpd.TypeDef = {
23-
val constrSym = cls.unforcedDecls.find(_.isPrimaryConstructor).orElse(
24-
// Dummy constructor for classes such as `<refinement>`
25-
ctx.newSymbol(cls, nme.CONSTRUCTOR, EmptyFlags, NoType)
26-
)
27-
val constr = tpd.DefDef(constrSym.asTerm)
28-
val parents = cls.classParents.map(tpd.TypeTree(_))
29-
val body = cls.unforcedDecls.filter(!_.isPrimaryConstructor).map(s => definitionFromSym(s))
30-
tpd.ClassDefWithParents(cls, constr, parents, body)
23+
def classDef(cls: ClassSymbol)(implicit ctx: Context): tpd.TypeDef = cls.tree match {
24+
case tree: tpd.TypeDef => tree
25+
case tpd.EmptyTree =>
26+
val constrSym = cls.unforcedDecls.find(_.isPrimaryConstructor).orElse(
27+
// Dummy constructor for classes such as `<refinement>`
28+
ctx.newSymbol(cls, nme.CONSTRUCTOR, EmptyFlags, NoType)
29+
)
30+
val constr = tpd.DefDef(constrSym.asTerm)
31+
val parents = cls.classParents.map(tpd.TypeTree(_))
32+
val body = cls.unforcedDecls.filter(!_.isPrimaryConstructor).map(s => definitionFromSym(s))
33+
tpd.ClassDefWithParents(cls, constr, parents, body)
3134
}
3235

33-
def typeDefFromSym(sym: TypeSymbol)(implicit ctx: Context): tpd.TypeDef = tpd.TypeDef(sym)
36+
// TODO: this logic could be moved inside sym.tree
3437

35-
def defDefFromSym(sym: TermSymbol)(implicit ctx: Context): tpd.DefDef = tpd.DefDef(sym)
38+
def typeDefFromSym(sym: TypeSymbol)(implicit ctx: Context): tpd.TypeDef = tree(sym) match {
39+
case tree: tpd.TypeDef => tree
40+
case tpd.EmptyTree => tpd.TypeDef(sym)
41+
}
42+
43+
def defDefFromSym(sym: TermSymbol)(implicit ctx: Context): tpd.DefDef = tree(sym) match {
44+
case tree: tpd.DefDef => tree
45+
case tpd.EmptyTree => tpd.DefDef(sym)
46+
}
47+
48+
def valDefFromSym(sym: TermSymbol)(implicit ctx: Context): tpd.ValDef = tree(sym) match {
49+
case tree: tpd.ValDef => tree
50+
case tpd.EmptyTree => tpd.ValDef(sym)
51+
}
52+
53+
private def tree(sym: Symbol)(implicit ctx: Context): tpd.Tree = sym.topLevelClass match {
54+
case top: ClassSymbol =>
55+
val findTree = new tpd.TreeAccumulator[tpd.Tree] {
56+
def apply(x: tpd.Tree, tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = {
57+
if (!x.isEmpty) x
58+
else tree match {
59+
case tree: tpd.DefTree if tree.symbol == sym => tree
60+
// TODO avoid searching in all the tree, look at sym.ownerIterator.toSet
61+
case _ => foldOver(x, tree)
62+
}
3663

37-
def valDefFromSym(sym: TermSymbol)(implicit ctx: Context): tpd.ValDef = tpd.ValDef(sym)
64+
}
65+
}
66+
findTree(tpd.EmptyTree, top.tree)
67+
68+
case _ => tpd.EmptyTree
69+
}
3870

3971
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with TastyCoreImpl with He
313313
}
314314

315315
object Inlined extends InlinedExtractor {
316-
def unapply(x: Term)(implicit ctx: Context): Option[(Option[Term], List[Statement], Term)] = x match {
316+
def unapply(x: Term)(implicit ctx: Context): Option[(Option[Parent], List[Statement], Term)] = x match {
317317
case x: tpd.Inlined =>
318318
Some((optional(x.call), x.bindings, x.expansion))
319319
case _ => None
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import dotty.tools.dotc.ast.tpd
5+
import dotty.tools.dotc.ast.Trees._
6+
import dotty.tools.dotc.core.Contexts._
7+
import dotty.tools.dotc.core.Phases._
8+
9+
/** Retain trees of the compilation units if -Yretain-trees is set */
10+
class RetainTrees extends Phase {
11+
import tpd._
12+
13+
def phaseName: String = "RetainTrees"
14+
15+
def run(implicit ctx: Context): Unit = {
16+
if (ctx.settings.YretainTrees.value)
17+
retainTopLevelClassTrees(ctx.compilationUnit.tpdTree)
18+
}
19+
20+
private def retainTopLevelClassTrees(tree: Tree)(implicit ctx: Context): Unit = tree match {
21+
case PackageDef(_, stats) => stats.foreach(retainTopLevelClassTrees)
22+
case tree: TypeDef => tree.symbol.asClass.treeOrProvider = tree
23+
case _ =>
24+
}
25+
26+
}

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ class CompilationTests extends ParallelTesting {
168168

169169
@Test def runAll: Unit = {
170170
implicit val testGroup: TestGroup = TestGroup("runAll")
171+
compileFilesInDir("tests/run-custom-args/Yretain-trees", defaultOptions and "-Yretain-trees") +
171172
compileFilesInDir("tests/run", defaultOptions)
172173
}.checkRuns()
173174

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ trait TreeOps extends TastyCore {
285285

286286
val Inlined: InlinedExtractor
287287
abstract class InlinedExtractor {
288-
def unapply(tree: Tree)(implicit ctx: Context): Option[(Option[Term], List[Definition], Term)]
288+
def unapply(tree: Tree)(implicit ctx: Context): Option[(Option[Parent], List[Definition], Term)]
289289
}
290290

291291
val SelectOuter: SelectOuterExtractor

library/src/scala/tasty/util/ShowExtractors.scala

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ class ShowExtractors[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
6767
case Term.Repeated(elems) =>
6868
this += "Term.Repeated(" ++= elems += ")"
6969
case Term.Inlined(call, bindings, expansion) =>
70-
this += "Term.Inlined(" += call += ", " ++= bindings += ", " += expansion += ")"
70+
this += "Term.Inlined("
71+
visitOption(call, visitParent)
72+
this += ", " ++= bindings += ", " += expansion += ")"
7173
case ValDef(name, tpt, rhs) =>
7274
this += "ValDef(\"" += name += "\", " += tpt += ", " += rhs += ")"
7375
case DefDef(name, typeParams, paramss, returnTpt, rhs) =>
@@ -76,10 +78,7 @@ class ShowExtractors[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
7678
this += "TypeDef(\"" += name += "\", " += rhs += ")"
7779
case ClassDef(name, constr, parents, self, body) =>
7880
this += "ClassDef(\"" += name += "\", " += constr += ", "
79-
visitList[Parent](parents, {
80-
case IsTerm(parent) => this += parent
81-
case IsTypeTree(parent) => this += parent
82-
})
81+
visitList[Parent](parents, visitParent)
8382
this += ", " += self += ", " ++= body += ")"
8483
case PackageDef(name, owner) =>
8584
this += "PackageDef(\"" += name += "\", " += owner += ")"
@@ -140,6 +139,11 @@ class ShowExtractors[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
140139
this += "Pattern.TypeTest(" += tpt += ")"
141140
}
142141

142+
def visitParent(x: Parent): Buffer = x match {
143+
case IsTerm(parent) => this += parent
144+
case IsTypeTree(parent) => this += parent
145+
}
146+
143147
def visitConstant(x: Constant): Buffer = x match {
144148
case Constant.Unit() => this += "Constant.Unit()"
145149
case Constant.Null() => this += "Constant.Null()"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DefDef("foo", Nil, Nil, TypeTree.TypeIdent("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(1)), "+", Some(Signature(List(scala.Int), scala.Int))), List(Term.Literal(Constant.Int(2))))))
2+
ValDef("bar", TypeTree.TypeIdent("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(2)), "+", Some(Signature(List(scala.Int), scala.Int))), List(Term.Literal(Constant.Int(3))))))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DefDef("foo", Nil, Nil, TypeTree.TypeIdent("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(1)), "+", Some(Signature(List(scala.Int), scala.Int))), List(Term.Literal(Constant.Int(2))))))
2+
ValDef("bar", TypeTree.TypeIdent("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(2)), "+", Some(Signature(List(scala.Int), scala.Int))), List(Term.Literal(Constant.Int(3))))))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
foo
2+
DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.TypeIdent("Array"), List(TypeTree.TypeIdent("String"))), None))), TypeTree.TypeIdent("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.TypeIdent("Macros$")), Nil, Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Synthetic())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Synthetic())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic())))), ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic())))), ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.TypeIdent("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "unary_~", Some(Signature(Nil, java.lang.Object)))))))
3+
4+
bar
5+
DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic()))))
6+
7+
bar2
8+
DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic()))))
9+
10+
foo2
11+
DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.TypeIdent("Array"), List(TypeTree.TypeIdent("String"))), None))), TypeTree.TypeIdent("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.TypeIdent("Macros$")), Nil, Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Synthetic())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Synthetic())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Synthetic(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Synthetic())))), ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic())))), ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.TypeIdent("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "unary_~", Some(Signature(Nil, java.lang.Object)))))))
12+
13+
baz
14+
ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic()))))
15+
16+
baz2
17+
ValDef("foo2", TypeTree.Synthetic(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Synthetic()))))
18+
19+
<init>
20+
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.TypeIdent("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))
21+
22+
b
23+
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.TypeIdent("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))
24+
25+
b2
26+
ClassDef("A", DefDef("<init>", Nil, List(Nil), TypeTree.Synthetic(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Synthetic()), "<init>", Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef("B", TypeTree.TypeIdent("Int")), DefDef("b", Nil, Nil, TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Synthetic(), Some(Term.Literal(Constant.Int(6))))))
27+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DefDef("foo", Nil, Nil, TypeTree.TypeIdent("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(1)), "+", Some(Signature(List(scala.Int), scala.Int))), List(Term.Literal(Constant.Int(2))))))
2+
ValDef("bar", TypeTree.TypeIdent("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(2)), "+", Some(Signature(List(scala.Int), scala.Int))), List(Term.Literal(Constant.Int(3))))))
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import scala.quoted._
2+
3+
import scala.tasty._
4+
5+
object Foo {
6+
7+
rewrite def inspectBody(i: => Int): String =
8+
~inspectBodyImpl('(i))
9+
10+
def inspectBodyImpl(x: Expr[Int])(implicit tasty: Tasty): Expr[String] = {
11+
import tasty._
12+
13+
def definitionString(tree: Tree): Expr[String] = tree.symbol.tree match {
14+
case Some(definition) => definition.show.toExpr
15+
case None => '("NO DEFINTION")
16+
}
17+
18+
x.toTasty match {
19+
case Term.Inlined(None, Nil, arg) => definitionString(arg)
20+
case arg => definitionString(arg) // TODO should all by name parameters be in an inline node
21+
}
22+
}
23+
24+
def foo: Int = 1 + 2
25+
val bar: Int = 2 + 3
26+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
println(Foo.inspectBody(Foo.foo))
5+
println(Foo.inspectBody(Foo.bar))
6+
}
7+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DefDef("foo", Nil, Nil, TypeTree.TypeIdent("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(1)), "+", Some(Signature(List(scala.Int), scala.Int))), List(Term.Literal(Constant.Int(2))))))
2+
ValDef("bar", TypeTree.TypeIdent("Int"), Some(Term.Apply(Term.Select(Term.Literal(Constant.Int(2)), "+", Some(Signature(List(scala.Int), scala.Int))), List(Term.Literal(Constant.Int(3))))))
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import scala.quoted._
2+
3+
import scala.tasty._
4+
5+
object Foo {
6+
7+
rewrite def inspectBody(i: => Int): String =
8+
~inspectBodyImpl('(i))
9+
10+
def inspectBodyImpl(x: Expr[Int])(implicit tasty: Tasty): Expr[String] = {
11+
import tasty._
12+
13+
def definitionString(tree: Tree): Expr[String] = tree.symbol.tree match {
14+
case Some(definition) => definition.show.toExpr
15+
case None => '("NO DEFINTION")
16+
}
17+
18+
x.toTasty match {
19+
case Term.Inlined(None, Nil, arg) => definitionString(arg)
20+
case arg => definitionString(arg) // TODO should all by name parameters be in an inline node
21+
}
22+
}
23+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
println(Foo.inspectBody(foo))
5+
println(Foo.inspectBody(bar))
6+
}
7+
8+
def foo: Int = 1 + 2
9+
val bar: Int = 2 + 3
10+
}

tests/run/tasty-definitions-2.check

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/run/tasty-definitions-3.check

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/run/tasty-extractors-owners.check

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)