diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-04-14 21:11:37 +0200 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-04-14 21:11:37 +0200 |
commit | 5e4c47f33b8e25feb59ab4599231b1b8d3150de8 (patch) | |
tree | ff2ded10d14c5a6e859ac216e60244029a17a505 /src/compiler/scala/reflect/reify/codegen | |
parent | 3a2901da406f2478b5634b0636e56de9c4cd676d (diff) | |
download | scala-5e4c47f33b8e25feb59ab4599231b1b8d3150de8.tar.gz scala-5e4c47f33b8e25feb59ab4599231b1b8d3150de8.tar.bz2 scala-5e4c47f33b8e25feb59ab4599231b1b8d3150de8.zip |
implements reification of tough types
Diffstat (limited to 'src/compiler/scala/reflect/reify/codegen')
4 files changed, 203 insertions, 128 deletions
diff --git a/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala b/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala new file mode 100644 index 0000000000..1d218317dc --- /dev/null +++ b/src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala @@ -0,0 +1,56 @@ +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/Symbols.scala b/src/compiler/scala/reflect/reify/codegen/Symbols.scala index 3328f5e402..2fc0002838 100644 --- a/src/compiler/scala/reflect/reify/codegen/Symbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/Symbols.scala @@ -46,13 +46,8 @@ trait Symbols { } } else { // todo. make sure that free methods and free local defs work correctly - if (sym.isTerm) { - if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym) - reifyFreeTerm(sym, Ident(sym)) - } else { - if (reifyDebug) println("Free type: " + sym) - reifyFreeType(sym, Ident(sym)) - } + if (sym.isTerm) reifyFreeTerm(sym, Ident(sym)) + else reifyFreeType(sym, Ident(sym)) } } @@ -61,13 +56,16 @@ trait Symbols { 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, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(capturedTpe), capturedValue, reify(origin(sym)))) + locallyReify(sym, name, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(capturedTpe), capturedValue, reify(origin(sym)))) } else { - locallyReify(sym, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(sym.tpe), value, reify(origin(sym)))) + locallyReify(sym, name, mirrorCall(nme.newFreeTerm, reify(sym.name.toString), reify(sym.tpe), value, reify(origin(sym)))) } } @@ -76,36 +74,104 @@ trait Symbols { 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)))) // todo. implement info reification for free types: type bounds, HK-arity, whatever else that can be useful - locallyReify(sym, mirrorCall(nme.newFreeType, reify(sym.name.toString), reify(sym.info), phantomTypeTag, reify(origin(sym)))) + locallyReify(sym, name, mirrorCall(nme.newFreeType, reify(sym.name.toString), reify(sym.info), phantomTypeTag, 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[ValDef]() + private val localReifications = ArrayBuffer[Tree]() private val locallyReified = Map[Symbol, Tree]() - def symbolTable: List[ValDef] = localReifications.toList - def symbolTable_=(newSymbolTable: List[ValDef]): Unit = { + 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 freedef @ FreeDef(_, name, binding, _) => - if (!(locallyReified contains binding.symbol)) { - localReifications += freedef - locallyReified(binding.symbol) = Ident(name) + case entry => + val att = entry.attachment + att match { + case sym: Symbol => + // 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 locallyReify(sym: Symbol, reificode: => Tree): Tree = { + 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 Apply(Select(_, flavor), _) = reified - // [Eugene] name clashes are impossible, right? - var name = newTermName(nme.MIRROR_FREE_PREFIX + sym.name) - if (flavor == nme.newFreeTerm && sym.isType) name = name.append(nme.MIRROR_FREE_THIS_SUFFIX); - // todo. also reify annotations for free vars - localReifications += ValDef(NoMods, name, TypeTree(), reified) + 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) setAttachment 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) + reified.attachment match { + case sym: Symbol => 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 { + val rset = Apply(Select(locallyReified(sym), nme.setTypeSignature), List(reifyType(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 index 22f42aea49..5ad53c0009 100644 --- a/src/compiler/scala/reflect/reify/codegen/Trees.scala +++ b/src/compiler/scala/reflect/reify/codegen/Trees.scala @@ -8,6 +8,11 @@ trait Trees { 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. @@ -59,6 +64,17 @@ trait Trees { 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(reifySymRef(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(reifyType(tree.tpe))) + } + rtree } @@ -82,7 +98,7 @@ trait Trees { case InlinedTreeSplice(_, inlinedSymbolTable, tree, _) => if (reifyDebug) println("inlining the splicee") // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels'' - inlinedSymbolTable foreach { case freedef @ FreeDef(_, _, binding, _) => assert(!binding.symbol.isLocalToReifee, freedef) } + inlinedSymbolTable collect { case freedef @ FreeDef(_, _, binding, _) if binding.symbol.isLocalToReifee => assert(false, freedef) } symbolTable ++= inlinedSymbolTable tree case tree => @@ -152,7 +168,7 @@ trait Trees { val tpe = tpe0.dealias if (reifyDebug) println("reifying bound type %s (underlying type is %s, dealiased is %s)".format(sym0, tpe0, tpe)) - if (eligibleForSplicing(tpe)) { + if (tpe.isSpliceable) { val spliced = spliceType(tpe) if (spliced == EmptyTree) { if (reifyDebug) println("splicing failed: reify as is") diff --git a/src/compiler/scala/reflect/reify/codegen/Types.scala b/src/compiler/scala/reflect/reify/codegen/Types.scala index 9bc113e8a4..948728088e 100644 --- a/src/compiler/scala/reflect/reify/codegen/Types.scala +++ b/src/compiler/scala/reflect/reify/codegen/Types.scala @@ -55,11 +55,9 @@ trait Types { case tpe @ NullaryMethodType(restpe) => reifyProduct(tpe) case tpe @ AnnotatedType(anns, underlying, selfsym) => -// reifyAnnotatedType(tpe) - CannotReifyType(tpe) + reifyAnnotatedType(tpe) case _ => -// reifyToughType(tpe) - CannotReifyType(tpe) + reifyToughType(tpe) } } @@ -70,13 +68,6 @@ trait Types { var maybeConcrete = true var definitelyConcrete = true - def eligibleForSplicing(tpe: Type): Boolean = { - // [Eugene] is this comprehensive? - // the only thingies that we want to splice are: 1) type parameters, 2) type members - // this check seems to cover them all, right? - tpe.isInstanceOf[TypeRef] && tpe.typeSymbol.isAbstractType - } - private type SpliceCacheKey = (Symbol, Symbol) private lazy val spliceCache: collection.mutable.Map[SpliceCacheKey, Tree] = { val cache = analyzer.perRunMacroCache.getOrElseUpdate(MacroContextReify, collection.mutable.Map[Any, Any]()) @@ -84,7 +75,7 @@ trait Types { } def spliceType(tpe: Type): Tree = { - if (eligibleForSplicing(tpe)) { + if (tpe.isSpliceable) { if (reifyDebug) println("splicing " + tpe) if (spliceTypesEnabled) { @@ -113,7 +104,7 @@ trait Types { splice match { case InlinedTypeSplice(_, inlinedSymbolTable, tpe) => // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels'' - inlinedSymbolTable foreach { case freedef @ FreeDef(_, _, binding, _) => assert(!binding.symbol.isLocalToReifee, freedef) } + inlinedSymbolTable collect { case freedef @ FreeDef(_, _, binding, _) if binding.symbol.isLocalToReifee => assert(false, freedef) } symbolTable ++= inlinedSymbolTable reifyTrace("inlined the splicee: ")(tpe) case tpe => @@ -134,93 +125,39 @@ trait Types { EmptyTree } - // yet another thingie disabled for simplicity - // in principle, we could retain and reify AnnotatedTypes - // but that'd require reifying every type and symbol inside ann.args - // however, since we've given up on tough types for the moment, the former would be problematic -// private def reifyAnnotatedType(tpe: AnnotatedType): Tree = { -// // ``Reshaper'' transforms annotation infos from symbols back into Modifier.annotations, which are trees -// // so the only place on Earth that can lead to reification of AnnotationInfos is the Ay Tee Land -// // therefore this function is as local as possible, don't move it out of this scope -// def reifyAnnotationInfo(ann: AnnotationInfo): Tree = { -// val reifiedArgs = ann.args map { arg => -// val saved1 = reifyTreeSymbols -// val saved2 = reifyTreeTypes -// -// try { -// // one more quirk of reifying annotations -// // -// // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs -// // that's because a lot of logic expects post-typer trees to have non-null tpes -// // -// // Q: reified trees are pre-typer, so there's shouldn't be a problem. -// // reflective typechecker will fill in missing symbols and types, right? -// // A: actually, no. annotation ASTs live inside AnnotatedTypes, -// // and insides of the types is the place where typechecker doesn't look. -// reifyTreeSymbols = true -// reifyTreeTypes = true -// -// // todo. every AnnotationInfo is an island, entire of itself -// // no regular Traverser or Transformer can reach it -// // hence we need to run its contents through the entire reification pipeline -// // e.g. to apply reshaping or to check metalevels -// reify(arg) -// } finally { -// reifyTreeSymbols = saved1 -// reifyTreeTypes = saved2 -// } -// } -// -// def reifyClassfileAnnotArg(arg: ClassfileAnnotArg): Tree = arg match { -// case LiteralAnnotArg(const) => -// mirrorFactoryCall(nme.LiteralAnnotArg, reifyProduct(const)) -// case ArrayAnnotArg(args) => -// mirrorFactoryCall(nme.ArrayAnnotArg, scalaFactoryCall(nme.Array, args map reifyClassfileAnnotArg: _*)) -// case NestedAnnotArg(ann) => -// mirrorFactoryCall(nme.NestedAnnotArg, reifyAnnotationInfo(ann)) -// } -// -// // if you reify originals of anns, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important -// val reifiedAssocs = ann.assocs map (assoc => scalaFactoryCall(nme.Tuple2, reify(assoc._1), reifyClassfileAnnotArg(assoc._2))) -// mirrorFactoryCall(nme.AnnotationInfo, reify(ann.atp), mkList(reifiedArgs), mkList(reifiedAssocs)) -// } -// -// val AnnotatedType(anns, underlying, selfsym) = tpe -// mirrorFactoryCall(nme.AnnotatedType, mkList(anns map reifyAnnotationInfo), reify(underlying), reify(selfsym)) -// } - - // previous solution to reifying tough types involved creating dummy symbols (see ``registerReifiableSymbol'' calls below) - // however such symbols lost all the connections with their origins and became almost useless, except for typechecking - // hence this approach was replaced by less powerful, but more principled one based on ``reifyFreeType'' - // it's possible that later on we will revise and revive ``reifyToughType'', but for now it's disabled under an implementation restriction -// /** Reify a tough type, i.e. the one that leads to creation of auxiliary symbols */ -// // This is the uncharted territory in the reifier -// private def reifyToughType(tpe: Type): Tree = { -// if (reifyDebug) println("tough type: %s (%s)".format(tpe, tpe.kind)) -// -// def reifyScope(scope: Scope): Tree = { -// scope foreach registerReifiableSymbol -// mirrorCall(nme.newScopeWith, scope.toList map reify: _*) -// } -// -// tpe match { -// case tpe @ RefinedType(parents, decls) => -// registerReifiableSymbol(tpe.typeSymbol) -// mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) -// case tpe @ ExistentialType(tparams, underlying) => -// tparams foreach registerReifiableSymbol -// mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) -// case tpe @ ClassInfoType(parents, decls, clazz) => -// registerReifiableSymbol(clazz) -// mirrorFactoryCall(tpe, reify(parents), reifyScope(decls), reify(tpe.typeSymbol)) -// case tpe @ MethodType(params, restpe) => -// params foreach registerReifiableSymbol -// mirrorFactoryCall(tpe, reify(params), reify(restpe)) -// case tpe @ PolyType(tparams, underlying) => -// tparams foreach registerReifiableSymbol -// mirrorFactoryCall(tpe, reify(tparams), reify(underlying)) -// case _ => -// throw new Error("internal error: %s (%s) is not supported".format(tpe, tpe.kind)) -// } -// } + /** 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 |