summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-06-06 14:29:05 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-06-08 15:32:03 +0200
commit6355d1a0b825c99560d4ccec1a8769f7421b1a71 (patch)
tree80f448f0da11dcab9cee30f3d8fe867cd66313ed /src/compiler/scala/reflect/reify/codegen/GenTrees.scala
parentce67870e64afabf75363679bcee597812ad223e9 (diff)
downloadscala-6355d1a0b825c99560d4ccec1a8769f7421b1a71.tar.gz
scala-6355d1a0b825c99560d4ccec1a8769f7421b1a71.tar.bz2
scala-6355d1a0b825c99560d4ccec1a8769f7421b1a71.zip
brings reification up to speed
Along with recovering from reflection refactoring, I implemented some new features (e.g. rollback of macro expansions), and did some stabilizing refactorings (e.g. moved mutable state into a ghetto). Also used the refactoring as a chance to fix free and aux symbols. Encapsulated this notion in a symbol table class, which allowed me to address outstanding issues with symbol table inheritance and inlining.
Diffstat (limited to 'src/compiler/scala/reflect/reify/codegen/GenTrees.scala')
-rw-r--r--src/compiler/scala/reflect/reify/codegen/GenTrees.scala218
1 files changed, 218 insertions, 0 deletions
diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
new file mode 100644
index 0000000000..b97bf6b0cd
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
@@ -0,0 +1,218 @@
+package scala.reflect.reify
+package codegen
+
+trait GenTrees {
+ self: Reifier =>
+
+ import global._
+ import definitions._
+
+ // unfortunately, these are necessary to reify AnnotatedTypes
+ // I'd gladly get rid of them, but I don't fancy making a metaprogramming API that doesn't work with annotated types
+ // luckily for our sanity, these vars are mutated only within a very restricted code execution path
+ def reifyTreeSymbols: Boolean = state.reifyTreeSymbols
+ def reifyTreeTypes: Boolean = state.reifyTreeTypes
+
+ /**
+ * Reify a tree.
+ * For internal use only, use ``reified'' instead.
+ */
+ def reifyTree(tree: Tree): Tree = {
+ assert(tree != null, "tree is null")
+
+ if (tree.isErroneous)
+ CannotReifyErroneousReifee(tree)
+
+ val splicedTree = spliceTree(tree)
+ if (splicedTree != EmptyTree)
+ return splicedTree
+
+ // the idea behind the new reincarnation of reifier is a simple maxim:
+ //
+ // never call ``reifyType'' to reify a tree
+ //
+ // this works because the stuff we are reifying was once represented with trees only
+ // and lexical scope information can be fully captured by reifying symbols
+ //
+ // to enable this idyll, we work hard in the ``Reshape'' phase
+ // which replaces all types with equivalent trees and works around non-idempotencies of the typechecker
+ //
+ // why bother? because this brings method to the madness
+ // the first prototype of reification reified all types and symbols for all trees => this quickly became unyieldy
+ // the second prototype reified external types, but avoided reifying local ones => this created an ugly irregularity
+ // current approach is uniform and compact
+ var rtree = tree match {
+ case global.EmptyTree =>
+ reifyMirrorObject(EmptyTree)
+ case global.emptyValDef =>
+ mirrorBuildSelect(nme.emptyValDef)
+ case FreeDef(_, _, _, _, _) =>
+ reifyNestedFreeDef(tree)
+ case FreeRef(_, _) =>
+ reifyNestedFreeRef(tree)
+ case BoundTerm(tree) =>
+ reifyBoundTerm(tree)
+ case BoundType(tree) =>
+ reifyBoundType(tree)
+ case Literal(const @ Constant(_)) =>
+ mirrorCall(nme.Literal, reifyProduct(const))
+ case Import(expr, selectors) =>
+ mirrorCall(nme.Import, reify(expr), mkList(selectors map reifyProduct))
+ case _ =>
+ reifyProduct(tree)
+ }
+
+ // usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation
+ // however, reification of AnnotatedTypes is special. see ``reifyType'' to find out why.
+ if (reifyTreeSymbols && tree.hasSymbol) {
+ if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree))
+ rtree = mirrorBuildCall(nme.setSymbol, rtree, reify(tree.symbol))
+ }
+ if (reifyTreeTypes && tree.tpe != null) {
+ if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree))
+ rtree = mirrorBuildCall(nme.setType, rtree, reify(tree.tpe))
+ }
+
+ rtree
+ }
+
+ def reifyModifiers(m: global.Modifiers) =
+ mirrorFactoryCall(nme.Modifiers, mirrorBuildCall(nme.flagsFromBits, reify(m.flags)), reify(m.privateWithin), reify(m.annotations))
+
+ private def spliceTree(tree: Tree): Tree = {
+ tree match {
+ case TreeSplice(splicee) =>
+ if (reifyDebug) println("splicing " + tree)
+
+ // see ``Metalevels'' for more info about metalevel breaches
+ // and about how we deal with splices that contain them
+ val isMetalevelBreach = splicee exists (sub => sub.hasSymbol && sub.symbol != NoSymbol && sub.symbol.metalevel > 0)
+ val isRuntimeEval = splicee exists (sub => sub.hasSymbol && sub.symbol == ExprSplice)
+ if (isMetalevelBreach || isRuntimeEval) {
+ // we used to convert dynamic splices into runtime evals transparently, but we no longer do that
+ // why? see comments in ``Metalevels''
+ // if (reifyDebug) println("splicing has failed: cannot splice when facing a metalevel breach")
+ // EmptyTree
+ CannotReifyRuntimeSplice(tree)
+ } else {
+ if (reifyDebug) println("splicing has succeeded")
+ splicee match {
+ // we intentionally don't care about the prefix (the first underscore in the `RefiedTree` pattern match)
+ case ReifiedTree(_, _, inlinedSymtab, rtree, _, _, _) =>
+ if (reifyDebug) println("inlining the splicee")
+ // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels''
+ inlinedSymtab.syms foreach (sym => if (sym.isLocalToReifee) assert(false, inlinedSymtab.symDef(sym)))
+ state.symtab ++= inlinedSymtab
+ rtree
+ case tree =>
+ val migrated = Apply(Select(splicee, nme.in), List(Ident(nme.MIRROR_SHORT)))
+ Select(migrated, nme.tree)
+ }
+ }
+ case _ =>
+ EmptyTree
+ }
+ }
+
+ // unlike in `reifyBoundType` we can skip checking for `tpe` being local or not local w.r.t the reifee
+ // a single check for a symbol of the bound term should be enough
+ // that's because only Idents and Thises can be bound terms, and they cannot host complex types
+ private def reifyBoundTerm(tree: Tree): Tree = tree match {
+ case tree @ This(_) if tree.symbol == NoSymbol =>
+ throw new Error("unexpected: bound term that doesn't have a symbol: " + showRaw(tree))
+ case tree @ This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass && !tree.symbol.isLocalToReifee =>
+ val sym = tree.symbol
+ if (reifyDebug) println("This for %s, reified as freeVar".format(sym))
+ if (reifyDebug) println("Free: " + sym)
+ mirrorBuildCall(nme.Ident, reifyFreeTerm(sym, This(sym)))
+ case tree @ This(_) if !tree.symbol.isLocalToReifee =>
+ if (reifyDebug) println("This for %s, reified as This".format(tree.symbol))
+ mirrorBuildCall(nme.This, reify(tree.symbol))
+ case tree @ This(_) if tree.symbol.isLocalToReifee =>
+ mirrorCall(nme.This, reify(tree.qual))
+ case tree @ Ident(_) if tree.symbol == NoSymbol =>
+ // this sometimes happens, e.g. for binds that don't have a body
+ // or for untyped code generated during previous phases
+ // (see a comment in Reifiers about the latter, starting with "why do we resetAllAttrs?")
+ mirrorCall(nme.Ident, reify(tree.name))
+ case tree @ Ident(_) if !tree.symbol.isLocalToReifee =>
+ if (tree.symbol.isVariable && tree.symbol.owner.isTerm) {
+ captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reification here.
+ mirrorCall(nme.Select, mirrorBuildCall(nme.Ident, reify(tree.symbol)), reify(nme.elem))
+ } else {
+ mirrorBuildCall(nme.Ident, reify(tree.symbol))
+ }
+ case tree @ Ident(_) if tree.symbol.isLocalToReifee =>
+ mirrorCall(nme.Ident, reify(tree.name))
+ case _ =>
+ throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass))
+ }
+
+ private def reifyBoundType(tree: Tree): Tree = {
+ def reifyBoundType(tree: Tree): Tree = {
+ if (tree.tpe == null)
+ throw new Error("unexpected: bound type that doesn't have a tpe: " + showRaw(tree))
+
+ // if a symbol or a type of the scrutinee are local to reifee
+ // (e.g. point to a locally declared class or to a path-dependent thingie that depends on a local variable)
+ // then we can reify the scrutinee as a symless AST and that will definitely be hygienic
+ // why? because then typechecking of a scrutinee doesn't depend on the environment external to the quasiquote
+ // otherwise we need to reify the corresponding type
+ if (tree.symbol.isLocalToReifee || tree.tpe.isLocalToReifee)
+ reifyProduct(tree)
+ else {
+ val sym0 = tree.symbol
+ val sym = sym0.dealias
+ val tpe0 = tree.tpe
+ val tpe = tpe0.dealias
+ if (reifyDebug) println("reifying bound type %s (underlying type is %s, dealiased is %s)".format(sym0, tpe0, tpe))
+
+ if (tpe.isSpliceable) {
+ val spliced = spliceType(tpe)
+ if (spliced == EmptyTree) {
+ if (reifyDebug) println("splicing failed: reify as is")
+ mirrorBuildCall(nme.TypeTree, reify(tpe))
+ } else {
+ spliced match {
+ case TypeRefToFreeType(freeType) =>
+ if (reifyDebug) println("splicing returned a free type: " + freeType)
+ Ident(freeType)
+ case _ =>
+ if (reifyDebug) println("splicing succeeded: " + spliced)
+ mirrorBuildCall(nme.TypeTree, spliced)
+ }
+ }
+ } else {
+ if (sym.isLocatable) {
+ if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym))
+ mirrorBuildCall(nme.Ident, reify(sym))
+ } else {
+ if (reifyDebug) println("tpe is an alias, but not a locatable: reify as TypeTree(%s)".format(tpe))
+ mirrorBuildCall(nme.TypeTree, reify(tpe))
+ }
+ }
+ }
+ }
+
+ tree match {
+ case Select(_, _) =>
+ reifyBoundType(tree)
+ case SelectFromTypeTree(_, _) =>
+ reifyBoundType(tree)
+ case Ident(_) =>
+ reifyBoundType(tree)
+ case _ =>
+ throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass))
+ }
+ }
+
+ private def reifyNestedFreeDef(tree: Tree): Tree = {
+ if (reifyDebug) println("nested free def: %s".format(showRaw(tree)))
+ reifyProduct(tree)
+ }
+
+ private def reifyNestedFreeRef(tree: Tree): Tree = {
+ if (reifyDebug) println("nested free ref: %s".format(showRaw(tree)))
+ reifyProduct(tree)
+ }
+}