From 5e4c47f33b8e25feb59ab4599231b1b8d3150de8 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Sat, 14 Apr 2012 21:11:37 +0200 Subject: implements reification of tough types --- src/compiler/scala/reflect/internal/StdNames.scala | 1 + src/compiler/scala/reflect/internal/Symbols.scala | 4 +- src/compiler/scala/reflect/internal/TreeInfo.scala | 16 +-- src/compiler/scala/reflect/internal/Types.scala | 33 ++++- .../reflect/reify/codegen/AnnotationInfos.scala | 56 ++++++++ .../scala/reflect/reify/codegen/Symbols.scala | 114 +++++++++++++---- .../scala/reflect/reify/codegen/Trees.scala | 20 ++- .../scala/reflect/reify/codegen/Types.scala | 141 ++++++--------------- .../scala/reflect/reify/phases/Metalevels.scala | 2 + .../scala/reflect/reify/phases/Reify.scala | 4 + src/compiler/scala/tools/nsc/util/Position.scala | 10 +- src/library/scala/reflect/api/Attachment.scala | 10 +- src/library/scala/reflect/api/StandardNames.scala | 1 + src/library/scala/reflect/api/Symbols.scala | 5 + src/library/scala/reflect/api/Trees.scala | 18 ++- src/library/scala/reflect/api/TypeTags.scala | 7 +- src/library/scala/reflect/api/Types.scala | 4 + 17 files changed, 296 insertions(+), 150 deletions(-) create mode 100644 src/compiler/scala/reflect/reify/codegen/AnnotationInfos.scala (limited to 'src') diff --git a/src/compiler/scala/reflect/internal/StdNames.scala b/src/compiler/scala/reflect/internal/StdNames.scala index ac2cf178bf..bf468affe6 100644 --- a/src/compiler/scala/reflect/internal/StdNames.scala +++ b/src/compiler/scala/reflect/internal/StdNames.scala @@ -206,6 +206,7 @@ trait StdNames extends NameManglers { self: SymbolTable => val MIRROR_FREE_PREFIX: NameType = "free$" val MIRROR_FREE_THIS_SUFFIX: NameType = "$this" val MIRROR_FREE_VALUE_SUFFIX: NameType = "$value" + val MIRROR_SYMDEF_PREFIX: NameType = "symdef$" val MIXIN_CONSTRUCTOR: NameType = "$init$" val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" val OUTER: NameType = "$outer" diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 3bb57cea04..c9947c3c09 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -64,6 +64,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => this: Symbol => def kind: String = kindString + def isExistential: Boolean = this.isExistentiallyBound def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { case n: TermName => newTermSymbol(n, pos, newFlags) @@ -897,8 +898,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (!owner.isLocatable) return false if (owner.isTerm) return false + if (isLocalDummy) return false if (isType && isNonClassType) return false + if (isRefinementClass) return false return true } @@ -2965,7 +2968,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def isFreeTerm = true } - // [Eugene] the NoSymbol origin works for type parameters. what about existential free types? class FreeType(name0: TypeName, value0: => Any, val origin: String) extends TypeSkolem(NoSymbol, NoPosition, name0, NoSymbol) { def value = value0 override def isFreeType = true diff --git a/src/compiler/scala/reflect/internal/TreeInfo.scala b/src/compiler/scala/reflect/internal/TreeInfo.scala index a8cca1625f..039c8e557a 100644 --- a/src/compiler/scala/reflect/internal/TreeInfo.scala +++ b/src/compiler/scala/reflect/internal/TreeInfo.scala @@ -590,7 +590,7 @@ abstract class TreeInfo { } object Reified { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + 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) => @@ -601,16 +601,16 @@ abstract class TreeInfo { } object ReifiedTree { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree)] = tree match { case reifee @ Block((mrDef @ ValDef(_, _, _, _)) :: symbolTable, Apply(Apply(_, List(tree)), List(Apply(_, List(tpe))))) if mrDef.name == nme.MIRROR_SHORT => - Some(reifee, symbolTable map (_.asInstanceOf[ValDef]), tree, tpe) + Some(reifee, symbolTable, tree, tpe) case _ => None } } object InlineableTreeSplice { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree, Symbol)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree, Symbol)] = tree match { case select @ Select(ReifiedTree(splicee, symbolTable, tree, tpe), _) if select.symbol == ExprEval || select.symbol == ExprValue => Some(splicee, symbolTable, tree, tpe, select.symbol) case _ => @@ -619,7 +619,7 @@ abstract class TreeInfo { } object InlinedTreeSplice { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree, Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree, Tree)] = tree match { case Select(ReifiedTree(splicee, symbolTable, tree, tpe), name) if name == ExprTree.name => Some(splicee, symbolTable, tree, tpe) case _ => @@ -628,16 +628,16 @@ abstract class TreeInfo { } object ReifiedType { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree)] = tree match { case reifee @ Block((mrDef @ ValDef(_, _, _, _)) :: symbolTable, Apply(_, List(tpe))) if mrDef.name == nme.MIRROR_SHORT => - Some(reifee, symbolTable map (_.asInstanceOf[ValDef]), tpe) + Some(reifee, symbolTable, tpe) case _ => None } } object InlinedTypeSplice { - def unapply(tree: Tree): Option[(Tree, List[ValDef], Tree)] = tree match { + def unapply(tree: Tree): Option[(Tree, List[Tree], Tree)] = tree match { case Select(ReifiedType(splicee, symbolTable, tpe), name) if name == TypeTagTpe.name => Some(splicee, symbolTable, tpe) case _ => diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 3efbe4b4df..d7c90d597c 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -266,6 +266,35 @@ trait Types extends api.Types { self: SymbolTable => def typeArguments = typeArgs def erasure = transformedType(this) def substituteTypes(from: List[Symbol], to: List[Type]): Type = subst(from, to) + + // [Eugene] to be discussed and refactored + def isConcrete = { + def notConcreteSym(sym: Symbol) = + sym.isAbstractType && !sym.isExistential + + def notConcreteTpe(tpe: Type): Boolean = tpe match { + case ThisType(_) => false + case SuperType(_, _) => false + case SingleType(pre, sym) => notConcreteSym(sym) + case ConstantType(_) => false + case TypeRef(_, sym, _) => notConcreteSym(sym) + case RefinedType(_, _) => false + case ExistentialType(_, _) => false + case AnnotatedType(_, tp, _) => notConcreteTpe(tp) + case _ => true + } + + !notConcreteTpe(this) + } + + // [Eugene] is this comprehensive? + // the only thingies that we want to splice are: 1) type parameters, 2) type members + // the thingies that we don't want to splice are: 1) concrete types (obviously), 2) existential skolems + // this check seems to cover them all, right? + // todo. after we discuss this, move the check to subclasses + def isSpliceable = { + this.isInstanceOf[TypeRef] && typeSymbol.isAbstractType && !typeSymbol.isExistential + } } /** The base class for all types */ @@ -2147,7 +2176,7 @@ trait Types extends api.Types { self: SymbolTable => sym.isPackageClass || pre.isGround && args.forall(_.isGround) ) - + def etaExpand: Type = { // must initialise symbol, see test/files/pos/ticket0137.scala val tpars = initializedTypeParams @@ -2763,7 +2792,7 @@ trait Types extends api.Types { self: SymbolTable => zippedArgs map { case (p, a) => p.name + "=" + a } mkString (origin + "[", ", ", "]") ) } - + trait UntouchableTypeVar extends TypeVar { override def untouchable = true override def isGround = true 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 diff --git a/src/compiler/scala/reflect/reify/phases/Metalevels.scala b/src/compiler/scala/reflect/reify/phases/Metalevels.scala index a329a1043d..bb0b8ac138 100644 --- a/src/compiler/scala/reflect/reify/phases/Metalevels.scala +++ b/src/compiler/scala/reflect/reify/phases/Metalevels.scala @@ -134,6 +134,8 @@ trait Metalevels { // 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 => if (reifyDebug) println("inlineable free ref: %s in %s".format(name, showRaw(tree))) val freedef @ FreeDef(_, _, binding, _) = freedefsToInline(name) diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index f6d6423605..02a96987ed 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -9,6 +9,7 @@ trait Reify extends Symbols with Types with Names with Trees + with AnnotationInfos with Positions with Util { @@ -30,6 +31,9 @@ trait Reify extends Symbols case tpe: Type => reifyType(tpe) case name: Name => reifyName(name) 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 pos: Position => reifyPosition(pos) case mods: mirror.Modifiers => reifyModifiers(mods) case xs: List[_] => reifyList(xs) diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index 573f7bc7b2..208cd5703a 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -40,7 +40,15 @@ trait Position extends scala.reflect.api.Position with scala.reflect.api.Attachm /** A bit weird method that is necessary to safely update positions without destroying custom attachments */ // necessary for conformance with Attachment - def withPos(pos: scala.reflect.api.Position) = pos + def withPos(newPos: scala.reflect.api.Position): scala.reflect.api.Attachment = newPos + + /** Exposes itself as payload of Attachment */ + // necessary for conformance with Attachment + def payload: Position = this + + /** A bit weird method that is necessary to safely update positions without destroying custom attachments */ + // necessary for conformance with Attachment + def withPayload(newPos: Any): scala.reflect.api.Attachment = newPos.asInstanceOf[Position] /** Java file corresponding to the source file of this position. */ diff --git a/src/library/scala/reflect/api/Attachment.scala b/src/library/scala/reflect/api/Attachment.scala index dfd362ebe0..9fa5ceb0fb 100644 --- a/src/library/scala/reflect/api/Attachment.scala +++ b/src/library/scala/reflect/api/Attachment.scala @@ -7,10 +7,18 @@ package api * Attachments have to carry positions, because we don't want to introduce even a single additional field in Tree * imposing an unnecessary memory tax because of something that will not be used in most cases. */ +// [Eugene] with the introduction of `attach` and `payload[T]` users don't need to create custom attachments anymore +// however, we cannot move attachments to scala.reflect.internal, because they are used in Trees, which are implemented completely in scala.reflect.api trait Attachment { /** Gets the underlying position */ def pos: Position /** Creates a copy of this attachment with its position updated */ - def withPos(pos: Position): Attachment + def withPos(newPos: Position): Attachment + + /** Gets the underlying payload */ + def payload: Any + + /** Creates a copy of this attachment with its payload updated */ + def withPayload(newPayload: Any): Attachment } diff --git a/src/library/scala/reflect/api/StandardNames.scala b/src/library/scala/reflect/api/StandardNames.scala index d2110ede75..d39d44dd86 100644 --- a/src/library/scala/reflect/api/StandardNames.scala +++ b/src/library/scala/reflect/api/StandardNames.scala @@ -44,6 +44,7 @@ trait StandardNames { self: Universe => val MIRROR_FREE_PREFIX: TermName val MIRROR_FREE_THIS_SUFFIX: TermName val MIRROR_FREE_VALUE_SUFFIX: TermName + val MIRROR_SYMDEF_PREFIX: TermName val MIXIN_CONSTRUCTOR: TermName val MODULE_INSTANCE_FIELD: TermName val OUTER: TermName diff --git a/src/library/scala/reflect/api/Symbols.scala b/src/library/scala/reflect/api/Symbols.scala index a154e5f7a0..e47bc7216e 100755 --- a/src/library/scala/reflect/api/Symbols.scala +++ b/src/library/scala/reflect/api/Symbols.scala @@ -173,6 +173,11 @@ trait Symbols { self: Universe => */ def isSkolem : Boolean + /** Does this symbol represent an existentially bound type? + * If yes, `isType` is also guaranteed to be true. + */ + def isExistential : Boolean + /** Does this symbol represent a free type captured by reification? */ // needed for ones who wish to inspect reified trees diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala index 6ddb2ea673..5ef73cba0c 100644 --- a/src/library/scala/reflect/api/Trees.scala +++ b/src/library/scala/reflect/api/Trees.scala @@ -85,10 +85,18 @@ trait Trees { self: Universe => def pos_=(pos: Position): Unit = rawatt = (rawatt withPos pos) // the "withPos" part is crucial to robustness def setPos(newpos: Position): this.type = { pos = newpos; this } - private[this] var rawatt: Attachment = NoPosition - def attachment: Attachment = rawatt - def attachment_=(att: Attachment): Unit = rawatt = att - def setAttachment(att: Attachment): this.type = { rawatt = att; this } + private var rawatt: Attachment = NoPosition + private case class NontrivialAttachment(pos: api.Position, payload: Any) extends Attachment { + def withPos(newPos: api.Position) = copy(pos = newPos, payload = payload) + def withPayload(newPayload: Any) = copy(pos = pos, payload = newPayload) + } + // todo. annotate T with ClassTag and make pattern matcher use it + // todo. support multiple attachments, and remove the assignment. only leave attach/detach +// def attachment[T]: T = rawatt.payload.asInstanceOf[T] +// def attachmentOpt[T]: Option[T] = try { Some(rawatt.payload.asInstanceOf[T]) } catch { case _: Throwable => None } + def attachment: Any = rawatt.payload + def attachment_=(att: Any): Unit = rawatt = NontrivialAttachment(pos, att) + def setAttachment(att: Any): this.type = { attachment = att; this } private[this] var rawtpe: Type = _ @@ -238,7 +246,7 @@ trait Trees { self: Universe => duplicateTree(this).asInstanceOf[this.type] private[scala] def copyAttrs(tree: Tree): this.type = { - attachment = tree.attachment + rawatt = tree.rawatt tpe = tree.tpe if (hasSymbol) symbol = tree.symbol this diff --git a/src/library/scala/reflect/api/TypeTags.scala b/src/library/scala/reflect/api/TypeTags.scala index 4ffabe1c36..59a7c87f44 100644 --- a/src/library/scala/reflect/api/TypeTags.scala +++ b/src/library/scala/reflect/api/TypeTags.scala @@ -61,9 +61,8 @@ trait TypeTags { self: Universe => // assert(tpe != null) def sym = tpe.typeSymbol - - def isConcrete = !isNotConcrete - def isNotConcrete = tpe exists (_.typeSymbol.isAbstractType) + def isConcrete = tpe.isConcrete + def notConcrete = !isConcrete def toConcrete: ConcreteTypeTag[T] = ConcreteTypeTag[T](tpe) override def toString = { @@ -124,7 +123,7 @@ trait TypeTags { self: Universe => // it's unsafe to use assert here, because we might run into deadlocks with Predef // also see comments in ClassTags.scala //assert(isConcrete, tpe) - if (isNotConcrete) throw new Error("%s (%s) is not concrete and cannot be used to construct a concrete type tag".format(tpe, tpe.kind)) + if (notConcrete) throw new Error("%s (%s) is not concrete and cannot be used to construct a concrete type tag".format(tpe, tpe.kind)) override def productPrefix = "ConcreteTypeTag" } diff --git a/src/library/scala/reflect/api/Types.scala b/src/library/scala/reflect/api/Types.scala index 12aad453b1..5e1c1af2fe 100755 --- a/src/library/scala/reflect/api/Types.scala +++ b/src/library/scala/reflect/api/Types.scala @@ -57,6 +57,10 @@ trait Types { self: Universe => */ def isHigherKinded: Boolean // !!! This should be called "isTypeConstructor", no? + /** Does this type refer to abstract types or is an abstract type? + */ + def isConcrete: Boolean + /** * Expands type aliases and converts higher-kinded TypeRefs to PolyTypes. * Functions on types are also implemented as PolyTypes. -- cgit v1.2.3