Skip to content

Commit

Permalink
reifyAnnotations
Browse files Browse the repository at this point in the history
Annotations are now supported by the reifier:
* AnnotationInfos from symbols get transformed back into mods.
* AnnotatedTypes are retained and are reified along with AnnotationInfos.

Reification is no magic, and reification of annotations especially:
* Annotations cannot refer to symbols defined inside the quasiquote.
  This restriction is due to the fact that we need to erase locally defined
  symbols before reifying to make subsequent reflective compilations succeed.
  However, while doing that, we also need to make sure that we don't make
  resulting ASTs non-compilable by removing essential information.
  This is tricky, and it more or less works for TypeTrees, but
  not for annotations that can contain arbitrary ASTs.
  For more details look into the comments to Reifiers.scala.
* Classfile annotations that contain array arguments and are applied to types,
  i.e. the ones that generate AnnotatedTypes, cannot be reified.
  This is because of limitations of manifest infrastructure.
  Typechecking "Array(mirror.LiteralAnnotArg(...))" would require the compiler
  to produce a manifest for a path-dependent type, which cannot be done now.

Review by @odersky.
  • Loading branch information
xeno-by committed Feb 12, 2012
1 parent 6b56405 commit 6548dcf
Show file tree
Hide file tree
Showing 36 changed files with 1,129 additions and 531 deletions.
17 changes: 15 additions & 2 deletions src/compiler/scala/reflect/internal/AnnotationInfos.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable =>
// Classfile annot: args empty. Scala annot: assocs empty.
assert(args.isEmpty || assocs.isEmpty, atp)

// @xeno.by: necessary for reification, see Reifiers.scala for more info
private var orig: Tree = EmptyTree
def original = orig
def setOriginal(t: Tree): this.type = { orig = t; this }

override def toString = (
atp +
(if (!args.isEmpty) args.mkString("(", ", ", ")") else "") +
Expand All @@ -130,18 +135,20 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable =>
private var forced = false
private lazy val forcedInfo =
try {
val result = lazyInfo
val result = lazyInfo
if (result.pos == NoPosition) result setPos pos
result
} finally forced = true

def atp: Type = forcedInfo.atp
def args: List[Tree] = forcedInfo.args
def assocs: List[(Name, ClassfileAnnotArg)] = forcedInfo.assocs
def original: Tree = forcedInfo.original
def setOriginal(t: Tree): this.type = { forcedInfo.setOriginal(t); this }

// We should always be able to print things without forcing them.
override def toString = if (forced) forcedInfo.toString else "@<?>"

override def pos: Position = if (forced) forcedInfo.pos else NoPosition
}

Expand All @@ -166,10 +173,16 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable =>
def args: List[Tree]
def assocs: List[(Name, ClassfileAnnotArg)]

// @xeno.by: necessary for reification, see Reifiers.scala for more info
def original: Tree
def setOriginal(t: Tree): this.type

/** Hand rolling Product. */
def _1 = atp
def _2 = args
def _3 = assocs
// @xeno.by: original hasn't become a product member for backward compatibility purposes
// def _4 = original
def canEqual(other: Any) = other.isInstanceOf[AnnotationInfo]
override def productPrefix = "AnnotationInfo"

Expand Down
196 changes: 125 additions & 71 deletions src/compiler/scala/reflect/internal/Importers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,25 @@ trait Importers { self: SymbolTable =>
val from: SymbolTable

lazy val symMap: WeakHashMap[from.Symbol, Symbol] = new WeakHashMap
lazy val tpeMap: WeakHashMap[from.Type, Type] = new WeakHashMap

// fixups and maps prevent stackoverflows in importer
var pendingSyms = 0
var pendingTpes = 0
lazy val fixups = collection.mutable.MutableList[Function0[Unit]]()
def addFixup(fixup: => Unit): Unit = fixups += (() => fixup)
def tryFixup(): Unit = {
if (pendingSyms == 0 && pendingTpes == 0) {
val fixups = this.fixups.toList
this.fixups.clear()
fixups foreach { _() }
}
}

object reverse extends from.Importer {
val from: self.type = self
for ((fromsym, mysym) <- Importer.this.symMap) symMap += ((mysym, fromsym))
for ((fromtpe, mytpe) <- Importer.this.tpeMap) tpeMap += ((mytpe, fromtpe))
}

def importPosition(pos: from.Position): Position = NoPosition
Expand Down Expand Up @@ -78,7 +93,7 @@ trait Importers { self: SymbolTable =>
mysym resetFlag Flags.LOCKED
} // end doImport

def importOrRelink: Symbol =
def importOrRelink: Symbol = {
if (sym == null)
null
else if (sym == from.NoSymbol)
Expand Down Expand Up @@ -129,69 +144,103 @@ trait Importers { self: SymbolTable =>
} else
doImport(sym)
}
symMap getOrElseUpdate (sym, importOrRelink)
} // end importOrRelink

if (symMap contains sym) {
symMap(sym)
} else {
pendingSyms += 1

try {
symMap getOrElseUpdate (sym, importOrRelink)
} finally {
pendingSyms -= 1
tryFixup()
}
}
}

def importType(tpe: from.Type): Type = tpe match {
case from.TypeRef(pre, sym, args) =>
TypeRef(importType(pre), importSymbol(sym), args map importType)
case from.ThisType(clazz) =>
ThisType(importSymbol(clazz))
case from.SingleType(pre, sym) =>
SingleType(importType(pre), importSymbol(sym))
case from.MethodType(params, restpe) =>
MethodType(params map importSymbol, importType(restpe))
case from.PolyType(tparams, restpe) =>
PolyType(tparams map importSymbol, importType(restpe))
case from.NullaryMethodType(restpe) =>
NullaryMethodType(importType(restpe))
case from.ConstantType(constant @ from.Constant(_)) =>
ConstantType(importConstant(constant))
case from.SuperType(thistpe, supertpe) =>
SuperType(importType(thistpe), importType(supertpe))
case from.TypeBounds(lo, hi) =>
TypeBounds(importType(lo), importType(hi))
case from.BoundedWildcardType(bounds) =>
BoundedWildcardType(importTypeBounds(bounds))
case from.ClassInfoType(parents, decls, clazz) =>
val myclazz = importSymbol(clazz)
val myscope = if (myclazz.isPackageClass) newPackageScope(myclazz) else newScope
val myclazzTpe = ClassInfoType(parents map importType, myscope, myclazz)
myclazz setInfo polyType(myclazz.typeParams, myclazzTpe) // needed so that newly created symbols find their scope
decls foreach importSymbol // will enter itself into myclazz
myclazzTpe
case from.RefinedType(parents, decls) =>
RefinedType(parents map importType, importScope(decls), importSymbol(tpe.typeSymbol))
case from.ExistentialType(tparams, restpe) =>
newExistentialType(tparams map importSymbol, importType(restpe))
case from.OverloadedType(pre, alts) =>
OverloadedType(importType(pre), alts map importSymbol)
case from.AntiPolyType(pre, targs) =>
AntiPolyType(importType(pre), targs map importType)
case x: from.TypeVar =>
TypeVar(importType(x.origin), importTypeConstraint(x.constr0), x.typeArgs map importType, x.params map importSymbol)
case from.NotNullType(tpe) =>
NotNullType(importType(tpe))
case from.AnnotatedType(annots, tpe, selfsym) =>
AnnotatedType(annots map importAnnotationInfo, importType(tpe), importSymbol(selfsym))
case from.ErrorType =>
ErrorType
case from.WildcardType =>
WildcardType
case from.NoType =>
NoType
case from.NoPrefix =>
NoPrefix
case null =>
null
def importType(tpe: from.Type): Type = {
def doImport(tpe: from.Type): Type = tpe match {
case from.TypeRef(pre, sym, args) =>
TypeRef(importType(pre), importSymbol(sym), args map importType)
case from.ThisType(clazz) =>
ThisType(importSymbol(clazz))
case from.SingleType(pre, sym) =>
SingleType(importType(pre), importSymbol(sym))
case from.MethodType(params, restpe) =>
MethodType(params map importSymbol, importType(restpe))
case from.PolyType(tparams, restpe) =>
PolyType(tparams map importSymbol, importType(restpe))
case from.NullaryMethodType(restpe) =>
NullaryMethodType(importType(restpe))
case from.ConstantType(constant @ from.Constant(_)) =>
ConstantType(importConstant(constant))
case from.SuperType(thistpe, supertpe) =>
SuperType(importType(thistpe), importType(supertpe))
case from.TypeBounds(lo, hi) =>
TypeBounds(importType(lo), importType(hi))
case from.BoundedWildcardType(bounds) =>
BoundedWildcardType(importTypeBounds(bounds))
case from.ClassInfoType(parents, decls, clazz) =>
val myclazz = importSymbol(clazz)
val myscope = if (myclazz.isPackageClass) newPackageScope(myclazz) else newScope
val myclazzTpe = ClassInfoType(parents map importType, myscope, myclazz)
myclazz setInfo polyType(myclazz.typeParams, myclazzTpe) // needed so that newly created symbols find their scope
decls foreach importSymbol // will enter itself into myclazz
myclazzTpe
case from.RefinedType(parents, decls) =>
RefinedType(parents map importType, importScope(decls), importSymbol(tpe.typeSymbol))
case from.ExistentialType(tparams, restpe) =>
newExistentialType(tparams map importSymbol, importType(restpe))
case from.OverloadedType(pre, alts) =>
OverloadedType(importType(pre), alts map importSymbol)
case from.AntiPolyType(pre, targs) =>
AntiPolyType(importType(pre), targs map importType)
case x: from.TypeVar =>
TypeVar(importType(x.origin), importTypeConstraint(x.constr0), x.typeArgs map importType, x.params map importSymbol)
case from.NotNullType(tpe) =>
NotNullType(importType(tpe))
case from.AnnotatedType(annots, tpe, selfsym) =>
AnnotatedType(annots map importAnnotationInfo, importType(tpe), importSymbol(selfsym))
case from.ErrorType =>
ErrorType
case from.WildcardType =>
WildcardType
case from.NoType =>
NoType
case from.NoPrefix =>
NoPrefix
case null =>
null
} // end doImport

def importOrRelink: Type =
doImport(tpe)

if (tpeMap contains tpe) {
tpeMap(tpe)
} else {
pendingTpes += 1

try {
tpeMap getOrElseUpdate (tpe, importOrRelink)
} finally {
pendingTpes -= 1
tryFixup()
}
}
}

def importTypeBounds(bounds: from.TypeBounds) = importType(bounds).asInstanceOf[TypeBounds]

def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo =
AnnotationInfo(importType(ann.atp), ann.args map importTree, ann.assocs map {
case (name, arg) => (importName(name), importAnnotArg(arg))
})
def importAnnotationInfo(ann: from.AnnotationInfo): AnnotationInfo = {
val atp1 = importType(ann.atp)
val args1 = ann.args map importTree
val assocs1 = ann.assocs map { case (name, arg) => (importName(name), importAnnotArg(arg)) }
val original1 = importTree(ann.original)
AnnotationInfo(atp1, args1, assocs1) setOriginal original1
}

def importAnnotArg(arg: from.ClassfileAnnotArg): ClassfileAnnotArg = arg match {
case from.LiteralAnnotArg(constant @ from.Constant(_)) =>
Expand Down Expand Up @@ -265,6 +314,8 @@ trait Importers { self: SymbolTable =>
new Function(vparams map importValDef, importTree(body))
case from.Assign(lhs, rhs) =>
new Assign(importTree(lhs), importTree(rhs))
case from.AssignOrNamedArg(lhs, rhs) =>
new AssignOrNamedArg(importTree(lhs), importTree(rhs))
case from.If(cond, thenp, elsep) =>
new If(importTree(cond), importTree(thenp), importTree(elsep))
case from.Match(selector, cases) =>
Expand Down Expand Up @@ -326,21 +377,24 @@ trait Importers { self: SymbolTable =>
case null =>
null
}
if (mytree != null) {
val mysym = if (tree hasSymbol) importSymbol(tree.symbol) else NoSymbol
val mytpe = importType(tree.tpe)
addFixup({
if (mytree != null) {
val mysym = if (tree hasSymbol) importSymbol(tree.symbol) else NoSymbol
val mytpe = importType(tree.tpe)

mytree match {
case mytt: TypeTree =>
val tt = tree.asInstanceOf[from.TypeTree]
if (mytree hasSymbol) mytt.symbol = mysym
if (tt.wasEmpty) mytt.defineType(mytpe) else mytt.setType(mytpe)
if (tt.original != null) mytt.setOriginal(importTree(tt.original))
case _ =>
if (mytree hasSymbol) mytree.symbol = importSymbol(tree.symbol)
mytree.tpe = importType(tree.tpe)
mytree match {
case mytt: TypeTree =>
val tt = tree.asInstanceOf[from.TypeTree]
if (mytree hasSymbol) mytt.symbol = mysym
if (tt.wasEmpty) mytt.defineType(mytpe) else mytt.setType(mytpe)
if (tt.original != null) mytt.setOriginal(importTree(tt.original))
case _ =>
if (mytree hasSymbol) mytree.symbol = importSymbol(tree.symbol)
mytree.tpe = importType(tree.tpe)
}
}
}
})
tryFixup()
mytree
}

Expand Down
3 changes: 3 additions & 0 deletions src/compiler/scala/reflect/internal/TreePrinters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable =>
case Assign(lhs, rhs) =>
print(lhs, " = ", rhs)

case AssignOrNamedArg(lhs, rhs) =>
print(lhs, " = ", rhs)

case If(cond, thenp, elsep) =>
print("if (", cond, ")"); indent; println()
print(thenp); undent
Expand Down
Loading

0 comments on commit 6548dcf

Please sign in to comment.