From 6355d1a0b825c99560d4ccec1a8769f7421b1a71 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Wed, 6 Jun 2012 14:29:05 +0200 Subject: 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. --- .../scala/reflect/internal/StdAttachments.scala | 6 +- src/compiler/scala/reflect/internal/StdNames.scala | 20 +- src/compiler/scala/reflect/internal/TreeInfo.scala | 173 ------------ .../reflect/makro/runtime/ContextReifiers.scala | 26 ++ .../scala/reflect/makro/runtime/Reifiers.scala | 55 +++- src/compiler/scala/reflect/reify/Errors.scala | 19 +- .../scala/reflect/reify/NodePrinters.scala | 24 -- src/compiler/scala/reflect/reify/Phases.scala | 21 +- src/compiler/scala/reflect/reify/Reifier.scala | 112 ++++---- src/compiler/scala/reflect/reify/States.scala | 65 +++++ src/compiler/scala/reflect/reify/Taggers.scala | 67 +++-- .../reflect/reify/codegen/AnnotationInfos.scala | 56 ---- .../reflect/reify/codegen/GenAnnotationInfos.scala | 55 ++++ .../scala/reflect/reify/codegen/GenNames.scala | 14 + .../scala/reflect/reify/codegen/GenPositions.scala | 17 ++ .../scala/reflect/reify/codegen/GenSymbols.scala | 109 ++++++++ .../scala/reflect/reify/codegen/GenTrees.scala | 218 +++++++++++++++ .../scala/reflect/reify/codegen/GenTypes.scala | 203 ++++++++++++++ .../scala/reflect/reify/codegen/GenUtils.scala | 145 ++++++++++ .../scala/reflect/reify/codegen/Names.scala | 15 -- .../scala/reflect/reify/codegen/Positions.scala | 18 -- .../scala/reflect/reify/codegen/Symbols.scala | 184 ------------- .../scala/reflect/reify/codegen/Trees.scala | 255 ------------------ .../scala/reflect/reify/codegen/Types.scala | 168 ------------ .../scala/reflect/reify/codegen/Util.scala | 112 -------- src/compiler/scala/reflect/reify/package.scala | 81 +++--- .../scala/reflect/reify/phases/Calculate.scala | 17 +- .../scala/reflect/reify/phases/Metalevels.scala | 82 +++--- .../scala/reflect/reify/phases/Reify.scala | 31 +-- .../scala/reflect/reify/phases/Reshape.scala | 47 +++- .../scala/reflect/reify/utils/Extractors.scala | 294 +++++++++++++++++++++ .../scala/reflect/reify/utils/NodePrinters.scala | 144 ++++++++++ .../scala/reflect/reify/utils/StdAttachments.scala | 12 + .../scala/reflect/reify/utils/SymbolTables.scala | 223 ++++++++++++++++ src/compiler/scala/reflect/reify/utils/Utils.scala | 21 ++ src/compiler/scala/reflect/runtime/package.scala | 15 +- .../scala/tools/nsc/settings/ScalaSettings.scala | 1 - .../scala/tools/nsc/transform/Erasure.scala | 2 +- .../scala/tools/nsc/transform/UnCurry.scala | 2 +- .../scala/tools/nsc/typechecker/Implicits.scala | 6 +- .../scala/tools/nsc/typechecker/Macros.scala | 2 +- .../scala/tools/nsc/typechecker/Taggings.scala | 71 ----- .../scala/tools/nsc/typechecker/Tags.scala | 86 ++++++ .../scala/tools/nsc/typechecker/Typers.scala | 18 +- src/compiler/scala/tools/reflect/FastTrack.scala | 11 +- .../scala/tools/reflect/ToolBoxFactory.scala | 2 +- src/library/scala/reflect/api/StandardNames.scala | 15 +- src/library/scala/reflect/makro/Context.scala | 2 +- src/library/scala/reflect/makro/Reifiers.scala | 25 +- 49 files changed, 2052 insertions(+), 1315 deletions(-) create mode 100644 src/compiler/scala/reflect/makro/runtime/ContextReifiers.scala delete mode 100644 src/compiler/scala/reflect/reify/NodePrinters.scala create mode 100644 src/compiler/scala/reflect/reify/States.scala delete mode 100644 src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala create mode 100644 src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala create mode 100644 src/compiler/scala/reflect/reify/codegen/GenNames.scala create mode 100644 src/compiler/scala/reflect/reify/codegen/GenPositions.scala create mode 100644 src/compiler/scala/reflect/reify/codegen/GenSymbols.scala create mode 100644 src/compiler/scala/reflect/reify/codegen/GenTrees.scala create mode 100644 src/compiler/scala/reflect/reify/codegen/GenTypes.scala create mode 100644 src/compiler/scala/reflect/reify/codegen/GenUtils.scala delete mode 100644 src/compiler/scala/reflect/reify/codegen/Names.scala delete mode 100644 src/compiler/scala/reflect/reify/codegen/Positions.scala delete mode 100644 src/compiler/scala/reflect/reify/codegen/Symbols.scala delete mode 100644 src/compiler/scala/reflect/reify/codegen/Trees.scala delete mode 100644 src/compiler/scala/reflect/reify/codegen/Types.scala delete mode 100644 src/compiler/scala/reflect/reify/codegen/Util.scala create mode 100644 src/compiler/scala/reflect/reify/utils/Extractors.scala create mode 100644 src/compiler/scala/reflect/reify/utils/NodePrinters.scala create mode 100644 src/compiler/scala/reflect/reify/utils/StdAttachments.scala create mode 100644 src/compiler/scala/reflect/reify/utils/SymbolTables.scala create mode 100644 src/compiler/scala/reflect/reify/utils/Utils.scala delete mode 100644 src/compiler/scala/tools/nsc/typechecker/Taggings.scala create mode 100644 src/compiler/scala/tools/nsc/typechecker/Tags.scala diff --git a/src/compiler/scala/reflect/internal/StdAttachments.scala b/src/compiler/scala/reflect/internal/StdAttachments.scala index 33e3eb03b7..df1a1267de 100644 --- a/src/compiler/scala/reflect/internal/StdAttachments.scala +++ b/src/compiler/scala/reflect/internal/StdAttachments.scala @@ -6,7 +6,9 @@ import scala.reflect.makro.runtime.{Context => MacroContext} trait StdAttachments { self: SymbolTable => - case class ReifyAttachment(original: Symbol) - case object BackquotedIdentifierAttachment + + case class CompoundTypeTreeOriginalAttachment(parents: List[Tree], stats: List[Tree]) + + case class MacroExpansionAttachment(original: Tree) } \ No newline at end of file diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index 07fbe6f049..0e5f3efb49 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -213,6 +213,8 @@ trait StdNames { final val REFINE_CLASS_NAME: NameType = "" final val REPEATED_PARAM_CLASS_NAME: NameType = "" final val WILDCARD_STAR: NameType = "_*" + final val REIFY_TREECREATOR_PREFIX: NameType = "$treecreator" + final val REIFY_TYPECREATOR_PREFIX: NameType = "$typecreator" final val Any: NameType = "Any" final val AnyVal: NameType = "AnyVal" @@ -299,12 +301,17 @@ trait StdNames { val LAZY_LOCAL: NameType = "$lzy" val LAZY_SLOW_SUFFIX: NameType = "$lzycompute" val LOCAL_SUFFIX_STRING = " " - val MIRROR_FREE_PREFIX: NameType = "free$" - val MIRROR_FREE_THIS_SUFFIX: NameType = "$this" - val MIRROR_FREE_VALUE_SUFFIX: NameType = "$value" - val MIRROR_PREFIX: NameType = "$mr." - val MIRROR_SHORT: NameType = "$mr" - val MIRROR_SYMDEF_PREFIX: NameType = "symdef$" + val UNIVERSE_BUILD_PREFIX: NameType = "$u.build." + val UNIVERSE_BUILD: NameType = "$u.build" + val UNIVERSE_PREFIX: NameType = "$u." + val UNIVERSE_SHORT: NameType = "$u" + val MIRROR_PREFIX: NameType = "$m." + val MIRROR_SHORT: NameType = "$m" + val MIRROR_UNTYPED: NameType = "$m$untyped" + val REIFY_FREE_PREFIX: NameType = "free$" + val REIFY_FREE_THIS_SUFFIX: NameType = "$this" + val REIFY_FREE_VALUE_SUFFIX: NameType = "$value" + val REIFY_SYMDEF_PREFIX: NameType = "symdef$" val MIXIN_CONSTRUCTOR: NameType = "$init$" val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" val OUTER: NameType = "$outer" @@ -747,7 +754,6 @@ trait StdNames { val tail: NameType = "tail" val `then` : NameType = "then" val this_ : NameType = "this" - val thisModuleType : NameType = "thisModuleType" val thisPrefix : NameType = "thisPrefix" val throw_ : NameType = "throw" val toArray: NameType = "toArray" diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index fc6e31ce1b..4b2105876d 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -568,177 +568,4 @@ abstract class TreeInfo { object DynamicUpdate extends DynamicApplicationExtractor(_ == nme.updateDynamic) object DynamicApplication extends DynamicApplicationExtractor(isApplyDynamicName) object DynamicApplicationNamed extends DynamicApplicationExtractor(_ == nme.applyDynamicNamed) - - - // domain-specific extractors for reification - - import definitions._ - - object TypedOrAnnotated { - def unapply(tree: Tree): Option[Tree] = tree match { - case ty @ Typed(_, _) => - Some(ty) - case at @ Annotated(_, _) => - Some(at) - case _ => - None - } - } - - object TreeSplice { - def unapply(tree: Tree): Option[Tree] = tree match { - case Select(splicee, _) if tree.symbol == ExprSplice => - Some(splicee) - case _ => - None - } - } - - object Reified { - def unapply(tree: Tree): Option[(Tree, List[Tree], Tree)] = tree match { - case ReifiedTree(reifee, symbolTable, reified, _) => - Some(reifee, symbolTable, reified) - case ReifiedType(reifee, symbolTable, reified) => - Some(reifee, symbolTable, reified) - case _ => - None - } - } - - object ReifiedTree { - def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree)] = tree match { - case reifee @ Block((mrDef @ ValDef(_, _, _, _)) :: symbolTable, Apply(Apply(_, List(tree)), List(Apply(_, tpe :: _)))) if mrDef.name == nme.MIRROR_SHORT => - Some(reifee, symbolTable, tree, tpe) - case _ => - None - } - } - - object InlineableTreeSplice { - def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree, Symbol)] = tree match { - case select @ Select(ReifiedTree(splicee, symbolTable, tree, tpe), _) if select.symbol == ExprSplice => - Some(splicee, symbolTable, tree, tpe, select.symbol) - case _ => - None - } - } - - object InlinedTreeSplice { - def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree)] = tree match { - case Select(ReifiedTree(splicee, symbolTable, tree, tpe), name) if name == nme.tree => - Some(splicee, symbolTable, tree, tpe) - case _ => - None - } - } - - object ReifiedType { - def unapply(tree: Tree): Option[(Tree, List[Tree], Tree)] = tree match { - case reifee @ Block((mrDef @ ValDef(_, _, _, _)) :: symbolTable, Apply(_, tpe :: _)) if mrDef.name == nme.MIRROR_SHORT => - Some(reifee, symbolTable, tpe) - case _ => - None - } - } - - object InlinedTypeSplice { - def unapply(tree: Tree): Option[(Tree, List[Tree], Tree)] = tree match { - case Select(ReifiedType(splicee, symbolTable, tpe), name) if name == nme.tpe => - Some(splicee, symbolTable, tpe) - case _ => - None - } - } - - object FreeDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case FreeTermDef(mrRef, name, binding, flags, origin) => - Some(mrRef, name, binding, flags, origin) - case FreeTypeDef(mrRef, name, binding, flags, origin) => - Some(mrRef, name, binding, flags, origin) - case _ => - None - } - } - - object FreeTermDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case ValDef(_, name, _, Apply(Select(mrRef @ Ident(_), newFreeTerm), List(_, _, binding, Literal(Constant(flags: Long)), Literal(Constant(origin: String))))) - if mrRef.name == nme.MIRROR_SHORT && newFreeTerm == nme.newFreeTerm => - Some(mrRef, name, binding, flags, origin) - case _ => - None - } - } - - object FreeTypeDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case ValDef(_, name, _, Apply(Select(mrRef1 @ Ident(_), newFreeType), List(_, _, value, Literal(Constant(flags: Long)), Literal(Constant(origin: String))))) - if mrRef1.name == nme.MIRROR_SHORT && (newFreeType == nme.newFreeType || newFreeType == nme.newFreeExistential) => - value match { - case Apply(TypeApply(Select(Select(mrRef2 @ Ident(_), typeTag), apply), List(binding)), List(Literal(Constant(null)), _)) - if mrRef2.name == nme.MIRROR_SHORT && typeTag == nme.TypeTag && apply == nme.apply => - Some(mrRef1, name, binding, flags, origin) - case Apply(TypeApply(Select(mrRef2 @ Ident(_), typeTag), List(binding)), List(Literal(Constant(null)), _)) - if mrRef2.name == nme.MIRROR_SHORT && typeTag == nme.TypeTag => - Some(mrRef1, name, binding, flags, origin) - case _ => - throw new Error("unsupported free type def: %s%n%s".format(value, showRaw(value))) - } - case _ => - None - } - } - - object FreeRef { - def unapply(tree: Tree): Option[(Tree, TermName)] = tree match { - case Apply(Select(mrRef @ Ident(_), ident), List(Ident(name: TermName))) if ident == nme.Ident && name.startsWith(nme.MIRROR_FREE_PREFIX) => - Some(mrRef, name) - case _ => - None - } - } - - object TypeRefToFreeType { - def unapply(tree: Tree): Option[TermName] = tree match { - case Apply(Select(Select(mrRef @ Ident(_), typeRef), apply), List(Select(_, noSymbol), Ident(freeType: TermName), nil)) - if (mrRef.name == nme.MIRROR_SHORT && typeRef == nme.TypeRef && noSymbol == nme.NoSymbol && freeType.startsWith(nme.MIRROR_FREE_PREFIX)) => - Some(freeType) - case _ => - None - } - } - - object NestedExpr { - def unapply(tree: Tree): Option[(Tree, Tree, Tree)] = tree match { - case Apply(Apply(factory @ Select(expr, apply), List(tree)), List(typetag)) if expr.symbol == ExprModule && apply == nme.apply => - Some(factory, tree, typetag) - case _ => - None - } - } - - object BoundTerm { - def unapply(tree: Tree): Option[Tree] = tree match { - case Ident(name) if name.isTermName => - Some(tree) - case This(_) => - Some(tree) - case _ => - None - } - } - - object BoundType { - def unapply(tree: Tree): Option[Tree] = tree match { - case Select(_, name) if name.isTypeName => - Some(tree) - case SelectFromTypeTree(_, name) if name.isTypeName => - Some(tree) - case Ident(name) if name.isTypeName => - Some(tree) - case _ => - None - } - } } diff --git a/src/compiler/scala/reflect/makro/runtime/ContextReifiers.scala b/src/compiler/scala/reflect/makro/runtime/ContextReifiers.scala new file mode 100644 index 0000000000..564148fe6c --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/ContextReifiers.scala @@ -0,0 +1,26 @@ +package scala.reflect.makro +package runtime + +abstract class ContextReifiers { self => + val c: Context + + import c.universe._ + import definitions._ + import treeBuild._ + + import scala.reflect.reify.Taggers + import language.implicitConversions + private implicit def context2taggers(c0: Context) : Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers + + private def forMacroContext[T](prefix: Tree)(op: (Tree, Tree) => T): T = { + val universe = gen.mkAttributedSelect(prefix.duplicate, MacroContextUniverse) setType SingleType(prefix.tpe, MacroContextUniverse) + val mirror = TypeApply(Select(Select(prefix.duplicate, nme.mirror), nme.asInstanceOf_), List(Select(Ident(nme.UNIVERSE_SHORT), tpnme.Mirror))) + op(universe, mirror) + } + + def materializeExprForMacroContext(prefix: Tree, expr: Tree): Tree = + forMacroContext(prefix)((universe, mirror) => c.materializeExpr(universe, mirror, expr)) + + def materializeTypeTagForMacroContext(prefix: Tree, tpe: Type, concrete: Boolean): Tree = + forMacroContext(prefix)((universe, mirror) => c.materializeTypeTag(universe, mirror, tpe, concrete)) +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Reifiers.scala b/src/compiler/scala/reflect/makro/runtime/Reifiers.scala index 7d404128fb..c578fceb2c 100644 --- a/src/compiler/scala/reflect/makro/runtime/Reifiers.scala +++ b/src/compiler/scala/reflect/makro/runtime/Reifiers.scala @@ -9,20 +9,59 @@ package runtime trait Reifiers { self: Context => + val global: universe.type = universe import universe._ import definitions._ - lazy val reflectMirrorPrefix: Tree = ??? + lazy val basisUniverse: Tree = gen.mkBasisUniverseRef - def reifyTree(prefix: Tree, tree: Tree): Tree = - scala.reflect.reify.`package`.reifyTree(universe)(callsiteTyper, prefix, tree) + lazy val runtimeUniverse: Tree = gen.mkRuntimeUniverseRef - def reifyType(prefix: Tree, tpe: Type, dontSpliceAtTopLevel: Boolean = false, concrete: Boolean = false): Tree = - scala.reflect.reify.`package`.reifyType(universe)(callsiteTyper, prefix, tpe, dontSpliceAtTopLevel, concrete) + def reifyTree(universe: Tree, mirror: Tree, tree: Tree): Tree = { + val result = scala.reflect.reify.`package`.reifyTree(self.universe)(callsiteTyper, universe, mirror, tree) + logFreeVars(enclosingPosition, result) + result + } + + def reifyType(universe: Tree, mirror: Tree, tpe: Type, concrete: Boolean = false): Tree = { + val result = scala.reflect.reify.`package`.reifyType(self.universe)(callsiteTyper, universe, mirror, tpe, concrete) + logFreeVars(enclosingPosition, result) + result + } def reifyRuntimeClass(tpe: Type, concrete: Boolean = true): Tree = - scala.reflect.reify.`package`.reifyRuntimeClass(universe)(callsiteTyper, tpe, concrete) + scala.reflect.reify.`package`.reifyRuntimeClass(universe)(callsiteTyper, tpe, concrete = concrete) + + def reifyEnclosingRuntimeClass: Tree = + scala.reflect.reify.`package`.reifyEnclosingRuntimeClass(universe)(callsiteTyper) + + def unreifyTree(tree: Tree): Tree = { + assert(ExprSplice != NoSymbol) + Select(tree, ExprSplice) + } + + object utils extends { + val global: self.global.type = self.global + val typer: global.analyzer.Typer = self.callsiteTyper + } with scala.reflect.reify.utils.Utils + import utils._ + + private def logFreeVars(position: Position, reification: Tree): Unit = { + def logFreeVars(symtab: SymbolTable): Unit = + // logging free vars only when they are untyped prevents avalanches of duplicate messages + symtab.syms map (sym => symtab.symDef(sym)) foreach { + case FreeTermDef(_, _, binding, _, origin) if universe.settings.logFreeTerms.value && binding.tpe == null => + reporter.echo(position, "free term: %s %s".format(showRaw(binding), origin)) + case FreeTypeDef(_, _, binding, _, origin) if universe.settings.logFreeTypes.value && binding.tpe == null => + reporter.echo(position, "free type: %s %s".format(showRaw(binding), origin)) + case _ => + // do nothing + } - def unreifyTree(tree: Tree): Tree = - Select(tree, definitions.ExprSplice) + if (universe.settings.logFreeTerms.value || universe.settings.logFreeTypes.value) + reification match { + case ReifiedTree(_, _, symtab, _, _, _, _) => logFreeVars(symtab) + case ReifiedType(_, _, symtab, _, _, _) => logFreeVars(symtab) + } + } } diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 6edadf3a47..824f237948 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -7,7 +7,7 @@ import scala.reflect.makro.UnexpectedReificationError trait Errors { self: Reifier => - import mirror._ + import global._ import definitions._ def defaultErrorPosition = { @@ -38,6 +38,23 @@ trait Errors { throw new ReificationError(defaultErrorPosition, msg) } + def CannotConvertManifestToTagWithoutScalaReflect(tpe: Type, manifestInScope: Tree) = { + val msg = s""" + |to create a type tag here, it is necessary to interoperate with the manifest `$manifestInScope` in scope. + |however manifest -> typetag conversion requires Scala reflection, which is not present on the classpath. + |to proceed put scala-reflect.jar on your compilation classpath and recompile.""".trim.stripMargin + throw new ReificationError(defaultErrorPosition, msg) + } + + def CannotReifyRuntimeSplice(tree: Tree) = { + val msg = """ + |the splice cannot be resolved statically, which means there is a cross-stage evaluation involved. + |cross-stage evaluations need to be invoked explicitly, so we're showing you this error. + |if you're sure this is not an oversight, add scala-compiler.jar to the classpath, + |import `scala.tools.reflect.Eval` and call `.eval` instead.""".trim.stripMargin + throw new ReificationError(tree.pos, 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 diff --git a/src/compiler/scala/reflect/reify/NodePrinters.scala b/src/compiler/scala/reflect/reify/NodePrinters.scala deleted file mode 100644 index f0d0d0f5d4..0000000000 --- a/src/compiler/scala/reflect/reify/NodePrinters.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* 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 = { - "temporarily disabled until reification is repaired" - } - } -} \ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/Phases.scala b/src/compiler/scala/reflect/reify/Phases.scala index 49d5a45e8e..54336ee708 100644 --- a/src/compiler/scala/reflect/reify/Phases.scala +++ b/src/compiler/scala/reflect/reify/Phases.scala @@ -1,16 +1,16 @@ package scala.reflect package reify -import scala.reflect.reify.phases._ +import phases._ -trait Phases extends Calculate - with Reshape +trait Phases extends Reshape + with Calculate with Metalevels with Reify { self: Reifier => - import mirror._ + import global._ import definitions._ private var alreadyRun = false @@ -26,16 +26,19 @@ trait Phases extends Calculate if (reifyDebug) println("[reshape phase]") tree = reshape.transform(tree) + if (reifyDebug) println("[interlude]") + if (reifyDebug) println("reifee = " + (if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)) + + if (reifyDebug) println("[calculate phase]") + calculate.traverse(tree) if (reifyDebug) println("[metalevels phase]") tree = metalevels.transform(tree) - if (reifyDebug) println("[interlude]") - if (reifyDebug) println("symbol table = " + (if (symbolTable.length == 0) "" else "")) - if (reifyDebug) symbolTable foreach (println(_)) - if (reifyDebug) println("reifee = " + (if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)) + if (reifyDebug) println(symtab.debugString) + if (reifyDebug) println("[reify phase]") - var result = reify(tree) + val result = reify(tree) result } diff --git a/src/compiler/scala/reflect/reify/Reifier.scala b/src/compiler/scala/reflect/reify/Reifier.scala index 41b45741ba..a5b2fef18c 100644 --- a/src/compiler/scala/reflect/reify/Reifier.scala +++ b/src/compiler/scala/reflect/reify/Reifier.scala @@ -4,6 +4,7 @@ package reify import scala.tools.nsc.Global import scala.reflect.makro.ReificationError import scala.reflect.makro.UnexpectedReificationError +import scala.reflect.reify.utils.Utils /** Given a tree or a type, generate a tree that when executed at runtime produces the original tree or type. * See more info in the comments to ``reify'' in scala.reflect.api.Universe. @@ -11,51 +12,59 @@ import scala.reflect.makro.UnexpectedReificationError * @author Martin Odersky * @version 2.10 */ -abstract class Reifier extends Phases - with Errors { +abstract class Reifier extends States + with Phases + with Errors + with Utils { - val mirror: Global - import mirror._ + val global: Global + import global._ import definitions._ - import treeInfo._ - val typer: mirror.analyzer.Typer - val prefix: Tree + val typer: global.analyzer.Typer + val universe: Tree + val mirror: Tree val reifee: Any - val dontSpliceAtTopLevel: Boolean val concrete: Boolean + // needed to seamlessly integrate with standalone utils + override def getReifier: Reifier { val global: Reifier.this.global.type } = + this.asInstanceOf[Reifier { val global: Reifier.this.global.type }] + override def hasReifier = true + /** * For ``reifee'' and other reification parameters, generate a tree of the form * * { - * val $mr = <[ prefix ]> - * $mr.Expr[T](rtree) // if data is a Tree - * $mr.TypeTag[T](rtree) // if data is a Type + * val $u: universe.type = <[ universe ]> + * val $m: $u.Mirror = <[ mirror ]> + * $u.Expr[T](rtree) // if data is a Tree + * $u.TypeTag[T](rtree) // if data is a Type * } * * where * - * - `prefix` is the tree that represents the universe - * the result will be bound to + * - `universe` is the tree that represents the universe the result will be bound to + * - `mirror` is the tree that represents the mirror the result will be initially bound to * - `rtree` is code that generates `reifee` at runtime. * - `T` is the type that corresponds to `data`. * * This is not a method, but a value to indicate the fact that Reifier instances are a one-off. */ - lazy val reified: Tree = { + lazy val reification: Tree = { try { // [Eugene] conventional way of doing this? - if (prefix exists (_.isErroneous)) CannotReifyErroneousPrefix(prefix) - if (prefix.tpe == null) CannotReifyUntypedPrefix(prefix) + if (universe exists (_.isErroneous)) CannotReifyErroneousPrefix(universe) + if (universe.tpe == null) CannotReifyUntypedPrefix(universe) - val rtree = reifee match { + val result = 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) + reifyTrace("universe = ")(universe) + reifyTrace("mirror = ")(mirror) // [Eugene] conventional way of doing this? - if (tree exists (_.isErroneous)) CannotReifyErroneousReifee(prefix) + if (tree exists (_.isErroneous)) CannotReifyErroneousReifee(tree) if (tree.tpe == null) CannotReifyUntypedReifee(tree) val pipeline = mkReificationPipeline val rtree = pipeline(tree) @@ -74,31 +83,23 @@ abstract class Reifier extends Phases if (tree.tpe exists (sub => sub.typeSymbol.isLocalToReifee)) CannotReifyReifeeThatHasTypeLocalToReifee(tree) - val taggedType = typer.packedType(tree, NoSymbol) - val tagModule = if (reificationIsConcrete) ConcreteTypeTagModule else TypeTagModule - val tagCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(taggedType))) - val exprCtor = TypeApply(Select(Ident(nme.MIRROR_SHORT), ExprModule.name), List(TypeTree(taggedType))) - val tagArgs = List(reify(taggedType), reifyRuntimeClass(mirror)(typer, taggedType, concrete = false)) - Apply(Apply(exprCtor, List(rtree)), List(Apply(tagCtor, tagArgs))) + val tpe = typer.packedType(tree, NoSymbol) + val ReifiedType(_, _, tpeSymtab, _, rtpe, tpeReificationIsConcrete) = `package`.reifyType(global)(typer, universe, mirror, tpe, concrete = false) + state.reificationIsConcrete &= tpeReificationIsConcrete + state.symtab ++= tpeSymtab + ReifiedTree(universe, mirror, symtab, rtree, tpe, rtpe, reificationIsConcrete) case tpe: Type => reifyTrace("reifying = ")(tpe.toString) - reifyTrace("prefix = ")(prefix) + reifyTrace("universe = ")(universe) + reifyTrace("mirror = ")(mirror) val rtree = reify(tpe) - - val taggedType = tpe - val tagModule = if (reificationIsConcrete) ConcreteTypeTagModule else TypeTagModule - val ctor = TypeApply(Select(Ident(nme.MIRROR_SHORT), tagModule.name), List(TypeTree(taggedType))) - val args = List(rtree, reifyRuntimeClass(mirror)(typer, taggedType, concrete = false)) - Apply(ctor, args) + ReifiedType(universe, mirror, symtab, tpe, rtree, reificationIsConcrete) case _ => throw new Error("reifee %s of type %s is not supported".format(reifee, if (reifee == null) "null" else reifee.getClass.toString)) } - 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 @@ -107,14 +108,14 @@ abstract class Reifier extends Phases // // ===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. + // and then the reference to $u becomes screwed up, because nested symbol tables are already typechecked, + // so we have an $u symbol that points to the nested $u rather than to the top-level one. // // ===example 2=== - // we inline a freevar by replacing a reference to it, e.g. $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 - + // we inline a freevar by replacing a reference to it, e.g. $u.Apply($u.Select($u.Ident($u.newTermName("$u")), $u.newTermName("Ident")), List($u.Ident($u.newTermName("free$x")))) + // with its original binding (e.g. $u.Ident("x")) + // we'd love to typecheck the result, but we cannot do this easily, because $u is external to this tree + // what's even worse, sometimes $u can point to the top-level symbol table's $u, which doesn't have any symbol/type yet - // it's just a ValDef that will be emitted only after the reification is completed // // hence, the simplest solution is to erase all attrs so that invalid (as well as non-existent) bindings get rebound correctly @@ -124,31 +125,30 @@ abstract class Reifier extends Phases // 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 entry @ FreeDef(_, _, binding, _, _) => binding.symbol } toSet - // [Eugene] yeah, ugly and extremely brittle, but we do need to do resetAttrs. will be fixed later - var importantSymbols = Set[Symbol](PredefModule, ScalaRunTimeModule) + // todo. to be updated + // [Eugene++] yeah, ugly and extremely brittle, but we do need to do resetAttrs. will be fixed later + // todo. maybe try `resetLocalAttrs` once the dust settles + var importantSymbols = Set[Symbol]( + NothingClass, AnyClass, SingletonClass, PredefModule, ScalaRunTimeModule, TypeCreatorClass, TreeCreatorClass, MirrorOfClass, + BaseUniverseClass, ApiUniverseClass, JavaUniverseClass, ReflectRuntimePackage, ReflectRuntimeCurrentMirror) importantSymbols ++= importantSymbols map (_.companionSymbol) importantSymbols ++= importantSymbols map (_.moduleClass) importantSymbols ++= importantSymbols map (_.linkedClassOfClass) - def importantSymbol(sym: Symbol): Boolean = sym != null && sym != NoSymbol && importantSymbols(sym) - val untyped = resetAllAttrs(wrapped, leaveAlone = { - case ValDef(_, mr, _, _) if mr == nme.MIRROR_SHORT => true - case tree if freevarBindings contains tree.symbol => true - case tree if importantSymbol(tree.symbol) => true + def isImportantSymbol(sym: Symbol): Boolean = sym != null && sym != NoSymbol && importantSymbols(sym) + val untyped = resetAllAttrs(result, leaveAlone = { + case ValDef(_, u, _, _) if u == nme.UNIVERSE_SHORT => true + case ValDef(_, m, _, _) if m == nme.MIRROR_SHORT => true + case tree if symtab.syms contains tree.symbol => true + case tree if isImportantSymbol(tree.symbol) => true case _ => false }) if (reifyCopypaste) { if (reifyDebug) println("=============================") - println(reifiedNodeToString(prefix, untyped)) + println(reifiedNodeToString(untyped)) if (reifyDebug) println("=============================") } else { - reifyTrace("reified = ")(untyped) + reifyTrace("reification = ")(untyped) } untyped diff --git a/src/compiler/scala/reflect/reify/States.scala b/src/compiler/scala/reflect/reify/States.scala new file mode 100644 index 0000000000..97b703d32b --- /dev/null +++ b/src/compiler/scala/reflect/reify/States.scala @@ -0,0 +1,65 @@ +package scala.reflect.reify + +trait States { + self: Reifier => + + import global._ + import definitions._ + + /** Encapsulates reifier state + * + * When untangling reifier symbol tables from the reifier itself, + * I discovered that encoding of a symbol table (e.g. producing corresponding reificode) + * might cause subsequent reification (e.g. when filling in signatures and annotations for syms). + * + * This is a mess in the face of nested reifications, splices and inlining of thereof, + * so I made `SymbolTable` immutable, which brought a significant amount of sanity. + * + * However that wasn't enough. Sure, symbol table became immutable, but the reifier still needed + * to mutate its `symtab` field during reification. This caused nasty desyncs between the table being encoded + * and the table of the underlying reifier, so I decided to encapsulate the entire state here, + * so that encoding can backup the state before it starts and restore it after it completes. + */ + val state = new State + + // todo. rewrite the reifier so that we don't need mutable state anymore + // to aid you with that I've already removed all the setters from the reifier + // so all the places that involve mutations are forced to do that by explicitly mentioning `state` + class State { + var symtab = SymbolTable() + var reifyTreeSymbols = false + var reifyTreeTypes = false + private var _reificationIsConcrete = true + def reificationIsConcrete: Boolean = _reificationIsConcrete + def reificationIsConcrete_=(value: Boolean): Unit = { + _reificationIsConcrete = value + if (!value && concrete) { + assert(current.isInstanceOf[Type], current) + val offender = current.asInstanceOf[Type] + CannotReifyConcreteTypeTagHavingUnresolvedTypeParameters(offender) + } + } + var reifyStack = reifee :: Nil + var localSymbols = Map[Symbol, Int]() + + def backup: State = { + val backup = new State + backup.symtab = this.symtab + backup.reifyTreeSymbols = this.reifyTreeSymbols + backup.reifyTreeTypes = this.reifyTreeTypes + backup._reificationIsConcrete = this._reificationIsConcrete + backup.reifyStack = this.reifyStack + backup.localSymbols = this.localSymbols + backup + } + + def restore(backup: State): Unit = { + this.symtab = backup.symtab + this.reifyTreeSymbols = backup.reifyTreeSymbols + this.reifyTreeTypes = backup.reifyTreeTypes + this._reificationIsConcrete = backup._reificationIsConcrete + this.reifyStack = backup.reifyStack + this.localSymbols = backup.localSymbols + } + } +} diff --git a/src/compiler/scala/reflect/reify/Taggers.scala b/src/compiler/scala/reflect/reify/Taggers.scala index d8da7e74ff..cf0a749e41 100644 --- a/src/compiler/scala/reflect/reify/Taggers.scala +++ b/src/compiler/scala/reflect/reify/Taggers.scala @@ -8,38 +8,57 @@ abstract class Taggers { import c.universe._ import definitions._ + import treeBuild._ val coreTags = Map( - ByteClass.asType -> newTermName("Byte"), - ShortClass.asType -> newTermName("Short"), - CharClass.asType -> newTermName("Char"), - IntClass.asType -> newTermName("Int"), - LongClass.asType -> newTermName("Long"), - FloatClass.asType -> newTermName("Float"), - DoubleClass.asType -> newTermName("Double"), - BooleanClass.asType -> newTermName("Boolean"), - UnitClass.asType -> newTermName("Unit"), - AnyClass.asType -> newTermName("Any"), - ObjectClass.asType -> newTermName("Object"), - NothingClass.asType -> newTermName("Nothing"), - NullClass.asType -> newTermName("Null"), - StringClass.asType -> newTermName("String")) + ByteClass.asType -> nme.Byte, + ShortClass.asType -> nme.Short, + CharClass.asType -> nme.Char, + IntClass.asType -> nme.Int, + LongClass.asType -> nme.Long, + FloatClass.asType -> nme.Float, + DoubleClass.asType -> nme.Double, + BooleanClass.asType -> nme.Boolean, + UnitClass.asType -> nme.Unit, + AnyClass.asType -> nme.Any, + ObjectClass.asType -> nme.Object, + NothingClass.asType -> nme.Nothing, + NullClass.asType -> nme.Null, + StringClass.asType -> nme.String) // todo. the following two methods won't be necessary once we implement implicit macro generators for tags def materializeArrayTag(prefix: Tree, tpe: Type): Tree = materializeClassTag(prefix, tpe) - def materializeClassTag(prefix: Tree, tpe: Type): Tree = - materializeTag(prefix, tpe, ClassTagModule, { - val runtimeClass = c.reifyRuntimeClass(tpe, concrete = true) - val factory = TypeApply(Select(Ident(ClassTagModule), "apply"), List(TypeTree(tpe))) - Apply(factory, List(runtimeClass)) + def materializeClassTag(prefix: Tree, tpe: Type): Tree = { + val tagModule = ClassTagModule + materializeTag(prefix, tpe, tagModule, { + val erasure = c.reifyRuntimeClass(tpe, concrete = true) + val factory = TypeApply(Select(Ident(tagModule), nme.apply), List(TypeTree(tpe))) + Apply(factory, List(erasure)) }) + } - def materializeTypeTag(prefix: Tree, tpe: Type, concrete: Boolean): Tree = { - val tagModule = if (concrete) ConcreteTypeTagModule else TypeTagModule - materializeTag(prefix, tpe, tagModule, c.reifyType(prefix, tpe, dontSpliceAtTopLevel = true, concrete = concrete)) + def materializeTypeTag(universe: Tree, mirror: Tree, tpe: Type, concrete: Boolean): Tree = { + if (universe.symbol == MacroContextUniverse && mirror == EmptyTree) { + import scala.reflect.makro.runtime.ContextReifiers + import language.implicitConversions + implicit def context2contextreifiers(c0: Context) : ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers + val Select(prefix, _) = universe + c.materializeTypeTagForMacroContext(prefix, tpe, concrete) + } else { + val tagType = if (concrete) ConcreteTypeTagClass else TypeTagClass + val unaffiliatedTagTpe = TypeRef(BaseUniverseClass.asTypeConstructor, tagType, List(tpe)) + val unaffiliatedTag = c.inferImplicitValue(unaffiliatedTagTpe, silent = true, withMacrosDisabled = true) + unaffiliatedTag match { + case success if !success.isEmpty => + Apply(Select(success, nme.in), List(mirror orElse mkDefaultMirrorRef(c.universe)(universe, c.callsiteTyper))) + case _ => + val tagModule = if (concrete) ConcreteTypeTagModule else TypeTagModule + materializeTag(universe, tpe, tagModule, c.reifyType(universe, mirror, tpe, concrete = concrete)) + } + } } private def materializeTag(prefix: Tree, tpe: Type, tagModule: Symbol, materializer: => Tree): Tree = { @@ -55,8 +74,8 @@ abstract class Taggers { catch { case terr @ c.TypeError(pos, msg) => failTag(result, terr) } } - def materializeExpr(prefix: Tree, expr: Tree): Tree = { - val result = translatingReificationErrors(c.reifyTree(prefix, expr)) + def materializeExpr(universe: Tree, mirror: Tree, expr: Tree): Tree = { + val result = translatingReificationErrors(c.reifyTree(universe, mirror, expr)) try c.typeCheck(result) catch { case terr @ c.TypeError(pos, msg) => failExpr(result, terr) } } diff --git a/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala b/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala deleted file mode 100644 index 1d218317dc..0000000000 --- a/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala +++ /dev/null @@ -1,56 +0,0 @@ -package scala.reflect.reify -package codegen - -trait AnnotationInfos { - self: Reifier => - - import mirror._ - import definitions._ - import treeInfo._ - - // usually annotations are reified as their originals from Modifiers - // however, when reifying free and tough types, we're forced to reify annotation infos as is - // why is that bad? take a look inside - 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)) - } -} \ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala b/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala new file mode 100644 index 0000000000..5f4296f54f --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala @@ -0,0 +1,55 @@ +package scala.reflect.reify +package codegen + +trait GenAnnotationInfos { + self: Reifier => + + import global._ + import definitions._ + + // usually annotations are reified as their originals from Modifiers + // however, when reifying free and tough types, we're forced to reify annotation infos as is + // why is that bad? take a look inside + 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. + state.reifyTreeSymbols = true + state.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 { + state.reifyTreeSymbols = saved1 + state.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)) + } +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/codegen/GenNames.scala b/src/compiler/scala/reflect/reify/codegen/GenNames.scala new file mode 100644 index 0000000000..4abf88f475 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/GenNames.scala @@ -0,0 +1,14 @@ +package scala.reflect.reify +package codegen + +trait GenNames { + self: Reifier => + + import global._ + import definitions._ + + 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/GenPositions.scala b/src/compiler/scala/reflect/reify/codegen/GenPositions.scala new file mode 100644 index 0000000000..8c5db04454 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/GenPositions.scala @@ -0,0 +1,17 @@ +package scala.reflect.reify +package codegen + +trait GenPositions { + self: Reifier => + + import global._ + import definitions._ + + // 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/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala new file mode 100644 index 0000000000..3a98d308a7 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -0,0 +1,109 @@ +package scala.reflect.reify +package codegen + +trait GenSymbols { + self: Reifier => + + import global._ + import definitions._ + + /** Symbol table of the reifee. + * + * Keeps track of auxiliary symbols that are necessary for this reification session. + * These include: + * 1) Free vars (terms, types and existentials), + * 2) Non-locatable symbols (sometimes, e.g. for RefinedTypes, we need to reify these; to do that we create their local copies in the reificode) + * 3) Non-locatable symbols that are referred by #1, #2 and #3 + * + * Exposes three main methods: + * 1) `syms` that lists symbols belonging to the table, + * 2) `symXXX` family of methods that provide information about the symbols in the table, + * 3) `encode` that renders the table into a list of trees (recursively populating #3 and setting up initialization code for #1, #2 and #3) + */ + def symtab: SymbolTable = state.symtab + + /** 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.isRootPackage) + mirrorMirrorSelect(nme.RootPackage) + else if (sym.isRoot) + mirrorMirrorSelect(nme.RootClass) + else if (sym.isEmptyPackage) + mirrorMirrorSelect(nme.EmptyPackage) + else if (sym.isEmptyPackageClass) + mirrorMirrorSelect(nme.EmptyPackageClass) + else if (sym.isModuleClass) + Select(Select(reify(sym.sourceModule), nme.asModuleSymbol), 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 + mirrorMirrorCall(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) + mirrorBuildCall(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) + mirrorBuildCall(nme.selectOverloadedMethod, rowner, rname, reify(index)) + } else + mirrorBuildCall(nme.selectTerm, rowner, rname) + } + } else { + // todo. make sure that free methods and free local defs work correctly + if (sym.isTerm) reifyFreeTerm(sym, Ident(sym)) + else reifyFreeType(sym, Ident(sym)) + } + } + + def reifyFreeTerm(sym: Symbol, value: Tree): Tree = + reifyIntoSymtab(sym) { + if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")") + var name = newTermName(nme.REIFY_FREE_PREFIX + sym.name) + if (sym.isType) name = name.append(nme.REIFY_FREE_THIS_SUFFIX) + if (sym.isCapturedVariable) { + assert(value.isInstanceOf[Ident], showRaw(value)) + val capturedTpe = capturedVariableType(sym) + val capturedValue = referenceCapturedVariable(sym) + (name, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), reify(capturedTpe), capturedValue, mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + } else { + (name, mirrorBuildCall(nme.newFreeTerm, reify(sym.name.toString), reify(sym.tpe), value, mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + } + } + + def reifyFreeType(sym: Symbol, value: Tree): Tree = + reifyIntoSymtab(sym) { + if (reifyDebug) println("Free type: %s (%s)".format(sym, sym.accurateKindString)) + var name = newTermName(nme.REIFY_FREE_PREFIX + sym.name) + val phantomTypeTag = Apply(TypeApply(Select(Ident(nme.UNIVERSE_SHORT), nme.TypeTag), List(value)), List(Literal(Constant(null)), Literal(Constant(null)))) + val flavor = if (sym.isExistential) nme.newFreeExistential else nme.newFreeType + (name, mirrorBuildCall(flavor, reify(sym.name.toString), reify(sym.info), phantomTypeTag, mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(origin(sym)))) + } + + def reifySymDef(sym: Symbol): Tree = + reifyIntoSymtab(sym) { + if (reifyDebug) println("Sym def: %s (%s)".format(sym, sym.accurateKindString)) + assert(!sym.isLocatable, sym) // if this assertion fires, then tough type reification needs to be rethought + sym.owner.ownersIterator find (!_.isLocatable) foreach reifySymDef + var name = newTermName(nme.REIFY_SYMDEF_PREFIX + sym.name) + (name, mirrorBuildCall(nme.newNestedSymbol, reify(sym.owner), reify(sym.name), reify(sym.pos), mirrorBuildCall(nme.flagsFromBits, reify(sym.flags)), reify(sym.isClass))) + } + + private def reifyIntoSymtab(sym: Symbol)(reificode: => (TermName, Tree)): Tree ={ + def fromSymtab = symtab symRef sym + if (fromSymtab == EmptyTree) { + val reification = reificode + state.symtab += (sym, reification._1, reification._2) + } + fromSymtab + } +} 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) + } +} diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala new file mode 100644 index 0000000000..25928651c8 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala @@ -0,0 +1,203 @@ +package scala.reflect.reify +package codegen + +trait GenTypes { + self: Reifier => + + import global._ + import definitions._ + + /** + * 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) + + // this is a very special case. see the comments below for more info. + if (isSemiConcreteTypeMember(tpe)) + return reifySemiConcreteTypeMember(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(Select(reify(tpe.typeSymbol), nme.asTypeSymbol), nme.asTypeConstructor) + else tpe match { + case tpe @ NoType => + reifyMirrorObject(tpe) + case tpe @ NoPrefix => + reifyMirrorObject(tpe) + case tpe @ ThisType(root) if root.isRoot => + mirrorBuildCall(nme.thisPrefix, mirrorMirrorSelect(nme.RootClass)) + case tpe @ ThisType(empty) if empty.isEmptyPackageClass => + mirrorBuildCall(nme.thisPrefix, mirrorMirrorSelect(nme.EmptyPackageClass)) + case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic => + // [Eugene++ to Martin] makes sense? + val module = mirrorMirrorCall(nme.staticModule, reify(clazz.fullName)) + val moduleClass = Select(Select(module, nme.asModuleSymbol), nme.moduleClass) + mirrorFactoryCall(nme.ThisType, moduleClass) + 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) + case _ => + reifyToughType(tpe) + } + } + + /** Keeps track of whether this reification contains abstract type parameters */ + def reificationIsConcrete: Boolean = state.reificationIsConcrete + + def spliceType(tpe: Type): Tree = { + // [Eugene] it seems that depending on the context the very same symbol can be either a spliceable tparam or a quantified existential. very weird! + val quantified = currentQuantified + if (tpe.isSpliceable && !(quantified contains tpe.typeSymbol)) { + if (reifyDebug) println("splicing " + tpe) + + val tagFlavor = if (concrete) tpnme.ConcreteTypeTag.toString else tpnme.TypeTag.toString + val key = (tagFlavor, tpe.typeSymbol) + // 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(universe, tagFlavor, tpe)) + val result = + typer.resolveTypeTag(defaultErrorPosition, universe.tpe, tpe, concrete = concrete, allowMaterialization = false) match { + case failure if failure.isEmpty => + if (reifyDebug) println("implicit search was fruitless") + if (reifyDebug) println("trying to splice as manifest") + val splicedAsManifest = spliceAsManifest(tpe) + if (splicedAsManifest.isEmpty) { + if (reifyDebug) println("no manifest in scope") + EmptyTree + } else { + if (reifyDebug) println("successfully spliced as manifest: " + splicedAsManifest) + splicedAsManifest + } + case success => + if (reifyDebug) println("implicit search has produced a result: " + success) + state.reificationIsConcrete &= concrete || success.tpe <:< ConcreteTypeTagClass.asTypeConstructor + Select(Apply(Select(success, nme.in), List(Ident(nme.MIRROR_SHORT))), nme.tpe) + } + if (result != EmptyTree) return result + state.reificationIsConcrete = false + } + + EmptyTree + } + + private def spliceAsManifest(tpe: Type): Tree = { + val ManifestClass = rootMirror.staticClass("scala.reflect.Manifest") + val ManifestModule = rootMirror.staticModule("scala.reflect.Manifest") + def isSynthetic(manifest: Tree) = manifest exists (sub => sub.symbol != null && (sub.symbol == ManifestModule || sub.symbol.owner == ManifestModule)) + def searchForManifest(typer: analyzer.Typer): Tree = + analyzer.inferImplicit( + EmptyTree, + appliedType(ManifestClass.asTypeConstructor, List(tpe)), + reportAmbiguous = false, + isView = false, + context = typer.context, + saveAmbiguousDivergent = false, + pos = defaultErrorPosition) match { + case success if !success.tree.isEmpty && !isSynthetic(success.tree) => + val manifestInScope = success.tree + // todo. write a test for this + if (ReflectRuntimeUniverse == NoSymbol) CannotConvertManifestToTagWithoutScalaReflect(tpe, manifestInScope) + val cm = typer.typed(Ident(ReflectRuntimeCurrentMirror)) + val tagTree = gen.mkMethodCall(ReflectRuntimeUniverse, nme.manifestToConcreteTypeTag, List(tpe), List(cm, manifestInScope)) + Select(Apply(Select(tagTree, nme.in), List(Ident(nme.MIRROR_SHORT))), nme.tpe) + case _ => + EmptyTree + } + val result = typer.silent(silentTyper => silentTyper.context.withMacrosDisabled(searchForManifest(silentTyper))) + result match { + case analyzer.SilentResultValue(result) => result + case analyzer.SilentTypeError(_) => EmptyTree + } + } + + /** Reify a semi-concrete type member. + * + * This is a VERY special case to deal with stuff like `typeOf[ru.Type]`. + * In that case `Type`, which is an abstract type member of scala.reflect.api.Universe, is not a free type. + * Why? Because we know its prefix, and it unambiguously determines the type. + * + * Here is a different view on this question that supports this suggestion. + * Say, you reify a tree. Iff it doesn't contain free types, it can be successfully compiled and run. + * For example, if you reify `tpe.asInstanceOf[T]` taken from `def foo[T]`, then you won't be able to compile the result. + * Fair enough, you don't know the `T`, so the compiler will choke. + * This fact is captured by reification result having a free type T (this can be inspected by calling `tree.freeTypes`). + * Now imagine you reify the following tree: `tpe.asInstanceOf[ru.Type]`. + * To the contrast with the previous example, that's totally not a problem. + * + * Okay, so we figured out that `ru.Type` is not a free type. + * However, in our reification framework, this type would be treated a free type. + * Why? Because `tpe.isSpliceable` will return true. + * Hence we intervene and handle this situation in a special way. + * + * By the way, we cannot change the definition of `isSpliceable`, because class tags also depend on it. + * And, you know, class tags don't care whether we select a type member from a concrete instance or get it from scope (as with type parameters). + * The type itself still remains not concrete, in the sense that we don't know its erasure. + * I.e. we can compile the code that involves `ru.Type`, but we cannot serialize an instance of `ru.Type`. + */ + private def reifySemiConcreteTypeMember(tpe: Type): Tree = tpe match { + case tpe @ TypeRef(pre @ SingleType(prepre, presym), sym, args) if sym.isAbstractType && !sym.isExistential => + return mirrorFactoryCall(nme.TypeRef, reify(pre), mirrorBuildCall(nme.selectType, reify(sym.owner), reify(sym.name.toString)), reify(args)) + } + + /** Reify an annotated type, i.e. the one that makes us deal with AnnotationInfos */ + private def reifyAnnotatedType(tpe: AnnotatedType): Tree = { + val AnnotatedType(anns, underlying, selfsym) = tpe + mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym)) + } + + /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */ + private def reifyToughType(tpe: Type): Tree = { + if (reifyDebug) println("tough type: %s (%s)".format(tpe, tpe.kind)) + + def reifyScope(scope: Scope): Tree = { + scope foreach reifySymDef + mirrorCall(nme.newScopeWith, scope.toList map reify: _*) + } + + tpe match { + case tpe @ RefinedType(parents, decls) => + reifySymDef(tpe.typeSymbol) + mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) + case tpe @ ExistentialType(tparams, underlying) => + tparams foreach reifySymDef + mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) + case tpe @ ClassInfoType(parents, decls, clazz) => + reifySymDef(clazz) + mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) + case tpe @ MethodType(params, restpe) => + params foreach reifySymDef + mirrorFactoryCall(tpe, reify(params), reify(restpe)) + case tpe @ PolyType(tparams, underlying) => + tparams foreach reifySymDef + mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) + case _ => + throw new Error("internal error: %s (%s) is not supported".format(tpe, tpe.kind)) + } + } +} diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala new file mode 100644 index 0000000000..e7fa858346 --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -0,0 +1,145 @@ +package scala.reflect.reify +package codegen + +trait GenUtils { + self: Reifier => + + import global._ + import definitions._ + + 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.UNIVERSE_PREFIX + name) + + def mirrorBuildSelect(name: String): Tree = + termPath(nme.UNIVERSE_BUILD_PREFIX + name) + + def mirrorMirrorSelect(name: String): Tree = + termPath(nme.MIRROR_PREFIX + name) + + def mirrorCall(name: TermName, args: Tree*): Tree = + call("" + (nme.UNIVERSE_PREFIX append name), args: _*) + + def mirrorCall(name: String, args: Tree*): Tree = + call(nme.UNIVERSE_PREFIX + name, args: _*) + + def mirrorBuildCall(name: TermName, args: Tree*): Tree = + call("" + (nme.UNIVERSE_BUILD_PREFIX append name), args: _*) + + def mirrorBuildCall(name: String, args: Tree*): Tree = + call(nme.UNIVERSE_BUILD_PREFIX + name, args: _*) + + def mirrorMirrorCall(name: TermName, args: Tree*): Tree = + call("" + (nme.MIRROR_PREFIX append name), args: _*) + + def mirrorMirrorCall(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) + } + + object TypedOrAnnotated { + def unapply(tree: Tree): Option[Tree] = tree match { + case ty @ Typed(_, _) => + Some(ty) + case at @ Annotated(_, _) => + Some(at) + case _ => + None + } + } + + def isAnnotated(tpe: Type) = { + def isAnnotated(tpe: Type) = tpe match { + case _: AnnotatedType => true + case _ => false + } + + tpe != null && (tpe exists isAnnotated) + } + + def isSemiConcreteTypeMember(tpe: Type) = tpe match { + case TypeRef(SingleType(_, _), sym, _) if sym.isAbstractType && !sym.isExistential => true + case _ => false + } + + def isCrossStageTypeBearer(tree: Tree): Boolean = tree match { + case TypeApply(hk, _) => isCrossStageTypeBearer(hk) + case Select(sym @ Select(_, ctor), nme.apply) if ctor == nme.TypeTag || ctor == nme.ConcreteTypeTag || ctor == nme.Expr => true + case _ => false + } + + 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/codegen/Names.scala b/src/compiler/scala/reflect/reify/codegen/Names.scala deleted file mode 100644 index 589f6355d0..0000000000 --- a/src/compiler/scala/reflect/reify/codegen/Names.scala +++ /dev/null @@ -1,15 +0,0 @@ -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 deleted file mode 100644 index ac9195ef31..0000000000 --- a/src/compiler/scala/reflect/reify/codegen/Positions.scala +++ /dev/null @@ -1,18 +0,0 @@ -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 deleted file mode 100644 index 5ab8a11efe..0000000000 --- a/src/compiler/scala/reflect/reify/codegen/Symbols.scala +++ /dev/null @@ -1,184 +0,0 @@ -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 == rootMirror.RootPackage) - Select(mirrorSelect(nme.definitions), nme.RootPackage) - else if (sym == rootMirror.RootClass) - Select(mirrorSelect(nme.definitions), nme.RootClass) - else if (sym == rootMirror.EmptyPackage) - Select(mirrorSelect(nme.definitions), nme.EmptyPackage) - else if (sym == rootMirror.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) reifyFreeTerm(sym, Ident(sym)) - else reifyFreeType(sym, Ident(sym)) - } - } - - def reifyFreeTerm(sym: Symbol, value: Tree): Tree = - locallyReified get sym match { - case Some(reified) => - reified - case None => - if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")") - var name = newTermName(nme.MIRROR_FREE_PREFIX + sym.name) - if (sym.isType) name = name.append(nme.MIRROR_FREE_THIS_SUFFIX) - if (sym.isCapturedVariable) { - assert(value.isInstanceOf[Ident], showRaw(value)) - val capturedTpe = capturedVariableType(sym) - val capturedValue = referenceCapturedVariable(sym) - locallyReify(sym, name, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(capturedTpe), capturedValue, reify(sym.flags), reify(origin(sym)))) - } else { - locallyReify(sym, name, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(sym.tpe), value, reify(sym.flags), reify(origin(sym)))) - } - } - - def reifyFreeType(sym: Symbol, value: Tree): Tree = - locallyReified get sym match { - case Some(reified) => - reified - case None => - if (reifyDebug) println("Free type: %s (%s)".format(sym, sym.accurateKindString)) - var name = newTermName(nme.MIRROR_FREE_PREFIX + sym.name) - val phantomTypeTag = Apply(TypeApply(Select(Ident(nme.MIRROR_SHORT), nme.TypeTag), List(value)), List(Literal(Constant(null)), Literal(Constant(null)))) - val flavor = if (sym.isExistential) nme.newFreeExistential else nme.newFreeType - locallyReify(sym, name, mirrorCall(flavor, reify(sym.name.toString), reify(sym.info), phantomTypeTag, reify(sym.flags), reify(origin(sym)))) - } - - def reifySymDef(sym: Symbol): Tree = - locallyReified get sym match { - case Some(reified) => - reified - case None => - if (reifyDebug) println("Sym def: %s (%s)".format(sym, sym.accurateKindString)) - assert(!sym.isLocatable, sym) // if this assertion fires, then tough type reification needs to be rethought - sym.owner.ownersIterator find (!_.isLocatable) foreach reifySymDef - var name = newTermName(nme.MIRROR_SYMDEF_PREFIX + sym.name) - locallyReify(sym, name, Apply(Select(reify(sym.owner), nme.newNestedSymbol), List(reify(sym.name), reify(sym.pos), reify(sym.flags), reify(sym.isClass)))) - } - - // todo. very brittle abstraction, needs encapsulation - import scala.collection.mutable._ - private val localReifications = ArrayBuffer[Tree]() - private val locallyReified = Map[Symbol, Tree]() - private var filledIn = false - def symbolTable: List[Tree] = { fillInSymbolTable(); localReifications.toList } - def symbolTable_=(newSymbolTable: List[Tree]): Unit = { - localReifications.clear() - locallyReified.clear() - filledIn = false - newSymbolTable foreach { - case entry => - val att = entry.attachments.get[ReifyAttachment] - att match { - case Some(ReifyAttachment(sym)) => - // don't duplicate reified symbols when merging inlined reifee - if (!(locallyReified contains sym)) { - val ValDef(_, name, _, _) = entry - localReifications += entry - locallyReified(sym) = Ident(name) - } - case other => - // do nothing => symbol table fill-ins will be repopulated later - } - } - } - - private def localName(name0: TermName): TermName = { - var name = name0.toString - name = name.replace(".type", "$type") - name = name.replace(" ", "$") - val fresh = typer.context.unit.fresh - newTermName(fresh.newName(name)) - } - - private def locallyReify(sym: Symbol, name0: TermName, reificode: => Tree): Tree = { - val reified = reificode - val name = localName(name0) - // todo. tried to declare a private class here to carry an attachment, but it's path-dependent - // so got troubles with exchanging free variables between nested and enclosing quasiquotes - // attaching just Symbol isn't good either, so we need to think of a principled solution - val local = ValDef(NoMods, name, TypeTree(), reified) addAttachment ReifyAttachment(sym) - localReifications += local - filledIn = false - locallyReified(sym) = Ident(name) - locallyReified(sym) - } - - /** Sets type signatures and annotations for locally reified symbols */ - private def fillInSymbolTable() = { - if (!filledIn) { - val fillIns = new ArrayBuffer[Tree] - var i = 0 - while (i < localReifications.length) { - // fillInSymbol might create new locallyReified symbols, that's why this is done iteratively - val reified = localReifications(i) - val att = reified.attachments.get[ReifyAttachment] - att match { - case Some(ReifyAttachment(sym)) => fillIns += fillInSymbol(sym) - case other => // do nothing - } - i += 1 - } - - filledIn = true - localReifications ++= fillIns.toList - } - } - - /** Generate code to add type and annotation info to a reified symbol */ - private def fillInSymbol(sym: Symbol): Tree = { - if (reifyDebug) println("Filling in: %s (%s)".format(sym, sym.accurateKindString)) - val isFree = locallyReified(sym) match { case Ident(name) => name startsWith nme.MIRROR_FREE_PREFIX } - if (isFree) { - if (sym.annotations.isEmpty) EmptyTree - else Apply(Select(locallyReified(sym), nme.setAnnotations), List(reify(sym.annotations))) - } else { - import scala.reflect.internal.Flags._ - if (sym hasFlag LOCKED) { - // [Eugene] better to have a symbol without a type signature, than to crash with a CyclicReference - EmptyTree - } else { - val rset = Apply(Select(locallyReified(sym), nme.setTypeSignature), List(reify(sym.info))) - if (sym.annotations.isEmpty) rset - else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations))) - } - } - } -} \ 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 deleted file mode 100644 index a4543d84c1..0000000000 --- a/src/compiler/scala/reflect/reify/codegen/Trees.scala +++ /dev/null @@ -1,255 +0,0 @@ -package scala.reflect.reify -package codegen - -trait Trees { - self: Reifier => - - import mirror._ - import definitions._ - import treeInfo._ - - // unfortunately, these are necessary to reify AnnotatedTypes - // I'd gladly got rid of them, but I don't fancy making a metaprogramming API that doesn't work with annotated types - var reifyTreeSymbols = false - var reifyTreeTypes = false - - /** - * 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(_)) => - // [Eugene] was necessary when we reified erasures as normalized tycons - // now, when we do existentialAbstraction on normalizations, everything works great - // todo. find an explanation -// if (const.tag == ClazzTag) { -//// def preprocess(tpe: Type): Type = tpe.typeSymbol match { -//// case ArrayClass => appliedType(ArrayClass, preprocess(tpe.typeArgs.head)) -//// case _ => tpe.typeConstructor -//// } -//// val tpe = preprocess(const.typeValue) -// val tpe = const.typeValue -// var reified = reify(tpe) -// reified = mirrorCall(nme.Literal, mirrorCall(nme.Constant, reified)) -//// val skolems = ClassClass.typeParams map (_ => newTypeName(typer.context.unit.fresh.newName("_$"))) -//// var existential = mirrorCall(nme.AppliedTypeTree, mirrorCall(nme.TypeTree, reify(ClassClass.typeConstructor)), mkList(skolems map (skolem => mirrorCall(nme.Ident, reify(skolem))))) -//// existential = mirrorCall(nme.ExistentialTypeTree, existential, reify(skolems map (skolem => TypeDef(Modifiers(Set(Modifier.deferred: Modifier)), skolem, Nil, TypeBoundsTree(Ident(NothingClass) setType NothingClass.tpe, Ident(AnyClass) setType AnyClass.tpe))))) -//// reified = mirrorCall(nme.TypeApply, mirrorCall(nme.Select, reified, reify(nme.asInstanceOf_)), mkList(List(existential))) -// // why is this required?? -//// reified = mirrorCall(nme.TypeApply, mirrorCall(nme.Select, reified, reify(nme.asInstanceOf_)), mkList(List(mirrorCall(nme.TypeTree, reify(appliedType(ClassClass.tpe, List(AnyClass.tpe))))))) -// reified -// } else { -// mirrorCall(nme.Literal, reifyProduct(const)) -// } - 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 = Apply(Select(rtree, nme.setSymbol), List(reify(tree.symbol))) - } - if (reifyTreeTypes && tree.tpe != null) { - if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree)) - rtree = Apply(Select(rtree, nme.setType), List(reify(tree.tpe))) - } - - 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 TreeSplice(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 collect { case freedef @ FreeDef(_, _, binding, _, _) if binding.symbol.isLocalToReifee => assert(false, 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 _ => - 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 (tpe.isSpliceable) { - val spliced = spliceType(tpe) - if (spliced == EmptyTree) { - if (reifyDebug) println("splicing failed: reify as is") - mirrorCall(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) - 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, 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 = 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 deleted file mode 100644 index a2b074c6b2..0000000000 --- a/src/compiler/scala/reflect/reify/codegen/Types.scala +++ /dev/null @@ -1,168 +0,0 @@ -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) - case _ => - reifyToughType(tpe) - } - } - - /** An obscure flag necessary for implicit TypeTag generation */ - private var spliceTypesEnabled = !dontSpliceAtTopLevel - - /** Keeps track of whether this reification contains abstract type parameters */ - private var _reificationIsConcrete = true - def reificationIsConcrete = _reificationIsConcrete - def reificationIsConcrete_=(value: Boolean) { - _reificationIsConcrete = value - if (!value && concrete) { - assert(current.isInstanceOf[Type], current) - val offender = current.asInstanceOf[Type] - CannotReifyConcreteTypeTagHavingUnresolvedTypeParameters(offender) - } - } - - 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 = { - // [Eugene] it seems that depending on the context the very same symbol can be either a spliceable tparam or a quantified existential. very weird! - val quantified = currentQuantified - if (tpe.isSpliceable && !(quantified contains tpe.typeSymbol)) { - if (reifyDebug) println("splicing " + tpe) - - if (spliceTypesEnabled) { - var tagClass = if (concrete) ConcreteTypeTagClass 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)) - typer.resolveTypeTag(prefix.tpe, tpe, defaultErrorPosition, concrete) match { - case failure if failure.isEmpty => - if (reifyDebug) println("implicit search was fruitless") - EmptyTree - case success => - if (reifyDebug) println("implicit search has produced a result: " + success) - reificationIsConcrete &= concrete - 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 collect { case freedef @ FreeDef(_, _, binding, _, _) if binding.symbol.isLocalToReifee => assert(false, 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") - } - - reificationIsConcrete = false - } - - spliceTypesEnabled = true - EmptyTree - } - - /** Reify an annotated type, i.e. the one that makes us deal with AnnotationInfos */ - private def reifyAnnotatedType(tpe: AnnotatedType): Tree = { - val AnnotatedType(anns, underlying, selfsym) = tpe - mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym)) - } - - /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */ - private def reifyToughType(tpe: Type): Tree = { - if (reifyDebug) println("tough type: %s (%s)".format(tpe, tpe.kind)) - - def reifyScope(scope: Scope): Tree = { - scope foreach reifySymDef - mirrorCall(nme.newScopeWith, scope.toList map reify: _*) - } - - tpe match { - case tpe @ RefinedType(parents, decls) => - reifySymDef(tpe.typeSymbol) - mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) - case tpe @ ExistentialType(tparams, underlying) => - tparams foreach reifySymDef - mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) - case tpe @ ClassInfoType(parents, decls, clazz) => - reifySymDef(clazz) - mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) - case tpe @ MethodType(params, restpe) => - params foreach reifySymDef - mirrorFactoryCall(tpe, reify(params), reify(restpe)) - case tpe @ PolyType(tparams, underlying) => - tparams foreach reifySymDef - 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 deleted file mode 100644 index bb369a1adb..0000000000 --- a/src/compiler/scala/reflect/reify/codegen/Util.scala +++ /dev/null @@ -1,112 +0,0 @@ -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 index 35b3d9bf38..342b49dee7 100644 --- a/src/compiler/scala/reflect/reify/package.scala +++ b/src/compiler/scala/reflect/reify/package.scala @@ -1,59 +1,78 @@ package scala.reflect +import language.implicitConversions +import language.experimental.macros +import scala.reflect.base.{Universe => BaseUniverse} +import scala.reflect.makro.{Context, ReificationError, UnexpectedReificationError} import scala.tools.nsc.Global -import scala.reflect.makro.ReificationError -import scala.reflect.makro.UnexpectedReificationError package object reify { - private def mkReifier(global: Global)(typer: global.analyzer.Typer, prefix: global.Tree, reifee: Any, dontSpliceAtTopLevel: Boolean = false, concrete: Boolean = false): Reifier { val mirror: global.type } = { + private def mkReifier(global1: Global)(typer: global1.analyzer.Typer, universe: global1.Tree, mirror: global1.Tree, reifee: Any, concrete: Boolean = false): Reifier { val global: global1.type } = { val typer1: typer.type = typer - val prefix1: prefix.type = prefix + val universe1: universe.type = universe + val mirror1: mirror.type = mirror val reifee1 = reifee - val dontSpliceAtTopLevel1 = dontSpliceAtTopLevel val concrete1 = concrete new { - val mirror: global.type = global + val global: global1.type = global1 val typer = typer1 - val prefix = prefix1 + val universe = universe1 + val mirror = mirror1 val reifee = reifee1 - val dontSpliceAtTopLevel = dontSpliceAtTopLevel1 val concrete = concrete1 } with Reifier } - def reifyTree(global: Global)(typer: global.analyzer.Typer, prefix: global.Tree, tree: global.Tree): global.Tree = - mkReifier(global)(typer, prefix, tree, false, false).reified.asInstanceOf[global.Tree] + private[reify] def mkDefaultMirrorRef(global: Global)(universe: global.Tree, typer0: global.analyzer.Typer): global.Tree = { + import global._ + import definitions._ + val enclosingErasure = reifyEnclosingRuntimeClass(global)(typer0) + // JavaUniverse is defined in scala-reflect.jar, so we must be very careful in case someone reifies stuff having only scala-library.jar on the classpath + val isJavaUniverse = JavaUniverseClass != NoSymbol && universe.tpe <:< JavaUniverseClass.asTypeConstructor + if (isJavaUniverse && !enclosingErasure.isEmpty) Apply(Select(universe, nme.runtimeMirror), List(Select(enclosingErasure, sn.GetClassLoader))) + else Select(universe, nme.rootMirror) + } - def reifyType(global: Global)(typer: global.analyzer.Typer, prefix: global.Tree, tpe: global.Type, dontSpliceAtTopLevel: Boolean = false, concrete: Boolean = false): global.Tree = - mkReifier(global)(typer, prefix, tpe, dontSpliceAtTopLevel, concrete).reified.asInstanceOf[global.Tree] + def reifyTree(global: Global)(typer: global.analyzer.Typer, universe: global.Tree, mirror: global.Tree, tree: global.Tree): global.Tree = + mkReifier(global)(typer, universe, mirror, tree, concrete = false).reification.asInstanceOf[global.Tree] + + def reifyType(global: Global)(typer: global.analyzer.Typer,universe: global.Tree, mirror: global.Tree, tpe: global.Type, concrete: Boolean = false): global.Tree = + mkReifier(global)(typer, universe, mirror, tpe, concrete = concrete).reification.asInstanceOf[global.Tree] def reifyRuntimeClass(global: Global)(typer0: global.analyzer.Typer, tpe: global.Type, concrete: Boolean = true): global.Tree = { import global._ import definitions._ import analyzer.enclosingMacroPosition - def erasureTagInScope = typer0.context.withMacrosDisabled(typer0.resolveErasureTag(tpe, enclosingMacroPosition, concrete = concrete)) - def arrayTagInScope = typer0.context.withMacrosDisabled(typer0.resolveArrayTag(tpe, enclosingMacroPosition)) - val inScope = (erasureTagInScope, arrayTagInScope) + if (tpe.isSpliceable) { + val classTagInScope = typer0.resolveClassTag(enclosingMacroPosition, tpe, allowMaterialization = false) + if (!classTagInScope.isEmpty) return Select(classTagInScope, nme.runtimeClass) + val arrayTagInScope = typer0.resolveArrayTag(enclosingMacroPosition, tpe, allowMaterialization = false) + if (!arrayTagInScope.isEmpty) return gen.mkMethodCall(arrayElementClassMethod, List(arrayTagInScope)) + if (concrete) throw new ReificationError(enclosingMacroPosition, "tpe %s is an unresolved spliceable type".format(tpe)) + } - inScope match { - case (success, _) if !success.isEmpty => - Select(success, nme.runtimeClass) - case (_, success) if !success.isEmpty => - gen.mkMethodCall(arrayElementClassMethod, List(success)) + tpe.normalize match { + case TypeRef(_, ArrayClass, componentTpe :: Nil) => + val componentErasure = reifyRuntimeClass(global)(typer0, componentTpe, concrete) + gen.mkMethodCall(arrayClassMethod, List(componentErasure)) case _ => - tpe.normalize match { - case TypeRef(_, ArrayClass, componentTpe :: Nil) => - val componentRuntimeClass = reifyRuntimeClass(global)(typer0, componentTpe, concrete) - gen.mkMethodCall(arrayClassMethod, List(componentRuntimeClass)) - case _ => - if (tpe.isSpliceable && concrete) - throw new ReificationError(enclosingMacroPosition, "tpe %s is an unresolved spliceable type".format(tpe)) - var erasure = tpe.erasure - if (tpe.typeSymbol.isDerivedValueClass && global.phase.id < global.currentRun.erasurePhase.id) erasure = tpe - gen.mkNullaryCall(Predef_classOf, List(erasure)) - } + var erasure = tpe.erasure + if (tpe.typeSymbol.isDerivedValueClass && global.phase.id < global.currentRun.erasurePhase.id) erasure = tpe + gen.mkNullaryCall(Predef_classOf, List(erasure)) } } + + def reifyEnclosingRuntimeClass(global: Global)(typer0: global.analyzer.Typer): global.Tree = { + import global._ + import definitions._ + def isThisInScope = typer0.context.enclosingContextChain exists (_.tree.isInstanceOf[Template]) + if (isThisInScope) { + val enclosingClasses = typer0.context.enclosingContextChain map (_.tree) collect { case classDef: ClassDef => classDef } + val classInScope = enclosingClasses.headOption getOrElse EmptyTree + if (!classInScope.isEmpty) reifyRuntimeClass(global)(typer0, classInScope.symbol.asTypeConstructor, concrete = true) + else Select(This(tpnme.EMPTY), sn.GetClass) + } else EmptyTree + } } diff --git a/src/compiler/scala/reflect/reify/phases/Calculate.scala b/src/compiler/scala/reflect/reify/phases/Calculate.scala index 93ef46472e..41cf6c066a 100644 --- a/src/compiler/scala/reflect/reify/phases/Calculate.scala +++ b/src/compiler/scala/reflect/reify/phases/Calculate.scala @@ -4,25 +4,26 @@ package phases trait Calculate { self: Reifier => - import mirror._ + import global._ import definitions._ - import treeInfo._ - implicit class RichSymbol(sym: Symbol) { - def metalevel: Int = { assert(sym != NoSymbol); localSymbols.getOrElse(sym, 0) } + implicit class RichCalculateSymbol(sym: Symbol) { + def metalevel: Int = { assert(sym != null && sym != NoSymbol); localSymbols.getOrElse(sym, 0) } def isLocalToReifee = (localSymbols contains sym) // [Eugene] how do I account for local skolems? } - implicit class RichType(tpe: Type) { + implicit class RichCalculateType(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 localSymbols: Map[Symbol, Int] = state.localSymbols // set of all symbols that are local to the tree to be reified + private def localSymbols_=(value: Map[Symbol, Int]): Unit = state.localSymbols = value 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 + else + localSymbols += (sym -> metalevel) } /** @@ -38,7 +39,7 @@ trait Calculate { 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)))) + 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") diff --git a/src/compiler/scala/reflect/reify/phases/Metalevels.scala b/src/compiler/scala/reflect/reify/phases/Metalevels.scala index 206f3b1118..1624bbe951 100644 --- a/src/compiler/scala/reflect/reify/phases/Metalevels.scala +++ b/src/compiler/scala/reflect/reify/phases/Metalevels.scala @@ -4,9 +4,8 @@ package phases trait Metalevels { self: Reifier => - import mirror._ + import global._ import definitions._ - import treeInfo._ /** * Makes sense of cross-stage bindings. @@ -23,7 +22,7 @@ trait Metalevels { * 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 + * y.splice // 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. @@ -37,21 +36,26 @@ trait Metalevels { * * 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, + * However, how exactly do we do that in the case of y.splice? 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). + * (in fact, this is something that ``Expr.splice'' does 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. * + * upd. We no longer do that. In case of a runaway ``splice'' inside a `reify`, one will get a static error. + * Why? Unfortunately, the cute idea of transparently converting between static and dynamic splices has failed. + * 1) Runtime eval that services dynamic splices requires scala-compiler.jar, which might not be on library classpath + * 2) Runtime eval incurs a severe performance penalty, so it'd better to be explicit about it + * * ================ * - * 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. + * As we can see, the only problem is the fact that lhs'es of `splice` can be code blocks that can capture variables from the outside. + * Code inside the lhs of an `splice` 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. @@ -61,10 +65,10 @@ trait Metalevels { * * reify { * val x = 2 - * reify{x}.eval + * reify{x}.splice * } * - * Since the result of the inner reify is wrapped in an eval, it won't be reified + * Since the result of the inner reify is wrapped in a splice, 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)). @@ -76,10 +80,10 @@ trait Metalevels { * reify { * val x = 2 * val y = reify{x} - * y.eval + * y.splice * } * - * In this case the inner reify doesn't appear next to eval, so it will be reified together with x. + * In this case the inner reify doesn't appear next to splice, so it will be reified together with x. * This means that no special processing is needed here. * * Example 4. Consider the following fragment: @@ -89,16 +93,16 @@ trait Metalevels { * { * val y = 2 * val z = reify{reify{x + y}} - * z.eval - * }.eval + * z.splice + * }.splice * } * * 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. + * However, we must not touch anything inside the splice'd block, because it's not getting reified. */ - var metalevels = new Transformer { + val metalevels = new Transformer { var insideSplice = false - var freedefsToInline = collection.mutable.Map[String, ValDef]() + var inlineableBindings = collection.mutable.Map[TermName, Tree]() def withinSplice[T](op: => T) = { val old = insideSplice @@ -107,40 +111,36 @@ trait Metalevels { finally insideSplice = old } - // Q: here we deal with all sorts of reified trees. what about ReifiedType(_, _, _, _)? + // 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(ReifiedTree(universe, mirror, symtab, rtree, tpe, rtpe, concrete)) => + if (reifyDebug) println("entering inlineable splice: " + tree) + val inlinees = symtab.syms filter (_.isLocalToReifee) + inlinees foreach (inlinee => symtab.symAliases(inlinee) foreach (alias => inlineableBindings(alias) = symtab.symBinding(inlinee))) + val symtab1 = symtab -- inlinees + if (reifyDebug) println("trimmed %s inlineable free defs from its symbol table: %s".format(inlinees.length, inlinees map (inlinee => symtab.symName(inlinee)) mkString(", "))) + withinSplice { super.transform(TreeSplice(ReifiedTree(universe, mirror, symtab1, rtree, tpe, rtpe, concrete))) } 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 ", ")) + val breaches = splicee filter (sub => sub.hasSymbol && sub.symbol != NoSymbol && sub.symbol.metalevel > 0) + if (!insideSplice && breaches.nonEmpty) { + // we used to convert dynamic splices into runtime evals transparently, but we no longer do that + // why? see comments above + // if (settings.logRuntimeSplices.value) reporter.echo(tree.pos, "this splice cannot be resolved statically") + // withinSplice { super.transform(tree) } + if (reifyDebug) println("metalevel breach in %s: %s".format(tree, (breaches map (_.symbol)).distinct mkString ", ")) + CannotReifyRuntimeSplice(tree) + } else { + withinSplice { super.transform(tree) } } - withinSplice { super.transform(tree) } - // todo. also inline usages of ``freedefsToInline'' in the symbolTable itself + // todo. also inline usages of ``inlineableBindings'' in the symtab 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 - // todo. same goes for auxiliary symbol defs reified to support tough types - // some of them need to be rebuilt, some of them need to be removed, because they're no longer necessary - case FreeRef(mr, name) if freedefsToInline contains name => + case FreeRef(_, name) if inlineableBindings 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) + val inlined = reify(inlineableBindings(name)) if (reifyDebug) println("verdict: inlined as %s".format(showRaw(inlined))) inlined case _ => diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index 0e0ce17bd0..dc0028be38 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -5,29 +5,22 @@ 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 AnnotationInfos - with Positions - with Util { +trait Reify extends GenSymbols + with GenTypes + with GenNames + with GenTrees + with GenAnnotationInfos + with GenPositions + with GenUtils { self: Reifier => - import mirror._ + import global._ import definitions._ - import treeInfo._ - // `reify` looked so nice, I wanted to push the last bit of orthogonal - // logic out of it so you can see the improvement. There is no cost to - // wrapper methods of this form because the inliner will eliminate them, - // but they are very good at separating concerns like pushing/popping - // a stack, and they are great for composition and reuse. - // - // Also, please avoid public vars whenever possible. private object reifyStack { - var currents: List[Any] = reifee :: Nil + def currents: List[Any] = state.reifyStack + def currents_=(value: List[Any]): Unit = state.reifyStack = value @inline final def push[T](reifee: Any)(body: => T): T = { currents ::= reifee @@ -53,9 +46,9 @@ trait Reify extends Symbols case tree: Tree => reifyTree(tree) // disabled because this is a very special case that I plan to remove later // why do I dislike annotations? see comments to `reifyAnnotationInfo` -// case ann: AnnotationInfo => reifyAnnotationInfo(ann) + // case ann: AnnotationInfo => reifyAnnotationInfo(ann) case pos: Position => reifyPosition(pos) - case mods: mirror.Modifiers => reifyModifiers(mods) + case mods: global.Modifiers => reifyModifiers(mods) case xs: List[_] => reifyList(xs) case s: String => Literal(Constant(s)) case v if isAnyVal(v) => Literal(Constant(v)) diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index 4ab306a13f..2a562d81e2 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -6,15 +6,16 @@ import scala.tools.nsc.symtab.Flags._ trait Reshape { self: Reifier => - import mirror._ + import global._ import definitions._ - import treeInfo._ /** * Rolls back certain changes that were introduced during typechecking of the reifee. * * These include: + * * Undoing macro expansions * * Replacing type trees with TypeTree(tpe) + * * Reassembling CompoundTypeTrees into reifiable form * * Transforming Modifiers.annotations into Symbol.annotations * * Transforming Annotated annotations into AnnotatedType annotations * * Transforming Annotated(annot, expr) into Typed(expr, TypeTree(Annotated(annot, _)) @@ -23,7 +24,8 @@ trait Reshape { val reshape = new Transformer { var currentSymbol: Symbol = NoSymbol - override def transform(tree: Tree) = { + override def transform(tree0: Tree) = { + val tree = undoMacroExpansion(tree0) currentSymbol = tree.symbol val preTyper = tree match { @@ -31,8 +33,13 @@ trait Reshape { tree case tt @ TypeTree() => toPreTyperTypeTree(tt) + case ctt @ CompoundTypeTree(_) => + toPreTyperCompoundTypeTree(ctt) case toa @ TypedOrAnnotated(_) => toPreTyperTypedOrAnnotated(toa) + case ta @ TypeApply(_, _) if isCrossStageTypeBearer(ta) => + if (reifyDebug) println("cross-stage type bearer, retaining: " + tree) + ta case ta @ TypeApply(hk, ts) => val discard = ts collect { case tt: TypeTree => tt } exists isDiscarded if (reifyDebug && discard) println("discarding TypeApply: " + tree) @@ -85,6 +92,29 @@ trait Reshape { super.transform(preTyper) } + private def undoMacroExpansion(tree: Tree): Tree = + tree.attachments.get[MacroExpansionAttachment] match { + case Some(MacroExpansionAttachment(original)) => + original match { + // this hack is necessary until I fix implicit macros + // so far tag materialization is implemented by sneaky macros hidden in scala-compiler.jar + // hence we cannot reify references to them, because noone will be able to see them later + // when implicit macros are fixed, these sneaky macros will move to corresponding companion objects + // of, say, ClassTag or TypeTag + case Apply(TypeApply(_, List(tt)), _) if original.symbol == MacroInternal_materializeArrayTag => + gen.mkNullaryCall(Predef_implicitly, List(appliedType(ArrayTagClass, tt.tpe))) + case Apply(TypeApply(_, List(tt)), _) if original.symbol == MacroInternal_materializeClassTag => + gen.mkNullaryCall(Predef_implicitly, List(appliedType(ClassTagClass, tt.tpe))) + case Apply(TypeApply(_, List(tt)), List(pre)) if original.symbol == MacroInternal_materializeTypeTag => + gen.mkNullaryCall(Predef_implicitly, List(typeRef(pre.tpe, TypeTagClass, List(tt.tpe)))) + case Apply(TypeApply(_, List(tt)), List(pre)) if original.symbol == MacroInternal_materializeConcreteTypeTag => + gen.mkNullaryCall(Predef_implicitly, List(typeRef(pre.tpe, ConcreteTypeTagClass, List(tt.tpe)))) + case _ => + original + } + case _ => tree + } + override def transformModifiers(mods: Modifiers) = { val mods1 = toPreTyperModifiers(mods, currentSymbol) super.transformModifiers(mods1) @@ -130,6 +160,7 @@ trait Reshape { * 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. There are also problems with CompoundTypeTrees. I had to use attachments to retain necessary information. * * 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!). @@ -137,7 +168,7 @@ trait Reshape { */ private def isDiscarded(tt: TypeTree) = tt.original == null private def toPreTyperTypeTree(tt: TypeTree): Tree = { - if (tt.original != null) { + if (!isDiscarded(tt)) { // 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) @@ -154,6 +185,14 @@ trait Reshape { } } + private def toPreTyperCompoundTypeTree(ctt: CompoundTypeTree): Tree = { + val CompoundTypeTree(tmpl @ Template(parents, self, stats)) = ctt + assert(self eq emptyValDef, self) + val att = tmpl.attachments.get[CompoundTypeTreeOriginalAttachment] + val CompoundTypeTreeOriginalAttachment(parents1, stats1) = att.getOrElse(CompoundTypeTreeOriginalAttachment(parents, stats)) + CompoundTypeTree(Template(parents1, self, stats1)) + } + private def toPreTyperTypedOrAnnotated(tree: Tree): Tree = tree match { case ty @ Typed(expr1, tt @ TypeTree()) => if (reifyDebug) println("reify typed: " + tree) diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala new file mode 100644 index 0000000000..1fdc015325 --- /dev/null +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -0,0 +1,294 @@ +package scala.reflect.reify +package utils + +trait Extractors { + self: Utils => + + import global._ + import definitions._ + import Flag._ + + // Example of a reified tree for `reify(List(1, 2))`: + // (also contains an example of a reified type as a third argument to the constructor of Expr) + // { + // val $u: reflect.runtime.universe.type = scala.reflect.runtime.`package`.universe; + // val $m: $u.Mirror = $u.runtimeMirror(Test.this.getClass().getClassLoader()); + // $u.Expr[List[Int]]($m, { + // final class $treecreator1 extends scala.reflect.base.TreeCreator { + // def (): $treecreator1 = { + // $treecreator1.super.(); + // () + // }; + // def apply[U >: Nothing <: scala.reflect.base.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Tree = { + // val $u: scala.reflect.api.Universe = $m$untyped.universe.asInstanceOf[scala.reflect.api.Universe]; + // val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror]; + // applyImpl($m).asInstanceOf[U#Tree]; + // } + // def applyImpl[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Tree = { + // val $u: U = $m$untyped.universe; + // val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror]; + // $u.Apply($u.Select($u.Select($u.build.This($m.staticModule("scala.collection.immutable").moduleClass), $u.newTermName("List")), $u.newTermName("apply")), List($u.Literal($u.Constant(1)), $u.Literal($u.Constant(2)))) + // } + // }; + // new $treecreator1() + // })($u.ConcreteTypeTag[List[Int]]($m, { + // final class $typecreator1 extends scala.reflect.base.TypeCreator { + // def (): $typecreator1 = { + // $typecreator1.super.(); + // () + // }; + // def apply[U >: Nothing <: scala.reflect.base.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Type = { + // val $u: U = $m$untyped.universe; + // val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror]; + // $u.TypeRef($u.ThisType($m.staticModule("scala.collection.immutable").moduleClass), $m.staticClass("scala.collection.immutable.List"), List($m.staticClass("scala.Int").asTypeConstructor)) + // } + // }; + // new $typecreator1() + // })) + // } + + private def mkCreator(flavor: TypeName, symtab: SymbolTable, rtree: Tree): Tree = { + val tparamu = newTypeName("U") + val (reifierBase, reifierName, reifierTpt, reifierUniverse) = flavor match { + case tpnme.REIFY_TYPECREATOR_PREFIX => (TypeCreatorClass, nme.apply, SelectFromTypeTree(Ident(tparamu), tpnme.Type), BaseUniverseClass) + case tpnme.REIFY_TREECREATOR_PREFIX => (TreeCreatorClass, nme.applyImpl, SelectFromTypeTree(Ident(BaseUniverseClass), tpnme.Tree), ApiUniverseClass) + case _ => throw new Error(s"unexpected flavor $flavor") + } + val reifierPreamble = flavor match { + case tpnme.REIFY_TYPECREATOR_PREFIX => Nil + case tpnme.REIFY_TREECREATOR_PREFIX => List[Tree]( + DefDef(NoMods, + nme.apply, + List(TypeDef(Modifiers(PARAM), tparamu, List(), TypeBoundsTree(Ident(NothingClass), CompoundTypeTree(Template(List(Ident(BaseUniverseClass), Ident(SingletonClass)), emptyValDef, List()))))), + List(List(ValDef(Modifiers(PARAM), nme.MIRROR_UNTYPED, AppliedTypeTree(Ident(MirrorOfClass), List(Ident(tparamu))), EmptyTree))), + SelectFromTypeTree(Ident(tparamu), tpnme.Tree), + Block( + ValDef(NoMods, nme.UNIVERSE_SHORT, Ident(ApiUniverseClass), TypeApply(Select(Select(Ident(nme.MIRROR_UNTYPED), nme.universe), nme.asInstanceOf_), List(Ident(ApiUniverseClass)))), + ValDef(NoMods, nme.MIRROR_SHORT, Select(Ident(nme.UNIVERSE_SHORT), tpnme.Mirror), TypeApply(Select(Ident(nme.MIRROR_UNTYPED), nme.asInstanceOf_), List(Select(Ident(nme.UNIVERSE_SHORT), tpnme.Mirror)))), + TypeApply(Select(Apply(TypeApply(Ident(reifierName), List(SingletonTypeTree(Ident(nme.UNIVERSE_SHORT)))), List(Ident(nme.MIRROR_SHORT))), nme.asInstanceOf_), List(SelectFromTypeTree(Ident(tparamu), tpnme.Tree))) + )) + ) + case _ => throw new Error(s"unexpected flavor $flavor") + } + val reifierBody = { + def gc(symtab: SymbolTable): SymbolTable = { + def loop(symtab: SymbolTable): SymbolTable = { + def extractNames(tree: Tree) = tree.collect{ case ref: RefTree => ref.name }.toSet + val usedNames = extractNames(rtree) ++ symtab.syms.flatMap(sym => extractNames(symtab.symDef(sym))) + symtab filterAliases { case (_, name) => usedNames(name) } + } + var prev = symtab + var next = loop(symtab) + while (next.syms.length < prev.syms.length) { + prev = next + next = loop(prev) + } + next + } + + val universeAlias = ValDef(NoMods, nme.UNIVERSE_SHORT, Ident(tparamu), Select(Ident(nme.MIRROR_UNTYPED), nme.universe)) + val mirrorAlias = ValDef(NoMods, nme.MIRROR_SHORT, Select(Ident(nme.UNIVERSE_SHORT), tpnme.Mirror), TypeApply(Select(Ident(nme.MIRROR_UNTYPED), nme.asInstanceOf_), List(Select(Ident(nme.UNIVERSE_SHORT), tpnme.Mirror)))) + val trimmedSymtab = if (hasReifier) gc(symtab) else symtab + Block(universeAlias :: mirrorAlias :: trimmedSymtab.encode, rtree) + } + val tpec = ClassDef( + Modifiers(FINAL), + newTypeName(global.currentUnit.fresh.newName(flavor.toString)), + List(), + Template(List(Ident(reifierBase)), + emptyValDef, + List( + DefDef(NoMods, nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))) + ) ++ reifierPreamble ++ List( + DefDef(NoMods, + reifierName, + List(TypeDef(Modifiers(PARAM), tparamu, List(), TypeBoundsTree(Ident(NothingClass), CompoundTypeTree(Template(List(Ident(reifierUniverse), Ident(SingletonClass)), emptyValDef, List()))))), + List(List(ValDef(Modifiers(PARAM), nme.MIRROR_UNTYPED, AppliedTypeTree(Ident(MirrorOfClass), List(Ident(tparamu))), EmptyTree))), + reifierTpt, reifierBody)))) + Block(tpec, ApplyConstructor(Ident(tpec.name), List())) + } + + private def mkWrapper(universe: Tree, mirror: Tree, wrappee: Tree): Tree = { + val universeAlias = ValDef(NoMods, nme.UNIVERSE_SHORT, SingletonTypeTree(universe), universe) + val mirrorAlias = ValDef(NoMods, nme.MIRROR_SHORT, Select(Ident(nme.UNIVERSE_SHORT), tpnme.Mirror), mirror orElse mkDefaultMirrorRef(global)(universe, typer)) + Block(List(universeAlias, mirrorAlias), wrappee) + } + + object ReifiedTree { + def apply(universe: Tree, mirror: Tree, symtab: SymbolTable, rtree: Tree, tpe: Type, rtpe: Tree, concrete: Boolean): Tree = { + val tagFactory = if (concrete) nme.ConcreteTypeTag else nme.TypeTag + val tagCtor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), tagFactory), nme.apply), List(TypeTree(tpe))) + val exprCtor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), nme.Expr), nme.apply), List(TypeTree(tpe))) + val tagArgs = List(Ident(nme.MIRROR_SHORT), mkCreator(tpnme.REIFY_TYPECREATOR_PREFIX, symtab, rtpe)) + val unwrapped = Apply(Apply(exprCtor, List(Ident(nme.MIRROR_SHORT), mkCreator(tpnme.REIFY_TREECREATOR_PREFIX, symtab, rtree))), List(Apply(tagCtor, tagArgs))) + mkWrapper(universe, mirror, unwrapped) + } + + def unapply(tree: Tree): Option[(Tree, Tree, SymbolTable, Tree, Type, Tree, Boolean)] = tree match { + case Block( + List(udef @ ValDef(_, _, _, universe), mdef @ ValDef(_, _, _, mirror)), + Apply( + Apply(TypeApply(_, List(ttpe @ TypeTree())), List(_, Block(List(ClassDef(_, _, _, Template(_, _, List(_, _, DefDef(_, _, _, _, _, Block(_ :: _ :: symbolTable1, rtree)))))), _))), + // todo. doesn't take into account optimizations such as $u.TypeTag.Int or the upcoming closure optimization + List(Apply(TypeApply(Select(_, tagFlavor), _), List(_, Block(List(ClassDef(_, _, _, Template(_, _, List(_, DefDef(_, _, _, _, _, Block(_ :: _ :: symbolTable2, rtpe)))))), _)))))) + if udef.name == nme.UNIVERSE_SHORT && mdef.name == nme.MIRROR_SHORT => + Some(universe, mirror, SymbolTable(symbolTable1 ++ symbolTable2), rtree, ttpe.tpe, rtpe, tagFlavor == nme.ConcreteTypeTag) + case _ => + None + } + } + + object ReifiedType { + def apply(universe: Tree, mirror: Tree, symtab: SymbolTable, tpe: Type, rtpe: Tree, concrete: Boolean) = { + val tagFactory = if (concrete) nme.ConcreteTypeTag else nme.TypeTag + val ctor = TypeApply(Select(Select(Ident(nme.UNIVERSE_SHORT), tagFactory), nme.apply), List(TypeTree(tpe))) + val args = List(Ident(nme.MIRROR_SHORT), mkCreator(tpnme.REIFY_TYPECREATOR_PREFIX, symtab, rtpe)) + val unwrapped = Apply(ctor, args) + mkWrapper(universe, mirror, unwrapped) + } + + def unapply(tree: Tree): Option[(Tree, Tree, SymbolTable, Type, Tree, Boolean)] = tree match { + case Block( + List(udef @ ValDef(_, _, _, universe), mdef @ ValDef(_, _, _, mirror)), + // todo. doesn't take into account optimizations such as $u.TypeTag.Int or the upcoming closure optimization + Apply(TypeApply(Select(_, tagFlavor), List(ttpe @ TypeTree())), List(_, Block(List(ClassDef(_, _, _, Template(_, _, List(_, DefDef(_, _, _, _, _, Block(_ :: _ :: symtab, rtpe)))))), _)))) + if udef.name == nme.UNIVERSE_SHORT && mdef.name == nme.MIRROR_SHORT => + Some(universe, mirror, SymbolTable(symtab), ttpe.tpe, rtpe, tagFlavor == nme.ConcreteTypeTag) + case _ => + None + } + } + + object TreeSplice { + def apply(splicee: Tree): Tree = + Select(splicee, ExprSplice) + + def unapply(tree: Tree): Option[Tree] = tree match { + case Select(splicee, _) if tree.symbol != NoSymbol && tree.symbol == ExprSplice => + Some(splicee) + case _ => + None + } + } + + object FreeDef { + def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { + case FreeTermDef(uref, name, binding, flags, origin) => + Some(uref, name, binding, flags, origin) + case FreeTypeDef(uref, name, binding, flags, origin) => + Some(uref, name, binding, flags, origin) + case _ => + None + } + } + + object FreeTermDef { + def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { + case + ValDef(_, name, _, Apply( + Select(Select(uref1 @ Ident(_), build1), newFreeTerm), + List( + _, + _, + binding, + Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), + Literal(Constant(origin: String))))) + if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newFreeTerm == nme.newFreeTerm && + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + Some(uref1, name, binding, flags, origin) + case _ => + None + } + } + + object FreeTypeDef { + def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { + case + ValDef(_, name, _, Apply( + Select(Select(uref1 @ Ident(_), build1), newFreeType), + List( + _, + _, + value, + Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), + Literal(Constant(origin: String))))) + if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && (newFreeType == nme.newFreeType || newFreeType == nme.newFreeExistential) && + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + value match { + case Apply(TypeApply(Select(Select(uref3 @ Ident(_), typeTag), apply), List(binding)), List(Literal(Constant(null)), _)) + if uref3.name == nme.UNIVERSE_SHORT && typeTag == nme.TypeTag && apply == nme.apply => + Some(uref1, name, binding, flags, origin) + case Apply(TypeApply(Select(uref3 @ Ident(_), typeTag), List(binding)), List(Literal(Constant(null)), _)) + if uref3.name == nme.UNIVERSE_SHORT && typeTag == nme.TypeTag => + Some(uref1, name, binding, flags, origin) + case _ => + throw new Error("unsupported free type def: %s%n%s".format(value, showRaw(value))) + } + case _ => + None + } + } + + object FreeRef { + def unapply(tree: Tree): Option[(Tree, TermName)] = tree match { + case Apply(Select(Select(uref @ Ident(_), build), ident), List(Ident(name: TermName))) + if build == nme.build && ident == nme.Ident && name.startsWith(nme.REIFY_FREE_PREFIX) => + Some(uref, name) + case _ => + None + } + } + + object SymDef { + def unapply(tree: Tree): Option[(Tree, TermName, Long, Boolean)] = tree match { + case + ValDef(_, name, _, Apply( + Select(Select(uref1 @ Ident(_), build1), newNestedSymbol), + List( + _, + _, + _, + Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), + Literal(Constant(isClass: Boolean))))) + if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newNestedSymbol == nme.newNestedSymbol && + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + Some(uref1, name, flags, isClass) + case _ => + None + } + } + + object TypeRefToFreeType { + def unapply(tree: Tree): Option[TermName] = tree match { + case Apply(Select(Select(uref @ Ident(_), typeRef), apply), List(Select(_, noSymbol), Ident(freeType: TermName), nil)) + if (uref.name == nme.UNIVERSE_SHORT && typeRef == nme.TypeRef && noSymbol == nme.NoSymbol && freeType.startsWith(nme.REIFY_FREE_PREFIX)) => + Some(freeType) + case _ => + None + } + } + + object BoundTerm { + def unapply(tree: Tree): Option[Tree] = tree match { + case Ident(name) if name.isTermName => + Some(tree) + case This(_) => + Some(tree) + case _ => + None + } + } + + object BoundType { + def unapply(tree: Tree): Option[Tree] = tree match { + case Select(_, name) if name.isTypeName => + Some(tree) + case SelectFromTypeTree(_, name) if name.isTypeName => + Some(tree) + case Ident(name) if name.isTypeName => + Some(tree) + case _ => + None + } + } +} diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala new file mode 100644 index 0000000000..ce0ab2196a --- /dev/null +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -0,0 +1,144 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ +package scala.reflect.reify +package utils + +import scala.compat.Platform.EOL + +trait NodePrinters { + self: Utils => + + import global._ + import definitions._ + import Flag._ + + object reifiedNodeToString extends (Tree => String) { + // [Eugene++ to Martin] can we do better? + // didn't want to invent anything myself in order not to interfere with your line of thought + def bitsToFlags(bits: String): String = { + val flags = bits.toLong + if (flags == NoFlags) nme.NoFlags.toString + else { + val s_flags = new collection.mutable.ListBuffer[String] + if (flags containsAll TRAIT) s_flags += "TRAIT" + if (flags containsAll MODULE) s_flags += "MODULE" + if (flags containsAll MUTABLE) s_flags += "MUTABLE" + if (flags containsAll PACKAGE) s_flags += "PACKAGE" + if (flags containsAll METHOD) s_flags += "METHOD" + if (flags containsAll DEFERRED) s_flags += "DEFERRED" + if (flags containsAll ABSTRACT) s_flags += "ABSTRACT" + if (flags containsAll FINAL) s_flags += "FINAL" + if (flags containsAll SEALED) s_flags += "SEALED" + if (flags containsAll IMPLICIT) s_flags += "IMPLICIT" + if (flags containsAll LAZY) s_flags += "LAZY" + if (flags containsAll OVERRIDE) s_flags += "OVERRIDE" + if (flags containsAll PRIVATE) s_flags += "PRIVATE" + if (flags containsAll PROTECTED) s_flags += "PROTECTED" + if (flags containsAll CASE) s_flags += "CASE" + if (flags containsAll ABSOVERRIDE) s_flags += "ABSOVERRIDE" + if (flags containsAll BYNAMEPARAM) s_flags += "BYNAMEPARAM" + if (flags containsAll PARAM) s_flags += "PARAM" + if (flags containsAll PARAMACCESSOR) s_flags += "PARAMACCESSOR" + if (flags containsAll CASEACCESSOR) s_flags += "CASEACCESSOR" + if (flags containsAll COVARIANT) s_flags += "COVARIANT" + if (flags containsAll CONTRAVARIANT) s_flags += "CONTRAVARIANT" + if (flags containsAll DEFAULTPARAM) s_flags += "DEFAULTPARAM" + if (flags containsAll INTERFACE) s_flags += "INTERFACE" + s_flags mkString " | " + } + } + + def apply(tree: Tree): String = { + var mirrorIsUsed = 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 lines = (tree.toString.split(EOL) drop 1 dropRight 1).toList splitAt 2 + var (List(universe, mirror), reification) = lines + reification = (for (line <- reification) yield { + var s = line substring 2 + s = s.replace(nme.UNIVERSE_PREFIX.toString, "") + s = s.replace(".apply", "") + s = "([^\"])scala\\.collection\\.immutable\\.".r.replaceAllIn(s, "$1") + s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List") + s = "List\\[.*?\\]".r.replaceAllIn(s, "List") + s = s.replace("immutable.this.Nil", "List()") + s = """build\.flagsFromBits\((\d+)[lL]\)""".r.replaceAllIn(s, m => { + flagsAreUsed = true + bitsToFlags(m.group(1)) + }) + 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 bits = m.group(1) + if (buf.nonEmpty || bits != "0L") { + flagsAreUsed = true + buf.append(bitsToFlags(bits)) + } + + val replacement = "Modifiers(" + buf.reverse.mkString(", ") + ")" + java.util.regex.Matcher.quoteReplacement(replacement) + }) + s + }) + + val isExpr = reification.length > 0 && reification(0).trim.startsWith("Expr[") + var rtree = reification dropWhile (!_.trim.startsWith(s"val ${nme.UNIVERSE_SHORT}: U = ${nme.MIRROR_UNTYPED}.universe;")) + rtree = rtree drop 2 + rtree = rtree takeWhile (_ != " }") + rtree = rtree map (s0 => { + var s = s0 + mirrorIsUsed |= s contains nme.MIRROR_PREFIX.toString + s = s.replace(nme.MIRROR_PREFIX.toString, "") + s.trim + }) + + val printout = collection.mutable.ListBuffer[String](); + printout += universe.trim + if (mirrorIsUsed) printout += mirror.replace("MirrorOf[", "scala.reflect.base.MirrorOf[").trim + val imports = collection.mutable.ListBuffer[String](); + imports += nme.UNIVERSE_SHORT + // if (buildIsUsed) imports += nme.build + if (mirrorIsUsed) imports += nme.MIRROR_SHORT + if (flagsAreUsed) imports += nme.Flag + printout += s"""import ${imports map (_ + "._") mkString ", "}""" + + val name = if (isExpr) "tree" else "tpe" + if (rtree(0) startsWith "val") { + printout += s"val $name = {" + printout ++= (rtree map (" " + _)) + printout += "}" + } else { + printout += s"val $name = " + rtree(0) + } + if (isExpr) { + if (mirror contains ".getClassLoader") { + printout += "import scala.tools.reflect.ToolBox" + printout += s"println(${nme.MIRROR_SHORT}.mkToolBox().runExpr(tree))" + } else { + printout += "println(tree)" + } + } else { + printout += "println(tpe)" + } + + // printout mkString EOL + val prefix = "// produced from " + reifier.defaultErrorPosition + (prefix +: "object Test extends App {" +: (printout map (" " + _)) :+ "}") mkString EOL + } + } +} diff --git a/src/compiler/scala/reflect/reify/utils/StdAttachments.scala b/src/compiler/scala/reflect/reify/utils/StdAttachments.scala new file mode 100644 index 0000000000..abbed814e0 --- /dev/null +++ b/src/compiler/scala/reflect/reify/utils/StdAttachments.scala @@ -0,0 +1,12 @@ +package scala.reflect.reify +package utils + +trait StdAttachments { + self: Utils => + + import global._ + + case class ReifyBindingAttachment(binding: Symbol) + + case class ReifyAliasAttachment(binding: Symbol, alias: TermName) +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/utils/SymbolTables.scala b/src/compiler/scala/reflect/reify/utils/SymbolTables.scala new file mode 100644 index 0000000000..a7ac299317 --- /dev/null +++ b/src/compiler/scala/reflect/reify/utils/SymbolTables.scala @@ -0,0 +1,223 @@ +package scala.reflect.reify +package utils + +import scala.collection._ +import scala.compat.Platform.EOL + +trait SymbolTables { + self: Utils => + + import global._ + import definitions._ + import Flag._ + + class SymbolTable private[SymbolTable] ( + private[SymbolTable] val symtab: immutable.ListMap[Symbol, Tree] = immutable.ListMap[Symbol, Tree](), + private[SymbolTable] val aliases: List[(Symbol, TermName)] = List[(Symbol, TermName)](), + private[SymbolTable] val original: Option[List[Tree]] = None) { + + def syms: List[Symbol] = symtab.keys.toList + +// def aliases: Map[Symbol, List[TermName]] = aliases.distinct groupBy (_._1) mapValues (_ map (_._2)) + + def symDef(sym: Symbol): Tree = + symtab.getOrElse(sym, EmptyTree) + + def symName(sym: Symbol): TermName = + symtab.get(sym) match { + case Some(FreeDef(_, name, _, _, _)) => name + case Some(SymDef(_, name, _, _)) => name + case None => EmptyTermName + } + + def symAliases(sym: Symbol): List[TermName] = + symName(sym) match { + case name if name.isEmpty => Nil + case _ => (aliases.distinct groupBy (_._1) mapValues (_ map (_._2)))(sym) + } + + def symBinding(sym: Symbol): Tree = + symtab.get(sym) match { + case Some(FreeDef(_, _, binding, _, _)) => binding + case Some(SymDef(_, _, _, _)) => throw new UnsupportedOperationException(s"${symtab(sym)} is a symdef, hence it doesn't have a binding") + case None => EmptyTree + } + + def symRef(sym: Symbol): Tree = + symtab.get(sym) match { + case Some(FreeDef(_, name, _, _, _)) => Ident(name) addAttachment ReifyBindingAttachment(sym) + case Some(SymDef(_, name, _, _)) => Ident(name) addAttachment ReifyBindingAttachment(sym) + case None => EmptyTree + } + + def +(sym: Symbol, name: TermName, reification: Tree): SymbolTable = add(sym, name, reification) + def +(sym: Symbol, name: TermName): SymbolTable = add(sym, name) + def +(symDef: Tree): SymbolTable = add(symDef) + def ++(symDefs: TraversableOnce[Tree]): SymbolTable = (this /: symDefs)((symtab, symDef) => symtab.add(symDef)) + def ++(symtab: SymbolTable): SymbolTable = { val updated = this ++ symtab.symtab.values; new SymbolTable(updated.symtab, updated.aliases ++ symtab.aliases) } + def -(sym: Symbol): SymbolTable = remove(sym) + def -(name: TermName): SymbolTable = remove(name) + def -(symDef: Tree): SymbolTable = remove(binding(symDef)) + def --(syms: GenTraversableOnce[Symbol]): SymbolTable = (this /: syms)((symtab, sym) => symtab.remove(sym)) + def --(names: Iterable[TermName]): SymbolTable = (this /: names)((symtab, name) => symtab.remove(name)) + def --(symDefs: TraversableOnce[Tree]): SymbolTable = this -- (symDefs map (binding(_))) + def --(symtab: SymbolTable): SymbolTable = { val updated = this -- symtab.symtab.values; new SymbolTable(updated.symtab, updated.aliases diff symtab.aliases) } + def filterSyms(p: Symbol => Boolean): SymbolTable = this -- (syms filterNot p) + def filterAliases(p: (Symbol, TermName) => Boolean): SymbolTable = this -- (aliases filterNot (tuple => p(tuple._1, tuple._2)) map (_._2)) + + private def add(symDef: Tree): SymbolTable = { + val sym = binding(symDef) + assert(sym != NoSymbol, showRaw(symDef)) + val name = symDef match { + case FreeDef(_, name, _, _, _) => name + case SymDef(_, name, _, _) => name + } + val newSymtab = if (!(symtab contains sym)) symtab + (sym -> symDef) else symtab + val newAliases = aliases :+ (sym -> name) + new SymbolTable(newSymtab, newAliases) + } + + private def add(sym: Symbol, name0: TermName, reification: Tree): SymbolTable = { + def freshName(name0: TermName): TermName = { + var name = name0.toString + name = name.replace(".type", "$type") + name = name.replace(" ", "$") + val fresh = typer.context.unit.fresh + newTermName(fresh.newName(name)) + } + add(ValDef(NoMods, freshName(name0), TypeTree(), reification) addAttachment ReifyBindingAttachment(sym)) + } + + private def add(sym: Symbol, name: TermName): SymbolTable = { + if (!(syms contains sym)) error("cannot add an alias to a symbol not in the symbol table") + add(sym, name, EmptyTree) + } + + private def remove(sym: Symbol): SymbolTable = { + val newSymtab = symtab - sym + val newAliases = aliases filter (_._1 != sym) + new SymbolTable(newSymtab, newAliases) + } + + private def remove(name: TermName): SymbolTable = { + var newSymtab = symtab + val newAliases = aliases filter (_._2 != name) + newSymtab = newSymtab filter { case ((sym, _)) => newAliases exists (_._1 == sym) } + newSymtab = newSymtab map { case ((sym, tree)) => + val ValDef(mods, primaryName, tpt, rhs) = tree + val tree1 = + if (!(newAliases contains (sym, primaryName))) { + val primaryName1 = newAliases.find(_._1 == sym).get._2 + ValDef(mods, primaryName1, tpt, rhs).copyAttrs(tree) + } else tree + (sym, tree1) + } + new SymbolTable(newSymtab, newAliases) + } + + private def binding(tree: Tree): Symbol = + tree.attachments.get[ReifyBindingAttachment] match { + case Some(ReifyBindingAttachment(binding)) => binding + case other => NoSymbol + } + + private val cache = mutable.Map[SymbolTable, List[Tree]]() + def encode: List[Tree] = cache.getOrElseUpdate(this, SymbolTable.encode(this)) map (_.duplicate) + + override def toString = { + val symtabString = symtab.keys.map(symName(_)).mkString(", ") + val trueAliases = aliases.distinct.filter(entry => symName(entry._1) != entry._2) + val aliasesString = trueAliases.map(entry => s"${symName(entry._1)} -> ${entry._2}").mkString(", ") + s"""symtab = [$symtabString], aliases = [$aliasesString]${if (original.isDefined) ", has original" else ""}""" + } + + def debugString: String = { + val buf = new StringBuilder + buf.append("symbol table = " + (if (syms.length == 0) "" else "")).append(EOL) + syms foreach (sym => buf.append(symDef(sym)).append(EOL)) + buf.delete(buf.length - EOL.length, buf.length) + buf.toString + } + } + + object SymbolTable { + def apply(): SymbolTable = + new SymbolTable() + + def apply(encoded: List[Tree]): SymbolTable = { + var result = new SymbolTable(original = Some(encoded)) + encoded foreach (entry => (entry.attachments.get[ReifyBindingAttachment], entry.attachments.get[ReifyAliasAttachment]) match { + case (Some(ReifyBindingAttachment(sym)), _) => result += entry + case (_, Some(ReifyAliasAttachment(sym, alias))) => result = new SymbolTable(result.symtab, result.aliases :+ (sym, alias)) + case _ => // do nothing, this is boilerplate that can easily be recreated by subsequent `result.encode` + }) + result + } + + private[SymbolTable] def encode(symtab0: SymbolTable): List[Tree] = { + if (symtab0.original.isDefined) return symtab0.original.get.map(_.duplicate) + else assert(hasReifier, "encoding a symbol table requires a reifier") + // during `encode` we might need to do some reifications + // these reifications might lead to changes in `reifier.symtab` + // reifier is mutable, symtab is immutable. this is a tough friendship + val backup = reifier.state.backup + reifier.state.symtab = symtab0.asInstanceOf[reifier.SymbolTable] + def currtab = reifier.symtab.asInstanceOf[SymbolTable] + try { + val cumulativeSymtab = mutable.ArrayBuffer[Tree](symtab0.symtab.values.toList: _*) + val cumulativeAliases = mutable.ArrayBuffer[(Symbol, TermName)](symtab0.aliases: _*) + + def fillInSymbol(sym: Symbol): Tree = { + if (reifyDebug) println("Filling in: %s (%s)".format(sym, sym.accurateKindString)) + val isFree = currtab.symName(sym) startsWith nme.REIFY_FREE_PREFIX + if (isFree) { + if (sym.annotations.isEmpty) EmptyTree + else Apply(Select(currtab.symRef(sym), nme.setAnnotations), List(reifier.reify(sym.annotations))) + } else { + import scala.reflect.internal.Flags._ + if (sym hasFlag LOCKED) { + // [Eugene] better to have a symbol without a type signature, than to crash with a CyclicReference + EmptyTree + } else { + val rset = reifier.mirrorBuildCall(nme.setTypeSignature, currtab.symRef(sym), reifier.reify(sym.info)) + if (sym.annotations.isEmpty) rset + else reifier.mirrorBuildCall(nme.setAnnotations, rset, reifier.mkList(sym.annotations map reifier.reifyAnnotationInfo)) + } + } + } + + // `fillInSymbol` might add symbols to `symtab`, that's why this is done iteratively + var progress = 0 + while (progress < cumulativeSymtab.length) { + val sym = currtab.binding(cumulativeSymtab(progress)) + if (sym != NoSymbol) { + val symtabProgress = currtab.symtab.size + val aliasesProgress = currtab.aliases.length + val fillIn = fillInSymbol(sym) + cumulativeSymtab ++= currtab.symtab.values drop symtabProgress + cumulativeAliases ++= currtab.aliases drop aliasesProgress + cumulativeSymtab += fillIn + } + progress += 1 + } + + val withAliases = cumulativeSymtab flatMap (entry => { + val result = mutable.ListBuffer[Tree]() + result += entry + val sym = currtab.binding(entry) + if (sym != NoSymbol) + result ++= cumulativeAliases.distinct filter (alias => alias._1 == sym && alias._2 != currtab.symName(sym)) map (alias => { + val canonicalName = currtab.symName(sym) + val aliasName = alias._2 + ValDef(NoMods, aliasName, TypeTree(), Ident(canonicalName)) addAttachment ReifyAliasAttachment(sym, aliasName) + }) + result.toList + }) + + withAliases.toList + } finally { + reifier.state.restore(backup) + } + } + } +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/utils/Utils.scala b/src/compiler/scala/reflect/reify/utils/Utils.scala new file mode 100644 index 0000000000..e1213f932c --- /dev/null +++ b/src/compiler/scala/reflect/reify/utils/Utils.scala @@ -0,0 +1,21 @@ +package scala.reflect.reify +package utils + +import scala.tools.nsc.Global + +trait Utils extends NodePrinters + with Extractors + with SymbolTables + with StdAttachments { + + val global: Global + val typer: global.analyzer.Typer + + lazy val reifier: Reifier { val global: Utils.this.global.type } = getReifier + def getReifier: Reifier { val global: Utils.this.global.type } = ??? + def hasReifier = false + + val reifyDebug = global.settings.Yreifydebug.value + val reifyCopypaste = global.settings.Yreifycopypaste.value + val reifyTrace = scala.tools.nsc.util.trace when reifyDebug +} \ No newline at end of file diff --git a/src/compiler/scala/reflect/runtime/package.scala b/src/compiler/scala/reflect/runtime/package.scala index 531873c661..a5809a2629 100644 --- a/src/compiler/scala/reflect/runtime/package.scala +++ b/src/compiler/scala/reflect/runtime/package.scala @@ -9,5 +9,18 @@ package object runtime { // [Eugene++ to Martin] removed `mirrorOfLoader`, because one can use `universe.runtimeMirror` instead - def currentMirror: universe.Mirror = ??? + def currentMirror: universe.Mirror = macro Macros.currentMirror +} + +package runtime { + object Macros { + def currentMirror(c: scala.reflect.makro.Context): c.Expr[universe.Mirror] = { + import c.universe._ + val runtimeClass = c.reifyEnclosingRuntimeClass + if (runtimeClass.isEmpty) c.abort(c.enclosingPosition, "call site does not have an enclosing class") + val runtimeUniverse = Select(Select(Select(Ident(newTermName("scala")), newTermName("reflect")), newTermName("runtime")), newTermName("universe")) + val currentMirror = Apply(Select(runtimeUniverse, newTermName("runtimeMirror")), List(Select(runtimeClass, newTermName("getClassLoader")))) + c.Expr[Nothing](currentMirror)(c.TypeTag.Nothing) + } + } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index cd1a808823..a16b42dc47 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -82,7 +82,6 @@ trait ScalaSettings extends AbsScalaSettings val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more detail on why some implicits are not applicable.") val logImplicitConv = BooleanSetting ("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.") val logReflectiveCalls = BooleanSetting("-Xlog-reflective-calls", "Print a message when a reflective method call is generated") - val logRuntimeSplices = BooleanSetting("-Xlog-runtime-splices", "Print a message when Expr.eval or Expr.value cannot be resolved statically.") val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.") val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index ea66dbedd6..fde27a650a 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -967,7 +967,7 @@ abstract class Erasure extends AddInterfaces } // Rewrite 5.getClass to ScalaRunTime.anyValClass(5) else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) - global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveErasureTag(qual.tpe.widen, tree.pos, true)))) + global.typer.typed(gen.mkRuntimeCall(nme.anyValClass, List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen)))) else tree diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 8af12f3f10..db3d8b2785 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -419,7 +419,7 @@ abstract class UnCurry extends InfoTransform val toArraySym = tree.tpe member nme.toArray assert(toArraySym != NoSymbol) def getArrayTag(tp: Type): Tree = { - val tag = localTyper.resolveArrayTag(tp, tree.pos) + val tag = localTyper.resolveArrayTag(tree.pos, tp) // Don't want bottom types getting any further than this (SI-4024) if (tp.typeSymbol.isBottomClass) getArrayTag(AnyClass.tpe) else if (!tag.isEmpty) tag diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index f3afa2d33f..9855284348 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1294,8 +1294,8 @@ trait Implicits { } val tagInScope = - if (full) resolveTypeTag(NoType, tp, pos, concrete = true) - else resolveArrayTag(tp, pos) + if (full) resolveTypeTag(pos, NoType, tp, concrete = true, allowMaterialization = false) + else resolveArrayTag(pos, tp, allowMaterialization = false) if (tagInScope.isEmpty) mot(tp, Nil, Nil) else { if (full) { @@ -1307,7 +1307,7 @@ trait Implicits { |to proceed put scala-reflect.jar on your compilation classpath and recompile.""".trim.stripMargin) return SearchFailure } - if (resolveErasureTag(tp, pos, concrete = true) == EmptyTree) { + if (resolveClassTag(pos, tp, allowMaterialization = true) == EmptyTree) { context.error(pos, s""" |to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope. |however typetag -> manifest conversion requires a class tag for the corresponding type to be present. diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 96fcdd793e..93991fe7d5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -982,7 +982,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { if (typer.context.hasErrors) fail("typecheck against expected type", expanded) macroLogVerbose("typechecked2:%n%s%n%s".format(typechecked, showRaw(typechecked))) - typechecked + typechecked addAttachment MacroExpansionAttachment(expandee) } finally { openMacros = openMacros.tail } diff --git a/src/compiler/scala/tools/nsc/typechecker/Taggings.scala b/src/compiler/scala/tools/nsc/typechecker/Taggings.scala deleted file mode 100644 index bbcfa2920b..0000000000 --- a/src/compiler/scala/tools/nsc/typechecker/Taggings.scala +++ /dev/null @@ -1,71 +0,0 @@ -package scala.tools.nsc -package typechecker - -trait Taggings { - self: Analyzer => - - import global._ - import definitions._ - - trait Tagging { - self: Typer => - - private def resolveTag(taggedTp: Type, pos: Position) = beforeTyper { - inferImplicit( - EmptyTree, - taggedTp, - /*reportAmbiguous =*/ true, - /*isView =*/ false, - /*context =*/ context, - /*saveAmbiguousDivergent =*/ true, - /*pos =*/ pos - ).tree - } - - /** Finds in scope or materializes an ArrayTag. - * Should be used instead of ClassTag or ClassManifest every time compiler needs to create an array. - * - * @param tp Type we're looking an ArrayTag for, e.g. resolveArrayTag(IntClass.tpe, pos) will look for ArrayTag[Int]. - * @param pos Position for error reporting. Please, provide meaningful value. - * - * @returns Tree that represents an `scala.reflect.ArrayTag` for `tp` if everything is okay. - * EmptyTree if the result contains unresolved (i.e. not spliced) type parameters and abstract type members. - */ - def resolveArrayTag(tp: Type, pos: Position): Tree = { - val taggedTp = appliedType(ArrayTagClass.typeConstructor, List(tp)) - resolveTag(taggedTp, pos) - } - - /** Finds in scope or materializes an ErasureTag (if `concrete` is false) or a ClassTag (if `concrete` is true). - * Should be used instead of ClassTag or ClassManifest every time compiler needs to persist an erasure. - * - * @param tp Type we're looking an ErasureTag for, e.g. resolveErasureTag(IntClass.tpe, pos, true) will look for ClassTag[Int]. - * @param pos Position for error reporting. Please, provide meaningful value. - * @param concrete If true then the result must not contain unresolved (i.e. not spliced) type parameters and abstract type members. - * If false then the function will always succeed (abstract types will be erased to their upper bounds). - * - * @returns Tree that represents an `scala.reflect.ErasureTag` for `tp` if everything is okay. - * EmptyTree if `concrete` is true and the result contains unresolved (i.e. not spliced) type parameters and abstract type members. - */ - def resolveErasureTag(tp: Type, pos: Position, concrete: Boolean): Tree = { - val taggedTp = appliedType(if (concrete) ClassTagClass.typeConstructor else ???, List(tp)) - resolveTag(taggedTp, pos) - } - - /** Finds in scope or materializes a TypeTag (if `concrete` is false) or a ConcreteTypeTag (if `concrete` is true). - * - * @param pre Prefix that represents a universe this type tag will be bound to. - * @param tp Type we're looking a TypeTag for, e.g. resolveTypeTag(reflectMirrorPrefix, IntClass.tpe, pos, false) will look for scala.reflect.mirror.TypeTag[Int]. - * @param pos Position for error reporting. Please, provide meaningful value. - * @param concrete If true then the result must not contain unresolved (i.e. not spliced) type parameters and abstract type members. - * If false then the function will always succeed (abstract types will be reified as free types). - * - * @returns Tree that represents a `scala.reflect.TypeTag` for `tp` if everything is okay. - * EmptyTree if `concrete` is true and the result contains unresolved (i.e. not spliced) type parameters and abstract type members. - */ - def resolveTypeTag(pre: Type, tp: Type, pos: Position, concrete: Boolean): Tree = { - val taggedTp = appliedType(singleType(pre, pre member (if (concrete) ConcreteTypeTagClass else TypeTagClass).name), List(tp)) - resolveTag(taggedTp, pos) - } - } -} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Tags.scala b/src/compiler/scala/tools/nsc/typechecker/Tags.scala new file mode 100644 index 0000000000..d371f02d1d --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/Tags.scala @@ -0,0 +1,86 @@ +package scala.tools.nsc +package typechecker + +trait Tags { + self: Analyzer => + + import global._ + import definitions._ + + trait Tag { + self: Typer => + + private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = beforeTyper { + def wrapper (tree: => Tree): Tree = if (allowMaterialization) (context.withMacrosEnabled[Tree](tree)) else (context.withMacrosDisabled[Tree](tree)) + wrapper(inferImplicit( + EmptyTree, + taggedTp, + /*reportAmbiguous =*/ true, + /*isView =*/ false, + /*context =*/ context, + /*saveAmbiguousDivergent =*/ true, + /*pos =*/ pos + ).tree) + } + + /** Finds in scope or materializes an ArrayTag. + * Should be used instead of ClassTag or ClassManifest every time compiler needs to create an array. + * + * @param pos Position for error reporting. Please, provide meaningful value. + * @param tp Type we're looking an ArrayTag for, e.g. resolveArrayTag(pos, IntClass.tpe) will look for ArrayTag[Int]. + * @param allowMaterialization If true (default) then the resolver is allowed to launch materialization macros when there's no array tag in scope. + * If false then materialization macros are prohibited from running. + * + * @returns Tree that represents an `scala.reflect.ArrayTag` for `tp` if everything is okay. + * EmptyTree if `allowMaterialization` is false, and there is no array tag in scope. + * EmptyTree if the result contains unresolved (i.e. not spliced) type parameters and abstract type members. + */ + def resolveArrayTag(pos: Position, tp: Type, allowMaterialization: Boolean = true): Tree = { + val taggedTp = appliedType(ArrayTagClass.typeConstructor, List(tp)) + resolveTag(pos, taggedTp, allowMaterialization) + } + + /** Finds in scope or materializes a ClassTag. + * Should be used instead of ClassManifest every time compiler needs to persist an erasure. + * + * Once upon a time, we had an `ErasureTag` which was to `ClassTag` the same that `TypeTag` is for `ConcreteTypeTag`. + * However we found out that we don't really need this concept, so it got removed. + * + * @param pos Position for error reporting. Please, provide meaningful value. + * @param tp Type we're looking a ClassTag for, e.g. resolveClassTag(pos, IntClass.tpe) will look for ClassTag[Int]. + * @param allowMaterialization If true (default) then the resolver is allowed to launch materialization macros when there's no class tag in scope. + * If false then materialization macros are prohibited from running. + * + * @returns Tree that represents an `scala.reflect.ClassTag` for `tp` if everything is okay. + * EmptyTree if the result contains unresolved (i.e. not spliced) type parameters and abstract type members. + * EmptyTree if `allowMaterialization` is false, and there is no class tag in scope. + */ + def resolveClassTag(pos: Position, tp: Type, allowMaterialization: Boolean = true): Tree = { + val taggedTp = appliedType(ClassTagClass.typeConstructor, List(tp)) + resolveTag(pos, taggedTp, allowMaterialization) + } + + /** Finds in scope or materializes a TypeTag (if `concrete` is false) or a ConcreteTypeTag (if `concrete` is true). + * + * @param pos Position for error reporting. Please, provide meaningful value. + * @param pre Prefix that represents a universe this type tag will be bound to. + * If `pre` is set to `NoType`, then any type tag in scope will do, regardless of its affiliation. + * If `pre` is set to `NoType`, and tag resolution involves materialization, then `mkBasisPrefix` will be used. + * @param tp Type we're looking a TypeTag for, e.g. resolveTypeTag(pos, reflectBasisPrefix, IntClass.tpe, false) will look for scala.reflect.basis.TypeTag[Int]. + * @param concrete If true then the result must not contain unresolved (i.e. not spliced) type parameters and abstract type members. + * If false then the function will always succeed (abstract types will be reified as free types). + * @param allowMaterialization If true (default) then the resolver is allowed to launch materialization macros when there's no type tag in scope. + * If false then materialization macros are prohibited from running. + * + * @returns Tree that represents a `scala.reflect.TypeTag` for `tp` if everything is okay. + * EmptyTree if `concrete` is true and the result contains unresolved (i.e. not spliced) type parameters and abstract type members. + * EmptyTree if `allowMaterialization` is false, and there is no array tag in scope. + */ + def resolveTypeTag(pos: Position, pre: Type, tp: Type, concrete: Boolean, allowMaterialization: Boolean = true): Tree = { + val tagSym = if (concrete) ConcreteTypeTagClass else TypeTagClass + val tagTp = if (pre == NoType) TypeRef(BaseUniverseClass.asTypeConstructor, tagSym, List(tp)) else singleType(pre, pre member tagSym.name) + val taggedTp = appliedType(tagTp, List(tp)) + resolveTag(pos, taggedTp, allowMaterialization) + } + } +} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index cc36ed7428..30202ed3b5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -26,7 +26,7 @@ import util.Statistics._ * @author Martin Odersky * @version 1.0 */ -trait Typers extends Modes with Adaptations with Taggings { +trait Typers extends Modes with Adaptations with Tags { self: Analyzer => import global._ @@ -96,7 +96,7 @@ trait Typers extends Modes with Adaptations with Taggings { // this is disabled by: -Xoldpatmat, scaladoc or interactive compilation @inline private def newPatternMatching = opt.virtPatmat && !forScaladoc && !forInteractive // && (phase.id < currentRun.uncurryPhase.id) - abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tagging with TyperContextErrors { + abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with TyperContextErrors { import context0.unit import typeDebug.{ ptTree, ptBlock, ptLine } import TyperErrorGen._ @@ -2512,11 +2512,18 @@ trait Typers extends Modes with Adaptations with Taggings { } } - def typedRefinement(stats: List[Tree]) { + def typedRefinement(templ: Template) { + val stats = templ.body namer.enterSyms(stats) // need to delay rest of typedRefinement to avoid cyclic reference errors unit.toCheck += { () => val stats1 = typedStats(stats, NoSymbol) + // this code kicks in only after typer, so `stats` will never be filled in time + // as a result, most of compound type trees with non-empty stats will fail to reify + // [Eugene++] todo. investigate whether something can be done about this + val att = templ.attachments.get[CompoundTypeTreeOriginalAttachment].getOrElse(CompoundTypeTreeOriginalAttachment(Nil, Nil)) + templ.removeAttachment[CompoundTypeTreeOriginalAttachment] + templ addAttachment att.copy(stats = stats1) for (stat <- stats1 if stat.isDef) { val member = stat.symbol if (!(context.owner.ancestors forall @@ -4584,7 +4591,8 @@ trait Typers extends Modes with Adaptations with Taggings { val decls = newScope //Console.println("Owner: " + context.enclClass.owner + " " + context.enclClass.owner.id) val self = refinedType(parents1 map (_.tpe), context.enclClass.owner, decls, templ.pos) - newTyper(context.make(templ, self.typeSymbol, decls)).typedRefinement(templ.body) + newTyper(context.make(templ, self.typeSymbol, decls)).typedRefinement(templ) + templ addAttachment CompoundTypeTreeOriginalAttachment(parents1, Nil) // stats are set elsewhere tree setType self } } @@ -4856,7 +4864,7 @@ trait Typers extends Modes with Adaptations with Taggings { val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe) val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.asType, List(tpe))).last val newArrayApp = atPos(tree.pos) { - val tag = resolveArrayTag(tagType, tree.pos) + val tag = resolveArrayTag(tree.pos, tagType) if (tag.isEmpty) MissingArrayTagError(tree, tagType) else new ApplyToImplicitArgs(Select(tag, nme.newArray), args) } diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala index 8658e3225d..534f6de682 100644 --- a/src/compiler/scala/tools/reflect/FastTrack.scala +++ b/src/compiler/scala/tools/reflect/FastTrack.scala @@ -1,6 +1,7 @@ package scala.tools package reflect +import scala.reflect.makro.runtime.ContextReifiers import scala.reflect.reify.Taggers import scala.tools.nsc.typechecker.{Analyzer, Macros} @@ -15,6 +16,7 @@ trait FastTrack { import language.implicitConversions private implicit def context2taggers(c0: MacroContext) : Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers + private implicit def context2contextreifiers(c0: MacroContext) : ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers implicit def fastTrackEntry2MacroRuntime(entry: FastTrackEntry): MacroRuntime = args => entry.run(args) type FastTrackExpander = PartialFunction[(MacroContext, Tree), Tree] @@ -36,10 +38,11 @@ trait FastTrack { implicit class BindTo(sym: Symbol) { def bindTo(expander: FastTrackExpander): Unit = if (sym != NoSymbol) registry += sym -> FastTrackEntry(sym, expander) } MacroInternal_materializeArrayTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeArrayTag(u, tt.tpe) } MacroInternal_materializeClassTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeClassTag(u, tt.tpe) } - MacroInternal_materializeTypeTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeTypeTag(u, tt.tpe, concrete = false) } - MacroInternal_materializeConcreteTypeTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeTypeTag(u, tt.tpe, concrete = true) } - ApiUniverseReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExpr(c.prefix.tree, expr) } - MacroContextReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExpr(c.prefix.tree, expr) } + MacroInternal_materializeTypeTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeTypeTag(u, EmptyTree, tt.tpe, concrete = false) } + MacroInternal_materializeConcreteTypeTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeTypeTag(u, EmptyTree, tt.tpe, concrete = true) } + ApiUniverseReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExpr(c.prefix.tree, EmptyTree, expr) } + MacroContextReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExprForMacroContext(c.prefix.tree, expr) } + ReflectRuntimeCurrentMirror bindTo { case (c, _) => scala.reflect.runtime.Macros.currentMirror(c).tree } registry } } \ No newline at end of file diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 3ef2337e0f..741f1b268f 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -79,7 +79,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => var name = ft.name.toString val namesakes = freeTerms takeWhile (_ != ft) filter (ft2 => ft != ft2 && ft.name == ft2.name) if (namesakes.length > 0) name += ("$" + (namesakes.length + 1)) - freeTermNames += (ft -> newTermName(name + nme.MIRROR_FREE_VALUE_SUFFIX)) + freeTermNames += (ft -> newTermName(name + nme.REIFY_FREE_VALUE_SUFFIX)) }) var expr = new Transformer { override def transform(tree: Tree): Tree = diff --git a/src/library/scala/reflect/api/StandardNames.scala b/src/library/scala/reflect/api/StandardNames.scala index 80aed04073..9ec66b8531 100644 --- a/src/library/scala/reflect/api/StandardNames.scala +++ b/src/library/scala/reflect/api/StandardNames.scala @@ -47,12 +47,17 @@ trait StandardNames extends base.StandardNames { val FAKE_LOCAL_THIS: TermName val INITIALIZER: TermName val LAZY_LOCAL: TermName - val MIRROR_FREE_PREFIX: NameType - val MIRROR_FREE_THIS_SUFFIX: NameType - val MIRROR_FREE_VALUE_SUFFIX: NameType + val UNIVERSE_BUILD: NameType + val UNIVERSE_BUILD_PREFIX: NameType + val UNIVERSE_PREFIX: NameType + val UNIVERSE_SHORT: NameType val MIRROR_PREFIX: NameType val MIRROR_SHORT: NameType - val MIRROR_SYMDEF_PREFIX: NameType + val MIRROR_UNTYPED: NameType + val REIFY_FREE_PREFIX: NameType + val REIFY_FREE_THIS_SUFFIX: NameType + val REIFY_FREE_VALUE_SUFFIX: NameType + val REIFY_SYMDEF_PREFIX: NameType val MIXIN_CONSTRUCTOR: TermName val MODULE_INSTANCE_FIELD: TermName val OUTER: TermName @@ -147,6 +152,8 @@ trait StandardNames extends base.StandardNames { val REFINE_CLASS_NAME: TypeName val REPEATED_PARAM_CLASS_NAME: TypeName val WILDCARD_STAR: TypeName + val REIFY_TYPECREATOR_PREFIX: NameType + val REIFY_TREECREATOR_PREFIX: NameType def dropSingletonName(name: Name): TypeName def implClassName(name: Name): TypeName diff --git a/src/library/scala/reflect/makro/Context.scala b/src/library/scala/reflect/makro/Context.scala index 58fd0d3df3..09cd96664c 100644 --- a/src/library/scala/reflect/makro/Context.scala +++ b/src/library/scala/reflect/makro/Context.scala @@ -34,6 +34,6 @@ trait Context extends Aliases val prefix: Expr[PrefixType] /** Alias to the underlying mirror's reify */ - // implementation is magically hardwired to `scala.reflect.reify.Taggers` + // implementation is magically hardwired to `scala.reflect.makro.runtime.ContextReifiers` def reify[T](expr: T): Expr[T] = macro ??? } diff --git a/src/library/scala/reflect/makro/Reifiers.scala b/src/library/scala/reflect/makro/Reifiers.scala index 427b68f2d7..535aaadc3d 100644 --- a/src/library/scala/reflect/makro/Reifiers.scala +++ b/src/library/scala/reflect/makro/Reifiers.scala @@ -3,15 +3,23 @@ package scala.reflect.makro trait Reifiers { self: Context => - /** Reification prefix that refers to the standard reflexive mirror, ``scala.reflect.mirror''. + /** Reification prefix that refers to the base reflexive universe, ``scala.reflect.basis''. * Providing it for the ``prefix'' parameter of ``reifyTree'' or ``reifyType'' will create a tree that can be inspected at runtime. */ - val reflectMirrorPrefix: Tree + val basisUniverse: Tree + + /** Reification prefix that refers to the runtime reflexive universe, ``scala.reflect.runtime.universe''. + * Providing it for the ``prefix'' parameter of ``reifyTree'' or ``reifyType'' will create a full-fledged tree that can be inspected at runtime. + */ + val runtimeUniverse: Tree /** Given a tree, generate a tree that when compiled and executed produces the original tree. - * The produced tree will be bound to the mirror specified by ``prefix'' (also see ``reflectMirrorPrefix''). * For more information and examples see the documentation for ``Universe.reify''. * + * The produced tree will be bound to the specified ``universe'' and ``mirror''. + * Possible values for ``universe'' include ``basisUniverse'' and ``runtimeUniverse''. + * Possible values for ``mirror'' include ``EmptyTree'' (in that case the reifier will automatically pick an appropriate mirror). + * * This function is deeply connected to ``Universe.reify'', a macro that reifies arbitrary expressions into runtime trees. * They do very similar things (``Universe.reify'' calls ``Context.reifyTree'' to implement itself), but they operate on different metalevels (see below). * @@ -40,19 +48,24 @@ trait Reifiers { * Typical usage of this function is to retain some of the trees received/created by a macro * into the form that can be inspected (via pattern matching) or compiled/run (by a reflective ToolBox) during the runtime. */ - def reifyTree(prefix: Tree, tree: Tree): Tree + def reifyTree(universe: Tree, mirror: Tree, tree: Tree): Tree /** Given a type, generate a tree that when compiled and executed produces the original type. - * The produced tree will be bound to the mirror specified by ``prefix'' (also see ``reflectMirrorPrefix''). + * The produced tree will be bound to the specified ``universe'' and ``mirror''. * For more information and examples see the documentation for ``Context.reifyTree'' and ``Universe.reify''. */ - def reifyType(prefix: Tree, tpe: Type, dontSpliceAtTopLevel: Boolean = false, concrete: Boolean = false): Tree + def reifyType(universe: Tree, mirror: Tree, tpe: Type, concrete: Boolean = false): Tree /** Given a type, generate a tree that when compiled and executed produces the runtime class of the original type. * If ``concrete'' is true, then this function will bail on types, who refer to abstract types (like `ClassTag` does). */ def reifyRuntimeClass(tpe: Type, concrete: Boolean = true): Tree + /** Given a type, generate a tree that when compiled and executed produces the runtime class of the enclosing class or module. + * Returns `EmptyTree` if there does not exist an enclosing class or module. + */ + def reifyEnclosingRuntimeClass: Tree + /** Undoes reification of a tree. * * This reversion doesn't simply restore the original tree (that would lose the context of reification), -- cgit v1.2.3