summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/reify
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/reflect/reify')
-rw-r--r--src/compiler/scala/reflect/reify/Errors.scala63
-rw-r--r--src/compiler/scala/reflect/reify/NodePrinters.scala111
-rw-r--r--src/compiler/scala/reflect/reify/Phases.scala42
-rw-r--r--src/compiler/scala/reflect/reify/Reifiers.scala154
-rw-r--r--src/compiler/scala/reflect/reify/codegen/Names.scala15
-rw-r--r--src/compiler/scala/reflect/reify/codegen/Positions.scala18
-rw-r--r--src/compiler/scala/reflect/reify/codegen/Symbols.scala111
-rw-r--r--src/compiler/scala/reflect/reify/codegen/Trees.scala220
-rw-r--r--src/compiler/scala/reflect/reify/codegen/Types.scala226
-rw-r--r--src/compiler/scala/reflect/reify/codegen/Util.scala112
-rw-r--r--src/compiler/scala/reflect/reify/package.scala22
-rw-r--r--src/compiler/scala/reflect/reify/phases/Calculate.scala61
-rw-r--r--src/compiler/scala/reflect/reify/phases/Metalevels.scala148
-rw-r--r--src/compiler/scala/reflect/reify/phases/Reify.scala42
-rw-r--r--src/compiler/scala/reflect/reify/phases/Reshape.scala296
15 files changed, 1641 insertions, 0 deletions
diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala
new file mode 100644
index 0000000000..8bfe64621b
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/Errors.scala
@@ -0,0 +1,63 @@
+package scala.reflect
+package reify
+
+import scala.tools.nsc.Global
+
+trait Errors {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+
+ class ReificationError(var pos: Position, val msg: String) extends Throwable(msg)
+ class UnexpectedReificationError(val pos: Position, val msg: String, val cause: Throwable = null) extends Throwable(msg)
+
+ lazy val defaultErrorPosition: Position =
+ mirror.analyzer.openMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication.pos).getOrElse(NoPosition)
+
+ // expected errors: these can happen if the user casually writes whatever.reify(...)
+ // hence we don't crash here, but nicely report a typechecking error and bail out asap
+
+ def CannotReifyReifeeThatHasTypeLocalToReifee(tree: Tree) = {
+ val msg = "implementation restriction: cannot reify block of type %s that involves a type declared inside the block being reified. consider casting the return value to a suitable type".format(tree.tpe)
+ throw new ReificationError(tree.pos, msg)
+ }
+
+ def CannotReifyType(tpe: Type) = {
+ val msg = "implementation restriction: cannot reify type %s (%s)".format(tpe, tpe.kind)
+ throw new ReificationError(defaultErrorPosition, msg)
+ }
+
+ def CannotReifySymbol(sym: Symbol) = {
+ val msg = "implementation restriction: cannot reify symbol %s (%s)".format(sym, sym.accurateKindString)
+ throw new ReificationError(defaultErrorPosition, msg)
+ }
+
+ def CannotReifyGroundTypeTagHavingUnresolvedTypeParameters(tpe: Type) = {
+ val msg = "cannot reify GroundTypeTag having unresolved type parameter %s".format(tpe)
+ throw new ReificationError(defaultErrorPosition, msg)
+ }
+
+ // unexpected errors: these can never happen under normal conditions unless there's a bug in the compiler (or in a compiler plugin or in a macro)
+ // hence, we fail fast and loudly and don't care about being nice - in this situation noone will appreciate our quiet nicety
+
+ def CannotReifyUntypedPrefix(prefix: Tree) = {
+ val msg = "internal error: untyped prefixes are not supported, consider typechecking the prefix before passing it to the reifier"
+ throw new UnexpectedReificationError(defaultErrorPosition, msg)
+ }
+
+ def CannotReifyUntypedReifee(reifee: Any) = {
+ val msg = "internal error: untyped trees are not supported, consider typechecking the reifee before passing it to the reifier"
+ throw new UnexpectedReificationError(defaultErrorPosition, msg)
+ }
+
+ def CannotReifyErroneousPrefix(prefix: Tree) = {
+ val msg = "internal error: erroneous prefixes are not supported, make sure that your prefix has typechecked successfully before passing it to the reifier"
+ throw new UnexpectedReificationError(defaultErrorPosition, msg)
+ }
+
+ def CannotReifyErroneousReifee(reifee: Any) = {
+ val msg = "internal error: erroneous reifees are not supported, make sure that your reifee has typechecked successfully before passing it to the reifier"
+ throw new UnexpectedReificationError(defaultErrorPosition, msg)
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/NodePrinters.scala b/src/compiler/scala/reflect/reify/NodePrinters.scala
new file mode 100644
index 0000000000..eaca9a4968
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/NodePrinters.scala
@@ -0,0 +1,111 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.reflect
+package reify
+
+import scala.Array.canBuildFrom
+import scala.compat.Platform.EOL
+import scala.tools.nsc.symtab.Flags
+import scala.tools.nsc.Global
+
+trait NodePrinters { self: scala.tools.nsc.ast.NodePrinters =>
+
+ val global: Global
+ import global._
+
+ object reifiedNodeToString extends Function2[Tree, Tree, String] {
+ def apply(prefix: Tree, tree: Tree): String = {
+ import scala.reflect.api.Modifier
+ var modifierIsUsed = false
+ var flagsAreUsed = false
+
+ // @PP: I fervently hope this is a test case or something, not anything being
+ // depended upon. Of more fragile code I cannot conceive.
+ // @Eugene: This stuff is only needed to debug-print out reifications in human-readable format
+ // Rolling a full-fledged, robust TreePrinter would be several times more code.
+ val (List(mirror), reified) = (for (line <- (tree.toString.split(EOL).toList drop 1 dropRight 1)) yield {
+ var s = line.trim
+ s = s.replace("$mr.", "")
+ s = s.replace(".apply", "")
+ s = s.replace("scala.collection.immutable.", "")
+ s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List")
+ s = "List\\[.*?\\]".r.replaceAllIn(s, "List")
+ s = s.replace("immutable.this.Nil", "List()")
+ s = s.replace("modifiersFromInternalFlags", "Modifiers")
+ s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()")
+ s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => {
+ val buf = new collection.mutable.ListBuffer[String]
+
+ val annotations = m.group(3)
+ if (buf.nonEmpty || annotations.nonEmpty)
+ buf.append("List(" + annotations + ")")
+
+ val privateWithin = "" + m.group(2)
+ if (buf.nonEmpty || privateWithin != "")
+ buf.append("newTypeName(\"" + privateWithin + "\")")
+
+ val flags = m.group(1).toLong
+ val s_flags = Flags.modifiersOfFlags(flags) map (_.sourceString) mkString ", "
+ if (buf.nonEmpty || s_flags != "") {
+ modifierIsUsed = true
+ buf.append("Set(" + s_flags + ")")
+ }
+
+ "Modifiers(" + buf.reverse.mkString(", ") + ")"
+ })
+ s = """setInternalFlags\((\d+)L\)""".r.replaceAllIn(s, m => {
+ flagsAreUsed = true
+ val flags = m.group(1).toLong
+ val mods = Flags.modifiersOfFlags(flags) map (_.sourceString)
+ "setInternalFlags(flagsOfModifiers(List(" + mods.mkString(", ") + ")))"
+ })
+
+ s
+ }) splitAt 1
+
+ val printout = collection.mutable.ListBuffer(mirror);
+ printout += "import " + nme.MIRROR_SHORT + "._"
+ if (modifierIsUsed) printout += "import scala.reflect.api.Modifier._"
+ if (flagsAreUsed) printout += "import scala.reflect.internal.Flags._"
+ val body = reified dropWhile (_.startsWith("val"))
+ if (body.length > 0 && body(0).startsWith("Expr[")) {
+ if (reified(0) startsWith "val") {
+ printout += "val code = {"
+ printout ++= (reified map (" " + _))
+ printout += "}"
+ printout += "mkToolBox().runExpr(code)"
+ } else {
+ printout += "val code = " + reified(0)
+ printout ++= reified drop 1
+ printout += "mkToolBox().runExpr(code)"
+ }
+ try {
+ val prefix = Select(Select(Ident(definitions.ScalaPackage), newTermName("reflect")), newTermName("mirror"))
+ val tree1 = new global.Transformer {
+ override def transform(tree: Tree) = super.transform(tree match {
+ case Block(ValDef(_, mr, _, _) :: Nil, expr) if mr == nme.MIRROR_SHORT => transform(expr)
+ case Block(ValDef(_, mr, _, _) :: symbolTable, expr) if mr == nme.MIRROR_SHORT => transform(Block(symbolTable, expr))
+ case Select(Ident(mr), name) if mr == nme.MIRROR_SHORT => Select(prefix, name)
+ case tree => tree
+ })
+ }.transform(tree)
+ val stringified = mkToolBox().runExpr(tree1).toString
+ if (settings.Yreifydebug.value) printout += "*****************************"
+ printout += stringified
+ } catch {
+ case ex: Throwable =>
+// val realex = ReflectionUtils.unwrapThrowable(ex)
+// val message = new java.io.StringWriter()
+// realex.printStackTrace(new java.io.PrintWriter(message))
+// println(message)
+ }
+ } else {
+ printout ++= reified
+ }
+ printout mkString EOL
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/Phases.scala b/src/compiler/scala/reflect/reify/Phases.scala
new file mode 100644
index 0000000000..49d5a45e8e
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/Phases.scala
@@ -0,0 +1,42 @@
+package scala.reflect
+package reify
+
+import scala.reflect.reify.phases._
+
+trait Phases extends Calculate
+ with Reshape
+ with Metalevels
+ with Reify {
+
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+
+ private var alreadyRun = false
+
+ lazy val mkReificationPipeline: Tree => Tree = tree0 => {
+ assert(!alreadyRun, "reifier instance cannot be used more than once")
+ alreadyRun = true
+
+ var tree = tree0
+
+ if (reifyDebug) println("[calculate phase]")
+ calculate.traverse(tree)
+
+ if (reifyDebug) println("[reshape phase]")
+ tree = reshape.transform(tree)
+
+ if (reifyDebug) println("[metalevels phase]")
+ tree = metalevels.transform(tree)
+
+ if (reifyDebug) println("[interlude]")
+ if (reifyDebug) println("symbol table = " + (if (symbolTable.length == 0) "<empty>" else ""))
+ if (reifyDebug) symbolTable foreach (println(_))
+ if (reifyDebug) println("reifee = " + (if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString))
+ if (reifyDebug) println("[reify phase]")
+ var result = reify(tree)
+
+ result
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/Reifiers.scala b/src/compiler/scala/reflect/reify/Reifiers.scala
new file mode 100644
index 0000000000..6854710949
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/Reifiers.scala
@@ -0,0 +1,154 @@
+package scala.reflect
+package reify
+
+import scala.tools.nsc.Global
+
+/** 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
+ */
+abstract class Reifier extends Phases
+ with Errors {
+
+ val mirror: Global
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ val typer: mirror.analyzer.Typer
+ val prefix: Tree
+ val reifee: Any
+ val dontSpliceAtTopLevel: Boolean
+ val requireGroundTypeTag: Boolean
+
+ /**
+ * For ``reifee'' and other reification parameters, generate a tree of the form
+ *
+ * {
+ * val $mr = <[ prefix ]>
+ * $mr.Expr[T](rtree) // if data is a Tree
+ * $mr.TypeTag[T](rtree) // if data is a Type
+ * }
+ *
+ * where
+ *
+ * - `prefix` is the tree that represents the universe
+ * the result will be 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 reified: Tree = {
+ try {
+ // [Eugene] conventional way of doing this?
+ if (prefix exists (_.isErroneous)) CannotReifyErroneousPrefix(prefix)
+ if (prefix.tpe == null) CannotReifyUntypedPrefix(prefix)
+
+ val rtree = reifee match {
+ case tree: Tree =>
+ reifyTrace("reifying = ")(if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
+ reifyTrace("reifee is located at: ")(tree.pos)
+ reifyTrace("prefix = ")(prefix)
+ // [Eugene] conventional way of doing this?
+ if (tree exists (_.isErroneous)) CannotReifyErroneousReifee(prefix)
+ if (tree.tpe == null) CannotReifyUntypedReifee(tree)
+ val pipeline = mkReificationPipeline
+ val rtree = pipeline(tree)
+
+ // consider the following code snippet
+ //
+ // val x = reify { class C; new C }
+ //
+ // inferred type for x will be C
+ // but C ceases to exist after reification so this type is clearly incorrect
+ // however, reify is "just" a library function, so it cannot affect type inference
+ //
+ // hence we crash here even though the reification itself goes well
+ // fortunately, all that it takes to fix the error is to cast "new C" to Object
+ // so I'm not very much worried about introducing this restriction
+ if (tree.tpe exists (sub => sub.typeSymbol.isLocalToReifee))
+ CannotReifyReifeeThatHasTypeLocalToReifee(tree)
+
+ val manifestedType = typer.packedType(tree, NoSymbol)
+ val manifestedRtype = reifyType(manifestedType)
+ val tagModule = if (definitelyGround) GroundTypeTagModule else TypeTagModule
+ var typeTagCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(manifestedType)))
+ var exprCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), ExprModule.name), List(TypeTree(manifestedType)))
+ Apply(Apply(exprCtor, List(rtree)), List(Apply(typeTagCtor, List(manifestedRtype))))
+
+ case tpe: Type =>
+ reifyTrace("reifying = ")(tpe.toString)
+ reifyTrace("prefix = ")(prefix)
+ val rtree = reify(tpe)
+
+ val manifestedType = tpe
+ var tagModule = if (definitelyGround) GroundTypeTagModule else TypeTagModule
+ var ctor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(manifestedType)))
+ Apply(ctor, List(rtree))
+
+ case _ =>
+ throw new Error("reifee %s of type %s is not supported".format(reifee, if (reifee == null) "null" else reifee.getClass.toString))
+ }
+
+ val mirrorAlias = ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(prefix), prefix)
+ val wrapped = Block(mirrorAlias :: symbolTable, rtree)
+
+ // todo. why do we resetAllAttrs?
+ //
+ // 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 mr$ becomes screwed up, because nested symbol tables are already typechecked,
+ // so we have an mr$ symbol that points to the nested mr$ rather than to the top-level one.
+ //
+ // ===example 2===
+ // we inline a freevar by replacing a reference to it, e.g. $mr.Apply($mr.Select($mr.Ident($mr.newTermName("$mr")), $mr.newTermName("Ident")), List($mr.Ident($mr.newTermName("free$x"))))
+ // with its original binding (e.g. $mr.Ident("x"))
+ // we'd love to typecheck the result, but we cannot do this easily, because $mr is external to this tree
+ // what's even worse, sometimes $mr can point to the top-level symbol table's $mr, 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
+ //
+ // list of non-hygienic transformations:
+ // 1) local freetype inlining in Nested
+ // 2) external freevar moving in Nested
+ // 3) local freeterm inlining in Metalevels
+ // 4) trivial tree splice inlining in Reify (Trees.scala)
+ // 5) trivial type splice inlining in Reify (Types.scala)
+ val freevarBindings = symbolTable collect { case freedef @ FreeDef(_, _, binding, _) => binding.symbol } toSet
+ val untyped = resetAllAttrs(wrapped, leaveAlone = {
+ case ValDef(_, mr, _, _) if mr == nme.MIRROR_SHORT => true
+ case tree if freevarBindings contains tree.symbol => true
+ case _ => false
+ })
+
+ if (reifyCopypaste) {
+ if (reifyDebug) println("=============================")
+ println(reifiedNodeToString(prefix, untyped))
+ if (reifyDebug) println("=============================")
+ } else {
+ reifyTrace("reified = ")(untyped)
+ }
+
+ untyped
+ } catch {
+ case ex: ReificationError =>
+ throw ex
+ case ex: UnexpectedReificationError =>
+ throw ex
+ case ex: Throwable =>
+ throw new UnexpectedReificationError(defaultErrorPosition, "reification crashed", ex)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/codegen/Names.scala b/src/compiler/scala/reflect/reify/codegen/Names.scala
new file mode 100644
index 0000000000..589f6355d0
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/codegen/Names.scala
@@ -0,0 +1,15 @@
+package scala.reflect.reify
+package codegen
+
+trait Names {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ def reifyName(name: Name) = {
+ val factory = if (name.isTypeName) nme.nmeNewTypeName else nme.nmeNewTermName
+ mirrorCall(factory, Literal(Constant(name.toString)))
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/codegen/Positions.scala b/src/compiler/scala/reflect/reify/codegen/Positions.scala
new file mode 100644
index 0000000000..ac9195ef31
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/codegen/Positions.scala
@@ -0,0 +1,18 @@
+package scala.reflect.reify
+package codegen
+
+trait Positions {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ // we do not reify positions because this inflates resulting trees, but doesn't buy as anything
+ // where would one use positions? right, in error messages
+ // but I can hardly imagine when one would need a position that points to the reified code
+ // usually reified trees are used to compose macro expansions or to be fed to the runtime compiler
+ // however both macros and toolboxes have their own means to report errors in synthetic trees
+ def reifyPosition(pos: Position): Tree =
+ reifyMirrorObject(NoPosition)
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/codegen/Symbols.scala b/src/compiler/scala/reflect/reify/codegen/Symbols.scala
new file mode 100644
index 0000000000..3328f5e402
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/codegen/Symbols.scala
@@ -0,0 +1,111 @@
+package scala.reflect.reify
+package codegen
+
+trait Symbols {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ /** Reify a reference to a symbol */
+ def reifySymRef(sym0: Symbol): Tree = {
+ assert(sym0 != null, "sym is null")
+ val sym = sym0.dealias
+
+ if (sym == NoSymbol)
+ mirrorSelect(nme.NoSymbol)
+ else if (sym == RootPackage)
+ Select(mirrorSelect(nme.definitions), nme.RootPackage)
+ else if (sym == RootClass)
+ Select(mirrorSelect(nme.definitions), nme.RootClass)
+ else if (sym == EmptyPackage)
+ Select(mirrorSelect(nme.definitions), nme.EmptyPackage)
+ else if (sym == EmptyPackageClass)
+ Select(mirrorSelect(nme.definitions), nme.EmptyPackageClass)
+ else if (sym.isModuleClass)
+ Select(reify(sym.sourceModule), nme.moduleClass)
+ else if (sym.isLocatable) {
+ // [Eugene] am I doing this right?
+// if (sym.isStaticOwner) { // no good for us, because it returns false for packages
+ if (sym.isStatic && (sym.isClass || sym.isModule)) {
+ val resolver = if (sym.isType) nme.staticClass else nme.staticModule
+ mirrorCall(resolver, reify(sym.fullName))
+ } else {
+ if (reifyDebug) println("Locatable: %s (%s) owned by %s (%s) at %s".format(sym, sym.accurateKindString, sym.owner, sym.owner.accurateKindString, sym.owner.fullNameString))
+ val rowner = reify(sym.owner)
+ val rname = reify(sym.name.toString)
+ if (sym.isType)
+ mirrorCall(nme.selectType, rowner, rname)
+ else if (sym.isMethod && sym.owner.isClass && sym.owner.info.decl(sym.name).isOverloaded) {
+ val index = sym.owner.info.decl(sym.name).alternatives indexOf sym
+ assert(index >= 0, sym)
+ mirrorCall(nme.selectOverloadedMethod, rowner, rname, reify(index))
+ } else
+ mirrorCall(nme.selectTerm, rowner, rname)
+ }
+ } else {
+ // todo. make sure that free methods and free local defs work correctly
+ if (sym.isTerm) {
+ if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym)
+ reifyFreeTerm(sym, Ident(sym))
+ } else {
+ if (reifyDebug) println("Free type: " + sym)
+ reifyFreeType(sym, Ident(sym))
+ }
+ }
+ }
+
+ def reifyFreeTerm(sym: Symbol, value: Tree): Tree =
+ locallyReified get sym match {
+ case Some(reified) =>
+ reified
+ case None =>
+ if (sym.isCapturedVariable) {
+ assert(value.isInstanceOf[Ident], showRaw(value))
+ val capturedTpe = capturedVariableType(sym)
+ val capturedValue = referenceCapturedVariable(sym)
+ locallyReify(sym, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(capturedTpe), capturedValue, reify(origin(sym))))
+ } else {
+ locallyReify(sym, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(sym.tpe), value, reify(origin(sym))))
+ }
+ }
+
+ def reifyFreeType(sym: Symbol, value: Tree): Tree =
+ locallyReified get sym match {
+ case Some(reified) =>
+ reified
+ case None =>
+ val phantomTypeTag = Apply(TypeApply(Select(Ident(nme.MIRROR_SHORT), nme.TypeTag), List(value)), List(Literal(Constant(null))))
+ // todo. implement info reification for free types: type bounds, HK-arity, whatever else that can be useful
+ locallyReify(sym, mirrorCall(nme.newFreeType, reify(sym.name.toString), reify(sym.info), phantomTypeTag, reify(origin(sym))))
+ }
+
+ import scala.collection.mutable._
+ private val localReifications = ArrayBuffer[ValDef]()
+ private val locallyReified = Map[Symbol, Tree]()
+ def symbolTable: List[ValDef] = localReifications.toList
+ def symbolTable_=(newSymbolTable: List[ValDef]): Unit = {
+ localReifications.clear()
+ locallyReified.clear()
+ newSymbolTable foreach {
+ case freedef @ FreeDef(_, name, binding, _) =>
+ if (!(locallyReified contains binding.symbol)) {
+ localReifications += freedef
+ locallyReified(binding.symbol) = Ident(name)
+ }
+ }
+ }
+
+ private def locallyReify(sym: Symbol, reificode: => Tree): Tree = {
+ val reified = reificode
+ val Apply(Select(_, flavor), _) = reified
+ // [Eugene] name clashes are impossible, right?
+ var name = newTermName(nme.MIRROR_FREE_PREFIX + sym.name)
+ if (flavor == nme.newFreeTerm && sym.isType) name = name.append(nme.MIRROR_FREE_THIS_SUFFIX);
+ // todo. also reify annotations for free vars
+ localReifications += ValDef(NoMods, name, TypeTree(), reified)
+ locallyReified(sym) = Ident(name)
+ locallyReified(sym)
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/codegen/Trees.scala b/src/compiler/scala/reflect/reify/codegen/Trees.scala
new file mode 100644
index 0000000000..22f42aea49
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/codegen/Trees.scala
@@ -0,0 +1,220 @@
+package scala.reflect.reify
+package codegen
+
+trait Trees {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ /**
+ * 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 mirror.EmptyTree =>
+ reifyMirrorObject(EmptyTree)
+ case mirror.emptyValDef =>
+ mirrorSelect(nme.emptyValDef)
+ case FreeDef(_, _, _, _) =>
+ reifyNestedFreeDef(tree)
+ case FreeRef(_, _) =>
+ reifyNestedFreeRef(tree)
+ case BoundTerm(tree) =>
+ reifyBoundTerm(tree)
+ case BoundType(tree) =>
+ reifyBoundType(tree)
+ case NestedExpr(_, _, _) =>
+ reifyNestedExpr(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)
+ }
+
+ rtree
+ }
+
+ def reifyModifiers(m: mirror.Modifiers) =
+ mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations))
+
+ private def spliceTree(tree: Tree): Tree = {
+ tree match {
+ case EvalSplice(splicee) =>
+ if (reifyDebug) println("splicing eval " + tree)
+
+ // see ``Metalevels'' for more info about metalevel breaches
+ // and about how we deal with splices that contain them
+ if (splicee exists (sub => sub.hasSymbol && sub.symbol != NoSymbol && sub.symbol.metalevel > 0)) {
+ if (reifyDebug) println("splicing has failed: cannot splice when facing a metalevel breach")
+ EmptyTree
+ } else {
+ if (reifyDebug) println("splicing has succeeded")
+ var splice = Select(splicee, nme.tree)
+ splice match {
+ case InlinedTreeSplice(_, inlinedSymbolTable, tree, _) =>
+ if (reifyDebug) println("inlining the splicee")
+ // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels''
+ inlinedSymbolTable foreach { case freedef @ FreeDef(_, _, binding, _) => assert(!binding.symbol.isLocalToReifee, freedef) }
+ symbolTable ++= inlinedSymbolTable
+ tree
+ case tree =>
+ // we need to preserve types of exprs, because oftentimes they cannot be inferred later
+ // this circumvents regular reification scheme, therefore we go the extra mile here
+ new Transformer {
+ override def transform(tree: Tree) = super.transform(tree match {
+ case NestedExpr(factory, tree, typetag) =>
+ val typedFactory = TypeApply(factory, List(TypeTree(typetag.tpe.typeArgs(0))))
+ Apply(Apply(typedFactory, List(tree)), List(typetag))
+ case _ =>
+ tree
+ })
+ }.transform(tree)
+ }
+ }
+ case ValueSplice(splicee) =>
+ // todo. implement this
+ ???
+ case _ =>
+ EmptyTree
+ }
+ }
+
+ 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)
+ mirrorCall(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))
+ mirrorCall(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, mirrorCall(nme.Ident, reify(tree.symbol)), reify(nme.elem))
+ } else {
+ mirrorCall(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 (tree.symbol.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 (eligibleForSplicing(tpe)) {
+ val spliced = spliceType(tpe)
+ if (spliced == EmptyTree) {
+ if (reifyDebug) println("splicing failed: reify as is")
+ mirrorCall(nme.TypeTree, reifyType(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)
+ mirrorCall(nme.TypeTree, spliced)
+ }
+ }
+ } else {
+ if (sym.isLocatable) {
+ if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym))
+ mirrorCall(nme.Ident, reify(sym))
+ } else {
+ if (reifyDebug) println("tpe is an alias, but not a locatable: reify as TypeTree(%s)".format(tpe))
+ mirrorCall(nme.TypeTree, reifyType(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 = tree match {
+ case Apply(Select(mrRef @ Ident(_), ident), List(Ident(name: TermName))) if ident == nme.Ident && name.startsWith(nme.MIRROR_FREE_PREFIX) =>
+ if (reifyDebug) println("nested free ref: %s".format(showRaw(tree)))
+ reifyProduct(tree)
+ case _ =>
+ throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass))
+ }
+
+ private def reifyNestedExpr(tree: Tree): Tree = tree match {
+ case NestedExpr(factory, tree, typetag) =>
+ // we need to preserve types of exprs, because oftentimes they cannot be inferred later
+ // this circumvents regular reification scheme, therefore we go through this crazy dance
+ if (reifyDebug) println("nested expr: %s".format(showRaw(tree)))
+ val rtype = mirrorCall(nme.TypeTree, reify(typetag.tpe.typeArgs(0)))
+ val rfactory = mirrorCall(nme.TypeApply, reify(factory), mkList(List(rtype)))
+ val rexpr = mirrorCall(nme.Apply, rfactory, reify(List(tree)))
+ val rwrapped = mirrorCall(nme.Apply, rexpr, reify(List(typetag)))
+ rwrapped
+ case _ =>
+ throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass))
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/codegen/Types.scala b/src/compiler/scala/reflect/reify/codegen/Types.scala
new file mode 100644
index 0000000000..6f3a60a076
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/codegen/Types.scala
@@ -0,0 +1,226 @@
+package scala.reflect.reify
+package codegen
+
+trait Types {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ /**
+ * Reify a type.
+ * For internal use only, use ``reified'' instead.
+ */
+ def reifyType(tpe0: Type): Tree = {
+ assert(tpe0 != null, "tpe is null")
+ val tpe = tpe0.dealias
+
+ if (tpe.isErroneous)
+ CannotReifyErroneousReifee(tpe)
+ if (tpe.isLocalToReifee)
+ CannotReifyType(tpe)
+
+ // [Eugene] how do I check that the substitution is legal w.r.t tpe.info?
+ val spliced = spliceType(tpe)
+ if (spliced != EmptyTree)
+ return spliced
+
+ val tsym = tpe.typeSymbol
+ if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic)
+ Select(reify(tpe.typeSymbol), nme.asTypeConstructor)
+ else tpe match {
+ case tpe @ NoType =>
+ reifyMirrorObject(tpe)
+ case tpe @ NoPrefix =>
+ reifyMirrorObject(tpe)
+ case tpe @ ThisType(root) if root == RootClass =>
+ mirrorSelect("definitions.RootClass.thisPrefix")
+ case tpe @ ThisType(empty) if empty == EmptyPackageClass =>
+ mirrorSelect("definitions.EmptyPackageClass.thisPrefix")
+ case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic =>
+ mirrorCall(nme.thisModuleType, reify(clazz.fullName))
+ case tpe @ ThisType(_) =>
+ reifyProduct(tpe)
+ case tpe @ SuperType(thistpe, supertpe) =>
+ reifyProduct(tpe)
+ case tpe @ SingleType(pre, sym) =>
+ reifyProduct(tpe)
+ case tpe @ ConstantType(value) =>
+ mirrorFactoryCall(nme.ConstantType, reifyProduct(value))
+ case tpe @ TypeRef(pre, sym, args) =>
+ reifyProduct(tpe)
+ case tpe @ TypeBounds(lo, hi) =>
+ reifyProduct(tpe)
+ case tpe @ NullaryMethodType(restpe) =>
+ reifyProduct(tpe)
+ case tpe @ AnnotatedType(anns, underlying, selfsym) =>
+// reifyAnnotatedType(tpe)
+ CannotReifyType(tpe)
+ case _ =>
+// reifyToughType(tpe)
+ CannotReifyType(tpe)
+ }
+ }
+
+ /** An obscure flag necessary for implicit TypeTag generation */
+ private var spliceTypesEnabled = !dontSpliceAtTopLevel
+
+ /** Keeps track of whether this reification contains abstract type parameters */
+ var maybeGround = true
+ var definitelyGround = true
+
+ def eligibleForSplicing(tpe: Type): Boolean = {
+ // [Eugene] is this comprehensive?
+ // the only thingies that we want to splice are: 1) type parameters, 2) type members
+ // this check seems to cover them all, right?
+ tpe.isInstanceOf[TypeRef] && tpe.typeSymbol.isAbstractType
+ }
+
+ private type SpliceCacheKey = (Symbol, Symbol)
+ private lazy val spliceCache: collection.mutable.Map[SpliceCacheKey, Tree] = {
+ val cache = analyzer.perRunMacroCache.getOrElseUpdate(MacroContextReify, collection.mutable.Map[Any, Any]())
+ cache.getOrElseUpdate("spliceCache", collection.mutable.Map[SpliceCacheKey, Tree]()).asInstanceOf[collection.mutable.Map[SpliceCacheKey, Tree]]
+ }
+
+ def spliceType(tpe: Type): Tree = {
+ if (eligibleForSplicing(tpe)) {
+ if (reifyDebug) println("splicing " + tpe)
+
+ if (spliceTypesEnabled) {
+ var tagClass = if (requireGroundTypeTag) GroundTypeTagClass else TypeTagClass
+ val tagTpe = singleType(prefix.tpe, prefix.tpe member tagClass.name)
+
+ // [Eugene] this should be enough for an abstract type, right?
+ val key = (tagClass, tpe.typeSymbol)
+ if (reifyDebug && spliceCache.contains(key)) println("cache hit: " + spliceCache(key))
+ val result = spliceCache.getOrElseUpdate(key, {
+ // if this fails, it might produce the dreaded "erroneous or inaccessible type" error
+ // to find out the whereabouts of the error run scalac with -Ydebug
+ if (reifyDebug) println("launching implicit search for %s.%s[%s]".format(prefix, tagClass.name, tpe))
+ val positionBearer = mirror.analyzer.openMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication).getOrElse(EmptyTree).asInstanceOf[Tree]
+ typer.resolveTypeTag(positionBearer, prefix.tpe, tpe, requireGroundTypeTag) match {
+ case failure if failure.isEmpty =>
+ if (reifyDebug) println("implicit search was fruitless")
+ definitelyGround &= false
+ maybeGround &= false
+ EmptyTree
+ case success =>
+ if (reifyDebug) println("implicit search has produced a result: " + success)
+ definitelyGround |= requireGroundTypeTag
+ maybeGround |= true
+ var splice = Select(success, nme.tpe)
+ splice match {
+ case InlinedTypeSplice(_, inlinedSymbolTable, tpe) =>
+ // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels''
+ inlinedSymbolTable foreach { case freedef @ FreeDef(_, _, binding, _) => assert(!binding.symbol.isLocalToReifee, freedef) }
+ symbolTable ++= inlinedSymbolTable
+ reifyTrace("inlined the splicee: ")(tpe)
+ case tpe =>
+ tpe
+ }
+ }
+ })
+ if (result != EmptyTree) return result.duplicate
+ } else {
+ if (reifyDebug) println("splicing has been cancelled: spliceTypesEnabled = false")
+ }
+
+ if (requireGroundTypeTag)
+ CannotReifyGroundTypeTagHavingUnresolvedTypeParameters(tpe)
+ }
+
+ spliceTypesEnabled = true
+ EmptyTree
+ }
+
+ // yet another thingie disabled for simplicity
+ // in principle, we could retain and reify AnnotatedTypes
+ // but that'd require reifying every type and symbol inside ann.args
+ // however, since we've given up on tough types for the moment, the former would be problematic
+// private def reifyAnnotatedType(tpe: AnnotatedType): Tree = {
+// // ``Reshaper'' transforms annotation infos from symbols back into Modifier.annotations, which are trees
+// // so the only place on Earth that can lead to reification of AnnotationInfos is the Ay Tee Land
+// // therefore this function is as local as possible, don't move it out of this scope
+// def reifyAnnotationInfo(ann: AnnotationInfo): Tree = {
+// val reifiedArgs = ann.args map { arg =>
+// val saved1 = reifyTreeSymbols
+// val saved2 = reifyTreeTypes
+//
+// try {
+// // one more quirk of reifying annotations
+// //
+// // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs
+// // that's because a lot of logic expects post-typer trees to have non-null tpes
+// //
+// // Q: reified trees are pre-typer, so there's shouldn't be a problem.
+// // reflective typechecker will fill in missing symbols and types, right?
+// // A: actually, no. annotation ASTs live inside AnnotatedTypes,
+// // and insides of the types is the place where typechecker doesn't look.
+// reifyTreeSymbols = true
+// reifyTreeTypes = true
+//
+// // todo. every AnnotationInfo is an island, entire of itself
+// // no regular Traverser or Transformer can reach it
+// // hence we need to run its contents through the entire reification pipeline
+// // e.g. to apply reshaping or to check metalevels
+// reify(arg)
+// } finally {
+// reifyTreeSymbols = saved1
+// reifyTreeTypes = saved2
+// }
+// }
+//
+// def reifyClassfileAnnotArg(arg: ClassfileAnnotArg): Tree = arg match {
+// case LiteralAnnotArg(const) =>
+// mirrorFactoryCall(nme.LiteralAnnotArg, reifyProduct(const))
+// case ArrayAnnotArg(args) =>
+// mirrorFactoryCall(nme.ArrayAnnotArg, scalaFactoryCall(nme.Array, args map reifyClassfileAnnotArg: _*))
+// case NestedAnnotArg(ann) =>
+// mirrorFactoryCall(nme.NestedAnnotArg, reifyAnnotationInfo(ann))
+// }
+//
+// // if you reify originals of anns, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important
+// val reifiedAssocs = ann.assocs map (assoc => scalaFactoryCall(nme.Tuple2, reify(assoc._1), reifyClassfileAnnotArg(assoc._2)))
+// mirrorFactoryCall(nme.AnnotationInfo, reify(ann.atp), mkList(reifiedArgs), mkList(reifiedAssocs))
+// }
+//
+// val AnnotatedType(anns, underlying, selfsym) = tpe
+// mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym))
+// }
+
+ // previous solution to reifying tough types involved creating dummy symbols (see ``registerReifiableSymbol'' calls below)
+ // however such symbols lost all the connections with their origins and became almost useless, except for typechecking
+ // hence this approach was replaced by less powerful, but more principled one based on ``reifyFreeType''
+ // it's possible that later on we will revise and revive ``reifyToughType'', but for now it's disabled under an implementation restriction
+// /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */
+// // This is the uncharted territory in the reifier
+// private def reifyToughType(tpe: Type): Tree = {
+// if (reifyDebug) println("tough type: %s (%s)".format(tpe, tpe.kind))
+//
+// def reifyScope(scope: Scope): Tree = {
+// scope foreach registerReifiableSymbol
+// mirrorCall(nme.newScopeWith, scope.toList map reify: _*)
+// }
+//
+// tpe match {
+// case tpe @ RefinedType(parents, decls) =>
+// registerReifiableSymbol(tpe.typeSymbol)
+// mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol))
+// case tpe @ ExistentialType(tparams, underlying) =>
+// tparams foreach registerReifiableSymbol
+// mirrorFactoryCall(tpe, reify(tparams), reify(underlying))
+// case tpe @ ClassInfoType(parents, decls, clazz) =>
+// registerReifiableSymbol(clazz)
+// mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol))
+// case tpe @ MethodType(params, restpe) =>
+// params foreach registerReifiableSymbol
+// mirrorFactoryCall(tpe, reify(params), reify(restpe))
+// case tpe @ PolyType(tparams, underlying) =>
+// tparams foreach registerReifiableSymbol
+// mirrorFactoryCall(tpe, reify(tparams), reify(underlying))
+// case _ =>
+// throw new Error("internal error: %s (%s) is not supported".format(tpe, tpe.kind))
+// }
+// }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/codegen/Util.scala b/src/compiler/scala/reflect/reify/codegen/Util.scala
new file mode 100644
index 0000000000..bb369a1adb
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/codegen/Util.scala
@@ -0,0 +1,112 @@
+package scala.reflect.reify
+package codegen
+
+trait Util {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ val reifyDebug = settings.Yreifydebug.value
+ val reifyCopypaste = settings.Yreifycopypaste.value
+ val reifyTrace = scala.tools.nsc.util.trace when reifyDebug
+ object reifiedNodePrinters extends { val global: mirror.type = mirror } with tools.nsc.ast.NodePrinters with NodePrinters
+ val reifiedNodeToString = reifiedNodePrinters.reifiedNodeToString
+
+ def reifyList(xs: List[Any]): Tree =
+ mkList(xs map reify)
+
+ def reifyProduct(x: Product): Tree =
+ reifyProduct(x.productPrefix, x.productIterator.toList)
+
+ def reifyProduct(prefix: String, elements: List[Any]): Tree = {
+ // reflection would be more robust, but, hey, this is a hot path
+ if (prefix.startsWith("Tuple")) scalaFactoryCall(prefix, (elements map reify).toList: _*)
+ else mirrorCall(prefix, (elements map reify): _*)
+ }
+
+ // helper functions
+
+ /** Reify a case object defined in Mirror */
+ def reifyMirrorObject(name: String): Tree =
+ mirrorSelect(name)
+
+ def reifyMirrorObject(x: Product): Tree =
+ reifyMirrorObject(x.productPrefix)
+
+ def call(fname: String, args: Tree*): Tree =
+ Apply(termPath(fname), args.toList)
+
+ def mirrorSelect(name: String): Tree =
+ termPath(nme.MIRROR_PREFIX + name)
+
+ def mirrorCall(name: TermName, args: Tree*): Tree =
+ call("" + (nme.MIRROR_PREFIX append name), args: _*)
+
+ def mirrorCall(name: String, args: Tree*): Tree =
+ call(nme.MIRROR_PREFIX + name, args: _*)
+
+ def mirrorFactoryCall(value: Product, args: Tree*): Tree =
+ mirrorFactoryCall(value.productPrefix, args: _*)
+
+ def mirrorFactoryCall(prefix: String, args: Tree*): Tree =
+ mirrorCall(prefix, args: _*)
+
+ def scalaFactoryCall(name: String, args: Tree*): Tree =
+ call("scala." + name + ".apply", args: _*)
+
+ def mkList(args: List[Tree]): Tree =
+ scalaFactoryCall("collection.immutable.List", args: _*)
+
+ /**
+ * An (unreified) path that refers to definition with given fully qualified name
+ * @param mkName Creator for last portion of name (either TermName or TypeName)
+ */
+ def path(fullname: String, mkName: String => Name): Tree = {
+ val parts = fullname split "\\."
+ val prefixParts = parts.init
+ val lastName = mkName(parts.last)
+ if (prefixParts.isEmpty) Ident(lastName)
+ else {
+ val prefixTree = ((Ident(prefixParts.head): Tree) /: prefixParts.tail)(Select(_, _))
+ Select(prefixTree, lastName)
+ }
+ }
+
+ /** An (unreified) path that refers to term definition with given fully qualified name */
+ def termPath(fullname: String): Tree = path(fullname, newTermName)
+
+ /** An (unreified) path that refers to type definition with given fully qualified name */
+ def typePath(fullname: String): Tree = path(fullname, newTypeName)
+
+ def isTough(tpe: Type) = {
+ def isTough(tpe: Type) = tpe match {
+ case _: RefinedType => true
+ case _: ExistentialType => true
+ case _: ClassInfoType => true
+ case _: MethodType => true
+ case _: PolyType => true
+ case _ => false
+ }
+
+ tpe != null && (tpe exists isTough)
+ }
+
+ def isAnnotated(tpe: Type) = {
+ def isAnnotated(tpe: Type) = tpe match {
+ case _: AnnotatedType => true
+ case _ => false
+ }
+
+ tpe != null && (tpe exists isAnnotated)
+ }
+
+ def origin(sym: Symbol) = {
+ var origin = ""
+ if (sym.owner != NoSymbol) origin += "defined by %s".format(sym.owner.name)
+ if (sym.pos != NoPosition) origin += " in %s:%s:%s".format(sym.pos.source.file.name, sym.pos.line, sym.pos.column)
+ if (origin == "") origin = "of unknown origin"
+ origin
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/package.scala b/src/compiler/scala/reflect/reify/package.scala
new file mode 100644
index 0000000000..7041fbf6ed
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/package.scala
@@ -0,0 +1,22 @@
+package scala.reflect
+
+import scala.tools.nsc.Global
+
+package object reify {
+ def mkReifier(global: Global)(typer: global.analyzer.Typer, prefix: global.Tree, reifee: Any, dontSpliceAtTopLevel: Boolean = false, requireGroundTypeTag: Boolean = false): Reifier { val mirror: global.type } = {
+ val typer1: typer.type = typer
+ val prefix1: prefix.type = prefix
+ val reifee1 = reifee
+ val dontSpliceAtTopLevel1 = dontSpliceAtTopLevel
+ val requireGroundTypeTag1 = requireGroundTypeTag
+
+ new {
+ val mirror: global.type = global
+ val typer = typer1
+ val prefix = prefix1
+ val reifee = reifee1
+ val dontSpliceAtTopLevel = dontSpliceAtTopLevel1
+ val requireGroundTypeTag = requireGroundTypeTag1
+ } with Reifier
+ }
+}
diff --git a/src/compiler/scala/reflect/reify/phases/Calculate.scala b/src/compiler/scala/reflect/reify/phases/Calculate.scala
new file mode 100644
index 0000000000..59a36f0ba4
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/phases/Calculate.scala
@@ -0,0 +1,61 @@
+package scala.reflect.reify
+package phases
+
+trait Calculate {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ implicit def sym2richSym(sym: Symbol): RichSymbol = new RichSymbol(sym)
+ class RichSymbol(sym: Symbol) {
+ def metalevel: Int = { assert(sym != NoSymbol); localSymbols.getOrElse(sym, 0) }
+ def isLocalToReifee = (localSymbols contains sym) // [Eugene] how do I account for local skolems?
+ }
+
+ implicit def tpe2richTpe(tpe: Type): RichType = new RichType(tpe)
+ class RichType(tpe: Type) {
+ def isLocalToReifee = tpe != null && (tpe exists (tp => (localSymbols contains tp.typeSymbol) || (localSymbols contains tp.termSymbol)))
+ }
+
+ private var localSymbols = collection.mutable.Map[Symbol, Int]() // set of all symbols that are local to the tree to be reified
+ private def registerLocalSymbol(sym: Symbol, metalevel: Int): Unit =
+ if (sym != null && sym != NoSymbol) {
+ if (localSymbols contains sym)
+ assert(localSymbols(sym) == metalevel, "metalevel mismatch: expected %s, actual %s".format(localSymbols(sym), metalevel))
+ localSymbols(sym) = metalevel
+ }
+
+ /**
+ * Merely traverses the reifiee and records local symbols along with their metalevels.
+ */
+ val calculate = new Traverser {
+ // see the explanation of metalevels in ``Metalevels''
+ var currMetalevel = 1
+
+ override def traverse(tree: Tree): Unit = tree match {
+ case TreeSplice(_) =>
+ currMetalevel -= 1
+ try super.traverse(tree)
+ finally currMetalevel += 1
+ case tree if tree.isDef =>
+ if (reifyDebug) println("boundSym: %s of type %s".format(tree.symbol, (tree.productIterator.toList collect { case tt: TypeTree => tt } headOption).getOrElse(TypeTree(tree.tpe))))
+ registerLocalSymbol(tree.symbol, currMetalevel)
+
+ bindRelatedSymbol(tree.symbol.sourceModule, "sourceModule")
+ bindRelatedSymbol(tree.symbol.moduleClass, "moduleClass")
+ bindRelatedSymbol(tree.symbol.companionClass, "companionClass")
+ bindRelatedSymbol(tree.symbol.companionModule, "companionModule")
+ Some(tree.symbol) collect { case termSymbol: TermSymbol => bindRelatedSymbol(termSymbol.referenced, "referenced") }
+ def bindRelatedSymbol(related: Symbol, name: String): Unit =
+ if (related != null && related != NoSymbol) {
+ if (reifyDebug) println("boundSym (" + name + "): " + related)
+ registerLocalSymbol(related, currMetalevel)
+ }
+ super.traverse(tree)
+ case _ =>
+ super.traverse(tree)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/phases/Metalevels.scala b/src/compiler/scala/reflect/reify/phases/Metalevels.scala
new file mode 100644
index 0000000000..a329a1043d
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/phases/Metalevels.scala
@@ -0,0 +1,148 @@
+package scala.reflect.reify
+package phases
+
+trait Metalevels {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ /**
+ * Makes sense of cross-stage bindings.
+ *
+ * ================
+ *
+ * Analysis of cross-stage bindings becomes convenient if we introduce the notion of metalevels.
+ * Metalevel of a tree is a number that gets incremented every time you reify something and gets decremented when you splice something.
+ * Metalevel of a symbol is equal to the metalevel of its definition.
+ *
+ * Example 1. Consider the following snippet:
+ *
+ * reify {
+ * val x = 2 // metalevel of symbol x is 1, because it's declared inside reify
+ * val y = reify{x} // metalevel of symbol y is 1, because it's declared inside reify
+ * // metalevel of Ident(x) is 2, because it's inside two reifies
+ * y.eval // metalevel of Ident(y) is 0, because it's inside a designator of a splice
+ * }
+ *
+ * Cross-stage bindings are introduced when symbol.metalevel != curr_metalevel.
+ * Both bindings introduced in Example 1 are cross-stage.
+ *
+ * Depending on what side of the inequality is greater, the following situations might occur:
+ *
+ * 1) symbol.metalevel < curr_metalevel. In this case reifier will generate a free variable
+ * that captures both the name of the symbol (to be compiled successfully) and its value (to be run successfully).
+ * For example, x in Example 1 will be reified as follows: Ident(newFreeVar("x", IntClass.tpe, x))
+ *
+ * 2) symbol.metalevel > curr_metalevel. This leads to a metalevel breach that violates intuitive perception of splicing.
+ * As defined in macro spec, splicing takes a tree and inserts it into another tree - as simple as that.
+ * However, how exactly do we do that in the case of y.eval? In this very scenario we can use dataflow analysis and inline it,
+ * but what if y were a var, and what if it were calculated randomly at runtime?
+ *
+ * This question has a genuinely simple answer. Sure, we cannot resolve such splices statically (i.e. during macro expansion of ``reify''),
+ * but now we have runtime toolboxes, so noone stops us from picking up that reified tree and evaluating it at runtime
+ * (in fact, this is something that ``Expr.eval'' and ``Expr.value'' do transparently).
+ *
+ * This is akin to early vs late binding dilemma.
+ * The prior is faster, plus, the latter (implemented with reflection) might not work because of visibility issues or might be not available on all platforms.
+ * But the latter still has its uses, so I'm allowing metalevel breaches, but introducing the -Xlog-runtime-evals to log them.
+ *
+ * ================
+ *
+ * As we can see, the only problem is the fact that lhs'es of eval can be code blocks that can capture variables from the outside.
+ * Code inside the lhs of an eval is not reified, while the code from the enclosing reify is.
+ *
+ * Hence some bindings become cross-stage, which is not bad per se (in fact, some cross-stage bindings have sane semantics, as in the example above).
+ * However this affects freevars, since they are delicate inter-dimensional beings that refer to both current and next planes of existence.
+ * When splicing tears the fabric of the reality apart, some freevars have to go single-dimensional to retain their sanity.
+ *
+ * Example 2. Consider the following snippet:
+ *
+ * reify {
+ * val x = 2
+ * reify{x}.eval
+ * }
+ *
+ * Since the result of the inner reify is wrapped in an eval, it won't be reified
+ * together with the other parts of the outer reify, but will be inserted into that result verbatim.
+ *
+ * The inner reify produces an Expr[Int] that wraps Ident(freeVar("x", IntClass.tpe, x)).
+ * However the freevar the reification points to will vanish when the compiler processes the outer reify.
+ * That's why we need to replace that freevar with a regular symbol that will point to reified x.
+ *
+ * Example 3. Consider the following fragment:
+ *
+ * reify {
+ * val x = 2
+ * val y = reify{x}
+ * y.eval
+ * }
+ *
+ * In this case the inner reify doesn't appear next to eval, so it will be reified together with x.
+ * This means that no special processing is needed here.
+ *
+ * Example 4. Consider the following fragment:
+ *
+ * reify {
+ * val x = 2
+ * {
+ * val y = 2
+ * val z = reify{reify{x + y}}
+ * z.eval
+ * }.eval
+ * }
+ *
+ * The reasoning from Example 2 still holds here - we do need to inline the freevar that refers to x.
+ * However, we must not touch anything inside the eval'd block, because it's not getting reified.
+ */
+ var metalevels = new Transformer {
+ var insideSplice = false
+ var freedefsToInline = collection.mutable.Map[String, ValDef]()
+
+ def withinSplice[T](op: => T) = {
+ val old = insideSplice
+ insideSplice = true
+ try op
+ finally insideSplice = old
+ }
+
+ // Q: here we deal with all sorts of reified trees. what about ReifiedType(_, _, _, _)?
+ // A: nothing. reified trees give us problems because they sometimes create dimensional rifts as described above
+ // to the contrast, reified types (i.e. synthetic typetags materialized by Implicits.scala) always stay on the same metalevel as their enclosing code
+ override def transform(tree: Tree): Tree = tree match {
+ case InlineableTreeSplice(splicee, inlinedSymbolTable, _, _, flavor) =>
+ if (reifyDebug) println("entering inlineable splice: " + splicee)
+ val Block(mrDef :: symbolTable, expr) = splicee
+ // [Eugene] how to express the fact that a scrutinee is both of some type and matches an extractor?
+ val freedefsToInline = symbolTable collect { case freedef @ FreeTermDef(_, _, binding, _) if binding.symbol.isLocalToReifee => freedef.asInstanceOf[ValDef] }
+ freedefsToInline foreach (vdef => this.freedefsToInline(vdef.name) = vdef)
+ val symbolTable1 = symbolTable diff freedefsToInline
+ val tree1 = Select(Block(mrDef :: symbolTable1, expr), flavor)
+ if (reifyDebug) println("trimmed %s inlineable free defs from its symbol table: %s".format(freedefsToInline.length, freedefsToInline map (_.name) mkString(", ")))
+ withinSplice { super.transform(tree1) }
+ case TreeSplice(splicee) =>
+ if (reifyDebug) println("entering splice: " + splicee)
+ val hasBreaches = splicee exists (_.symbol.metalevel > 0)
+ if (!insideSplice && hasBreaches) {
+ if (settings.logRuntimeSplices.value) reporter.echo(tree.pos, "this splice cannot be resolved statically")
+ if (reifyDebug) println("metalevel breach in %s: %s".format(tree, (splicee filter (_.symbol.metalevel > 0) map (_.symbol) distinct) mkString ", "))
+ }
+ withinSplice { super.transform(tree) }
+ // todo. also inline usages of ``freedefsToInline'' in the symbolTable itself
+ // e.g. a free$Foo can well use free$x, if Foo is path-dependent w.r.t x
+ // FreeRef(_, _) check won't work, because metalevels of symbol table and body are different, hence, freerefs in symbol table look different from freerefs in body
+ // todo. also perform garbage collection on local symbols
+ // so that local symbols used only in type signatures of free vars get removed
+ case FreeRef(mr, name) if freedefsToInline contains name =>
+ if (reifyDebug) println("inlineable free ref: %s in %s".format(name, showRaw(tree)))
+ val freedef @ FreeDef(_, _, binding, _) = freedefsToInline(name)
+ if (reifyDebug) println("related definition: %s".format(showRaw(freedef)))
+ val inlined = reify(binding)
+ if (reifyDebug) println("verdict: inlined as %s".format(showRaw(inlined)))
+ inlined
+ case _ =>
+ super.transform(tree)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala
new file mode 100644
index 0000000000..f6d6423605
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/phases/Reify.scala
@@ -0,0 +1,42 @@
+package scala.reflect.reify
+package phases
+
+import scala.runtime.ScalaRunTime.isAnyVal
+import scala.runtime.ScalaRunTime.isTuple
+import scala.reflect.reify.codegen._
+
+trait Reify extends Symbols
+ with Types
+ with Names
+ with Trees
+ with Positions
+ with Util {
+
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ /**
+ * Reifies any supported value.
+ * For internal use only, use ``reified'' instead.
+ */
+ def reify(reifee: Any): Tree = reifee match {
+ // before adding some case here, in global scope, please, consider
+ // whether it can be localized like reifyAnnotationInfo or reifyScope
+ // this will help reification stay as sane as possible
+ case sym: Symbol => reifySymRef(sym)
+ case tpe: Type => reifyType(tpe)
+ case name: Name => reifyName(name)
+ case tree: Tree => reifyTree(tree)
+ case pos: Position => reifyPosition(pos)
+ case mods: mirror.Modifiers => reifyModifiers(mods)
+ case xs: List[_] => reifyList(xs)
+ case s: String => Literal(Constant(s))
+ case v if isAnyVal(v) => Literal(Constant(v))
+ case null => Literal(Constant(null))
+ case _ =>
+ throw new Error("reifee %s of type %s is not supported".format(reifee, reifee.getClass))
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala
new file mode 100644
index 0000000000..e700604612
--- /dev/null
+++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala
@@ -0,0 +1,296 @@
+package scala.reflect.reify
+package phases
+
+import scala.tools.nsc.symtab.Flags._
+
+trait Reshape {
+ self: Reifier =>
+
+ import mirror._
+ import definitions._
+ import treeInfo._
+
+ /**
+ * Rolls back certain changes that were introduced during typechecking of the reifee.
+ *
+ * These include:
+ * * Replacing type trees with TypeTree(tpe)
+ * * Transforming Modifiers.annotations into Symbol.annotations
+ * * Transforming Annotated annotations into AnnotatedType annotations
+ * * Transforming Annotated(annot, expr) into Typed(expr, TypeTree(Annotated(annot, _))
+ * * Non-idempotencies of the typechecker: https://issues.scala-lang.org/browse/SI-5464
+ */
+ val reshape = new Transformer {
+ var currentSymbol: Symbol = NoSymbol
+
+ override def transform(tree: Tree) = {
+ currentSymbol = tree.symbol
+
+ val preTyper = tree match {
+ case tree if tree.isErroneous =>
+ tree
+ case tt @ TypeTree() =>
+ toPreTyperTypeTree(tt)
+ case toa @ TypedOrAnnotated(_) =>
+ toPreTyperTypedOrAnnotated(toa)
+ case ta @ TypeApply(hk, ts) =>
+ val discard = ts collect { case tt: TypeTree => tt } exists isDiscarded
+ if (reifyDebug && discard) println("discarding TypeApply: " + tree)
+ if (discard) hk else ta
+ case classDef @ ClassDef(mods, name, params, impl) =>
+ val Template(parents, self, body) = impl
+ var body1 = trimAccessors(classDef, body)
+ body1 = trimSyntheticCaseClassMembers(classDef, body1)
+ var impl1 = Template(parents, self, body1).copyAttrs(impl)
+ ClassDef(mods, name, params, impl1).copyAttrs(classDef)
+ case moduledef @ ModuleDef(mods, name, impl) =>
+ val Template(parents, self, body) = impl
+ var body1 = trimAccessors(moduledef, body)
+ body1 = trimSyntheticCaseClassMembers(moduledef, body1)
+ var impl1 = Template(parents, self, body1).copyAttrs(impl)
+ ModuleDef(mods, name, impl1).copyAttrs(moduledef)
+ case template @ Template(parents, self, body) =>
+ val discardedParents = parents collect { case tt: TypeTree => tt } filter isDiscarded
+ if (reifyDebug && discardedParents.length > 0) println("discarding parents in Template: " + discardedParents.mkString(", "))
+ val parents1 = parents diff discardedParents
+ val body1 = trimSyntheticCaseClassCompanions(body)
+ Template(parents1, self, body1).copyAttrs(template)
+ case block @ Block(stats, expr) =>
+ val stats1 = trimSyntheticCaseClassCompanions(stats)
+ Block(stats1, expr).copyAttrs(block)
+ case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isLazy =>
+ if (reifyDebug) println("dropping $lzy in lazy val's name: " + tree)
+ val name1 = if (name endsWith nme.LAZY_LOCAL) name dropRight nme.LAZY_LOCAL.length else name
+ ValDef(mods, name1, tpt, rhs).copyAttrs(valdef)
+ case unapply @ UnApply(fun, args) =>
+ def extractExtractor(tree: Tree): Tree = {
+ val Apply(fun, args) = tree
+ args match {
+ case List(Ident(special)) if special == nme.SELECTOR_DUMMY =>
+ val Select(extractor, flavor) = fun
+ assert(flavor == nme.unapply || flavor == nme.unapplySeq)
+ extractor
+ case _ =>
+ extractExtractor(fun)
+ }
+ }
+
+ if (reifyDebug) println("unapplying unapply: " + tree)
+ val fun1 = extractExtractor(fun)
+ Apply(fun1, args).copyAttrs(unapply)
+ case Literal(const @ Constant(tpe: Type)) =>
+ // todo. implement this
+ ???
+ case Literal(const @ Constant(sym: Symbol)) =>
+ // todo. implement this
+ ???
+ case _ =>
+ tree
+ }
+
+ super.transform(preTyper)
+ }
+
+ override def transformModifiers(mods: Modifiers) = {
+ val mods1 = toPreTyperModifiers(mods, currentSymbol)
+ super.transformModifiers(mods1)
+ }
+
+ private def toPreTyperModifiers(mods: Modifiers, sym: Symbol) = {
+ if (!sym.annotations.isEmpty) {
+ val Modifiers(flags, privateWithin, annotations) = mods
+ val postTyper = sym.annotations filter (_.original != EmptyTree)
+ if (reifyDebug && !postTyper.isEmpty) println("reify symbol annotations for: " + sym)
+ if (reifyDebug && !postTyper.isEmpty) println("originals are: " + sym.annotations)
+ val preTyper = postTyper map toPreTyperAnnotation
+ mods.withAnnotations(preTyper)
+ } else {
+ mods
+ }
+ }
+
+ /** Restore pre-typer representation of a type.
+ *
+ * NB: This is the trickiest part of reification!
+ *
+ * In most cases, we're perfectly fine to reify a Type itself (see ``reifyType'').
+ * However if the type involves a symbol declared inside the quasiquote (i.e. registered in ``boundSyms''),
+ * then we cannot reify it, or otherwise subsequent reflective compilation will fail.
+ *
+ * Why will it fail? Because reified deftrees (e.g. ClassDef(...)) will generate fresh symbols during that compilation,
+ * so naively reified symbols will become out of sync, which brings really funny compilation errors and/or crashes, e.g.:
+ * https://issues.scala-lang.org/browse/SI-5230
+ *
+ * To deal with this unpleasant fact, we need to fall back from types to equivalent trees (after all, parser trees don't contain any types, just trees, so it should be possible).
+ * Luckily, these original trees get preserved for us in the ``original'' field when Trees get transformed into TypeTrees.
+ * And if an original of a type tree is empty, we can safely assume that this type is non-essential (e.g. was inferred/generated by the compiler).
+ * In that case the type can be omitted (e.g. reified as an empty TypeTree), since it will be inferred again later on.
+ *
+ * An important property of the original is that it isn't just a pre-typer tree.
+ * It's actually kind of a post-typer tree with symbols assigned to its Idents (e.g. Ident("List") will contain a symbol that points to immutable.this.List).
+ * This is very important, since subsequent reflective compilation won't have to resolve these symbols.
+ * In general case, such resolution cannot be performed, since reification doesn't preserve lexical context,
+ * which means that reflective compilation won't be aware of, say, imports that were provided when the reifee has been compiled.
+ *
+ * This workaround worked surprisingly well and allowed me to fix several important reification bugs, until the abstraction has leaked.
+ * Suddenly I found out that in certain contexts original trees do not contain symbols, but are just parser trees.
+ * To the moment I know only one such situation: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless.
+ * Thus we apply a workaround for that in typedAnnotated. I hope this will be the only workaround in this department.
+ *
+ * upd. Recently I went ahead and started using original for all TypeTrees, regardless of whether they refer to local symbols or not.
+ * As a result, ``reifyType'' is never called directly by tree reification (and, wow, it seems to work great!).
+ * The only usage of ``reifyType'' now is for servicing typetags, however, I have some ideas how to get rid of that as well.
+ */
+ private def isDiscarded(tt: TypeTree) = tt.original == null
+ private def toPreTyperTypeTree(tt: TypeTree): Tree = {
+ if (tt.original != null) {
+ // here we rely on the fact that the originals that reach this point
+ // have all necessary symbols attached to them (i.e. that they can be recompiled in any lexical context)
+ // if this assumption fails, please, don't be quick to add postprocessing here (like I did before)
+ // but rather try to fix this in Typer, so that it produces quality originals (like it's done for typedAnnotated)
+ if (reifyDebug) println("TypeTree, essential: %s (%s)".format(tt.tpe, tt.tpe.kind))
+ if (reifyDebug) println("verdict: rolled back to original %s".format(tt.original))
+ transform(tt.original)
+ } else {
+ // type is deemed to be non-essential
+ // erase it and hope that subsequent reflective compilation will be able to recreate it again
+ if (reifyDebug) println("TypeTree, non-essential: %s (%s)".format(tt.tpe, tt.tpe.kind))
+ if (reifyDebug) println("verdict: discarded")
+ TypeTree()
+ }
+ }
+
+ private def toPreTyperTypedOrAnnotated(tree: Tree): Tree = tree match {
+ case ty @ Typed(expr1, tt @ TypeTree()) =>
+ if (reifyDebug) println("reify typed: " + tree)
+ val annotatedArg = {
+ def loop(tree: Tree): Tree = tree match {
+ case annotated1 @ Annotated(ann, annotated2 @ Annotated(_, _)) => loop(annotated2)
+ case annotated1 @ Annotated(ann, arg) => arg
+ case _ => EmptyTree
+ }
+
+ loop(tt.original)
+ }
+ if (annotatedArg != EmptyTree) {
+ if (annotatedArg.isType) {
+ if (reifyDebug) println("verdict: was an annotated type, reify as usual")
+ ty
+ } else {
+ if (reifyDebug) println("verdict: was an annotated value, equivalent is " + tt.original)
+ toPreTyperTypedOrAnnotated(tt.original)
+ }
+ } else {
+ if (reifyDebug) println("verdict: wasn't annotated, reify as usual")
+ ty
+ }
+ case at @ Annotated(annot, arg) =>
+ if (reifyDebug) println("reify type annotations for: " + tree)
+ assert(at.tpe.isInstanceOf[AnnotatedType], "%s (%s)".format(at.tpe, at.tpe.kind))
+ val annot1 = toPreTyperAnnotation(at.tpe.asInstanceOf[AnnotatedType].annotations(0))
+ if (reifyDebug) println("originals are: " + annot1)
+ Annotated(annot1, arg).copyAttrs(at)
+ }
+
+ /** Restore pre-typer representation of an annotation.
+ * The trick here is to retain the symbols that have been populated during typechecking of the annotation.
+ * If we do not do that, subsequent reflective compilation will fail.
+ */
+ private def toPreTyperAnnotation(ann: AnnotationInfo): Tree = {
+ val args = if (ann.assocs.isEmpty) {
+ ann.args
+ } else {
+ def toScalaAnnotation(jann: ClassfileAnnotArg): Tree = jann match {
+ case LiteralAnnotArg(const) =>
+ Literal(const)
+ case ArrayAnnotArg(arr) =>
+ Apply(Ident(definitions.ArrayModule), arr.toList map toScalaAnnotation)
+ case NestedAnnotArg(ann) =>
+ toPreTyperAnnotation(ann)
+ }
+
+ ann.assocs map { case (nme, arg) => AssignOrNamedArg(Ident(nme), toScalaAnnotation(arg)) }
+ }
+
+ def extractOriginal: PartialFunction[Tree, Tree] = { case Apply(Select(New(tpt), _), _) => tpt }
+ assert(extractOriginal.isDefinedAt(ann.original), showRaw(ann.original))
+ New(TypeTree(ann.atp) setOriginal extractOriginal(ann.original), List(args))
+ }
+
+ // [Eugene] is this implemented correctly?
+ private def trimAccessors(deff: Tree, stats: List[Tree]): List[Tree] = {
+ val symdefs = stats collect { case vodef: ValOrDefDef => vodef } map (vodeff => vodeff.symbol -> vodeff) toMap
+ val accessors = collection.mutable.Map[ValDef, List[DefDef]]()
+ stats collect { case ddef: DefDef => ddef } foreach (defdef => {
+ val valdef = symdefs get defdef.symbol.accessedOrSelf collect { case vdef: ValDef => vdef } getOrElse null
+ if (valdef != null) accessors(valdef) = accessors.getOrElse(valdef, Nil) :+ defdef
+
+ def detectBeanAccessors(prefix: String): Unit = {
+ if (defdef.name.startsWith(prefix)) {
+ var name = defdef.name.toString.substring(prefix.length)
+ def uncapitalize(s: String) = if (s.length == 0) "" else { val chars = s.toCharArray; chars(0) = chars(0).toLower; new String(chars) }
+ def findValDef(name: String) = symdefs.values collect { case vdef: ValDef if nme.dropLocalSuffix(vdef.name).toString == name => vdef } headOption;
+ val valdef = findValDef(name) orElse findValDef(uncapitalize(name)) orNull;
+ if (valdef != null) accessors(valdef) = accessors.getOrElse(valdef, Nil) :+ defdef
+ }
+ }
+ detectBeanAccessors("get")
+ detectBeanAccessors("set")
+ detectBeanAccessors("is")
+ });
+
+ var stats1 = stats flatMap {
+ case vdef @ ValDef(mods, name, tpt, rhs) =>
+ val mods1 = if (accessors.contains(vdef)) {
+ val ddef = accessors(vdef)(0) // any accessor will do
+ val Modifiers(flags, privateWithin, annotations) = mods
+ var flags1 = flags & ~LOCAL
+ if (!ddef.symbol.isPrivate) flags1 = flags1 & ~PRIVATE
+ val privateWithin1 = ddef.mods.privateWithin
+ val annotations1 = accessors(vdef).foldLeft(annotations)((curr, acc) => curr ++ (acc.symbol.annotations map toPreTyperAnnotation))
+ Modifiers(flags1, privateWithin1, annotations1) setPositions mods.positions
+ } else {
+ mods
+ }
+ val mods2 = toPreTyperModifiers(mods1, vdef.symbol)
+ val name1 = nme.dropLocalSuffix(name)
+ val vdef1 = ValDef(mods2, name1, tpt, rhs)
+ if (reifyDebug) println("resetting visibility of field: %s => %s".format(vdef, vdef1))
+ Some(vdef1) // no copyAttrs here, because new ValDef and old symbols are not out of sync
+ case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ if (accessors.values.exists(_.contains(ddef))) {
+ if (reifyDebug) println("discarding accessor method: " + ddef)
+ None
+ } else {
+ Some(ddef)
+ }
+ case tree =>
+ Some(tree)
+ }
+
+ stats1
+ }
+
+ private def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]): List[Tree] =
+ stats filterNot (memberDef => memberDef.isDef && {
+ val isSynthetic = memberDef.symbol.isSynthetic
+ // this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
+ // that's why I replace the check with an assumption that all synthetic members are, in fact, generated of case classes
+ // val isCaseMember = deff.symbol.isCaseClass || deff.symbol.companionClass.isCaseClass
+ val isCaseMember = true
+ if (isSynthetic && isCaseMember && reifyDebug) println("discarding case class synthetic def: " + memberDef)
+ isSynthetic && isCaseMember
+ })
+
+ private def trimSyntheticCaseClassCompanions(stats: List[Tree]): List[Tree] =
+ stats diff (stats collect { case moddef: ModuleDef => moddef } filter (moddef => {
+ val isSynthetic = moddef.symbol.isSynthetic
+ // this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
+ // that's why I replace the check with an assumption that all synthetic modules are, in fact, companions of case classes
+ // val isCaseCompanion = moddef.symbol.companionClass.isCaseClass
+ val isCaseCompanion = true
+ if (isSynthetic && isCaseCompanion && reifyDebug) println("discarding synthetic case class companion: " + moddef)
+ isSynthetic && isCaseCompanion
+ }))
+ }
+} \ No newline at end of file