diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-06-06 14:29:05 +0200 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-06-08 15:32:03 +0200 |
commit | 6355d1a0b825c99560d4ccec1a8769f7421b1a71 (patch) | |
tree | 80f448f0da11dcab9cee30f3d8fe867cd66313ed /src/compiler/scala/reflect/reify/codegen/GenTrees.scala | |
parent | ce67870e64afabf75363679bcee597812ad223e9 (diff) | |
download | scala-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.scala | 218 |
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) + } +} |