1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
package scala.reflect.reify
import scala.tools.nsc.Global
import scala.reflect.macros.ReificationException
import scala.reflect.macros.UnexpectedReificationException
import scala.reflect.reify.utils.Utils
/** Given a tree or a type, generate a tree that when executed at runtime produces the original tree or type.
* See more info in the comments to `reify` in scala.reflect.api.Universe.
*
* @author Martin Odersky
* @version 2.10
* @since 2.10
*/
abstract class Reifier extends States
with Phases
with Errors
with Utils {
val global: Global
import global._
import definitions._
private val runDefinitions = currentRun.runDefinitions
import runDefinitions._
val typer: global.analyzer.Typer
val universe: Tree
val mirror: Tree
val reifee: Any
val concrete: Boolean
// needed to seamlessly integrate with standalone utils
override def getReifier: Reifier { val global: Reifier.this.global.type } =
this.asInstanceOf[Reifier { val global: Reifier.this.global.type }]
override def hasReifier = true
/** For `reifee` and other reification parameters, generate a tree of the form
* {{{
* {
* val \$u: universe.type = <[ universe ]>
* val \$m: \$u.Mirror = <[ mirror ]>
* \$u.Expr[T](rtree) // if data is a Tree
* \$u.TypeTag[T](rtree) // if data is a Type
* }
* }}}
*
* where
*
* - `universe` is the tree that represents the universe the result will be bound to.
* - `mirror` is the tree that represents the mirror the result will be initially bound to.
* - `rtree` is code that generates `reifee` at runtime.
* - `T` is the type that corresponds to `data`.
*
* This is not a method, but a value to indicate the fact that Reifier instances are a one-off.
*/
lazy val reification: Tree = {
try {
if (universe exists (_.isErroneous)) CannotReifyErroneousPrefix(universe)
if (universe.tpe == null) CannotReifyUntypedPrefix(universe)
val result = reifee match {
case tree: Tree =>
reifyTrace("reifying = ")(if (settings.Xshowtrees || settings.XshowtreesCompact || settings.XshowtreesStringified) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
reifyTrace("reifee is located at: ")(tree.pos)
reifyTrace("universe = ")(universe)
reifyTrace("mirror = ")(mirror)
if (tree exists (_.isErroneous)) CannotReifyErroneousReifee(tree)
if (tree.tpe == null) CannotReifyUntypedReifee(tree)
val pipeline = mkReificationPipeline
val rtree = pipeline(tree)
val tpe = typer.packedType(tree, NoSymbol)
val ReifiedType(_, _, tpeSymtab, _, rtpe, tpeReificationIsConcrete) = `package`.reifyType(global)(typer, universe, mirror, tpe, concrete = false)
state.reificationIsConcrete &= tpeReificationIsConcrete
state.symtab ++= tpeSymtab
ReifiedTree(universe, mirror, symtab, rtree, tpe, rtpe, reificationIsConcrete)
case tpe: Type =>
reifyTrace("reifying = ")(tpe.toString)
reifyTrace("universe = ")(universe)
reifyTrace("mirror = ")(mirror)
val rtree = reify(tpe)
ReifiedType(universe, mirror, symtab, tpe, rtree, reificationIsConcrete)
case _ =>
throw new Error("reifee %s of type %s is not supported".format(reifee, if (reifee == null) "null" else reifee.getClass.toString))
}
// todo. why do we reset attrs?
//
// typically we do some preprocessing before reification and
// the code emitted/moved around during preprocessing is very hard to typecheck, so we leave it as it is
// however this "as it is" sometimes doesn't make any sense
//
// ===example 1===
// we move a freevar from a nested symbol table to a top-level symbol table,
// and then the reference to $u becomes screwed up, because nested symbol tables are already typechecked,
// so we have an $u symbol that points to the nested $u rather than to the top-level one.
//
// ===example 2===
// we inline a freevar by replacing a reference to it, e.g. $u.Apply($u.Select($u.Ident($u.newTermName("$u")), $u.newTermName("Ident")), List($u.Ident($u.newTermName("free$x"))))
// with its original binding (e.g. $u.Ident("x"))
// we'd love to typecheck the result, but we cannot do this easily, because $u is external to this tree
// what's even worse, sometimes $u can point to the top-level symbol table's $u, which doesn't have any symbol/type yet -
// it's just a ValDef that will be emitted only after the reification is completed
//
// hence, the simplest solution is to erase all attrs so that invalid (as well as non-existent) bindings get rebound correctly
// this is ugly, but it's the best we can do
//
// todo. this is a common problem with non-trivial macros in our current macro system
// needs to be solved some day
// upd. a new hope: https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ
val untyped = resetAttrs(result, leaveAlone = {
case ValDef(_, u, _, _) if u == nme.UNIVERSE_SHORT => true
case ValDef(_, m, _, _) if m == nme.MIRROR_SHORT => true
case tree if symtab.syms contains tree.symbol => true
case _ => false
})
if (reifyCopypaste) {
if (reifyDebug) println("=============================")
println(reifiedNodeToString(untyped))
if (reifyDebug) println("=============================")
} else {
reifyTrace("reification = ")(untyped)
}
untyped
} catch {
case ex: ReificationException =>
throw ex
case ex: UnexpectedReificationException =>
throw ex
case ex: Throwable =>
throw new UnexpectedReificationException(defaultErrorPosition, "reification crashed", ex)
}
}
}
|