scala.reflect.macros.Reifiers.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-reflect Show documentation
Show all versions of scala-reflect Show documentation
Compiler for the Scala Programming Language
package scala.reflect
package macros
/**
* EXPERIMENTAL
*
* A slice of [[scala.reflect.macros.Context the Scala macros context]] that
* exposes functions to save reflection artifacts for runtime.
*/
trait Reifiers {
self: Context =>
/** Given a tree, generate a tree that when compiled and executed produces the original tree.
* For more information and examples see the documentation for ``Universe.reify''.
*
* The produced tree will be bound to the specified ``universe'' and ``mirror''.
* Possible values for ``universe'' include ``universe.treeBuild.mkRuntimeUniverseRef''.
* Possible values for ``mirror'' include ``EmptyTree'' (in that case the reifier will automatically pick an appropriate mirror).
*
* This function is deeply connected to ``Universe.reify'', a macro that reifies arbitrary expressions into runtime trees.
* They do very similar things (``Universe.reify'' calls ``Context.reifyTree'' to implement itself), but they operate on different metalevels (see below).
*
* Let's study the differences between ``Context.reifyTree'' and ``Universe.reify'' on an example of using them inside a ``fooMacro'' macro:
*
* * Since reify itself is a macro, it will be executed when fooMacro is being compiled (metalevel -1)
* and will produce a tree that when evaluated during macro expansion of fooMacro (metalevel 0) will recreate the input tree.
*
* This provides a facility analogous to quasi-quoting. Writing "reify{ expr }" will generate an AST that represents expr.
* Afterwards this AST (or its parts) can be used to construct the return value of fooMacro.
*
* * reifyTree is evaluated during macro expansion (metalevel 0)
* and will produce a tree that when evaluated during the runtime of the program (metalevel 1) will recreate the input tree.
*
* This provides a way to retain certain trees from macro expansion time to be inspected later, in the runtime.
* For example, DSL authors may find it useful to capture DSL snippets into ASTs that are then processed at runtime in a domain-specific way.
*
* Also note the difference between universes of the runtime trees produced by two reifies:
*
* * The result of compiling and running the result of reify will be bound to the Universe that called reify.
* This is possible because it's a macro, so it can generate whatever code it wishes.
*
* * The result of compiling and running the result of reifyTree will be the ``prefix'' that needs to be passed explicitly.
* This happens because the Universe of the evaluated result is from a different metalevel than the Context the called reify.
*
* Typical usage of this function is to retain some of the trees received/created by a macro
* into the form that can be inspected (via pattern matching) or compiled/run (by a reflective ToolBox) during the runtime.
*/
def reifyTree(universe: Tree, mirror: Tree, tree: Tree): Tree
/** Given a type, generate a tree that when compiled and executed produces the original type.
* The produced tree will be bound to the specified ``universe'' and ``mirror''.
* For more information and examples see the documentation for ``Context.reifyTree'' and ``Universe.reify''.
*/
def reifyType(universe: Tree, mirror: Tree, tpe: Type, concrete: Boolean = false): Tree
/** Given a type, generate a tree that when compiled and executed produces the runtime class of the original type.
* If ``concrete'' is true, then this function will bail on types, who refer to abstract types (like `ClassTag` does).
*/
def reifyRuntimeClass(tpe: Type, concrete: Boolean = true): Tree
/** Given a type, generate a tree that when compiled and executed produces the runtime class of the enclosing class or module.
* Returns `EmptyTree` if there does not exist an enclosing class or module.
*/
def reifyEnclosingRuntimeClass: Tree
/** Undoes reification of a tree.
*
* This reversion doesn't simply restore the original tree (that would lose the context of reification),
* but does something more involved that conforms to the following laws:
*
* 1) unreifyTree(reifyTree(tree)) != tree // unreified tree is tree + saved context
* // in current implementation, the result of unreify is opaque
* // i.e. there's no possibility to inspect underlying tree/context
*
* 2) reifyTree(unreifyTree(reifyTree(tree))) == reifyTree(tree) // the result of reifying a tree in its original context equals to
* // the result of reifying a tree along with its saved context
*
* 3) compileAndEval(unreifyTree(reifyTree(tree))) ~ compileAndEval(tree) // at runtime original and unreified trees are behaviorally equivalent
*/
def unreifyTree(tree: Tree): Tree
}
// made these guys non path-dependent, otherwise exception handling quickly becomes a mess
/** Indicates an expected error during one of the `reifyXXX` methods in [[scala.reflect.macros.Reifiers]].
* Such errors represent one of the standard ways for reification to go wrong, e.g.
* an attempt to create a `TypeTag` from a weak type.
*/
case class ReificationException(val pos: scala.reflect.api.Position, val msg: String) extends Exception(msg)
/** Indicates an unexpected expected error during one of the `reifyXXX` methods in [[scala.reflect.macros.Reifiers]].
* Such errors wrap random crashes in reification logic and are distinguished from expected [[scala.reflect.macros.ReificationException]]s
* so that the latter can be reported as compilation errors, while the former manifest themselves as compiler crashes.
*/
case class UnexpectedReificationException(val pos: scala.reflect.api.Position, val msg: String, val cause: Throwable = null) extends Exception(msg, cause)