summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/reflect/internal/AnnotationInfos.scala17
-rw-r--r--src/compiler/scala/reflect/internal/Importers.scala196
-rw-r--r--src/compiler/scala/reflect/internal/TreePrinters.scala3
-rw-r--r--src/compiler/scala/tools/nsc/ast/Reifiers.scala969
-rw-r--r--src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala22
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreePrinters.scala3
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala19
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala47
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala25
-rw-r--r--src/library/scala/reflect/api/Trees.scala20
-rw-r--r--test/files/neg/reify_ann2a.check4
-rw-r--r--test/files/neg/reify_ann2a.scala30
-rw-r--r--test/files/neg/reify_ann2b.check7
-rw-r--r--test/files/neg/reify_ann2b.scala30
-rw-r--r--test/files/run/reify_ann1a.check30
-rw-r--r--test/files/run/reify_ann1a.scala30
-rw-r--r--test/files/run/reify_ann1b.check30
-rw-r--r--test/files/run/reify_ann1b.scala30
-rw-r--r--test/files/run/reify_classfileann_a.check18
-rw-r--r--test/files/run/reify_classfileann_a.scala24
-rw-r--r--test/files/run/t5224.check (renamed from test/pending/run/t5224.check)4
-rw-r--r--test/files/run/t5224.scala9
-rw-r--r--test/files/run/t5225_1.check4
-rw-r--r--test/files/run/t5225_1.scala7
-rw-r--r--test/files/run/t5225_2.check4
-rw-r--r--test/files/run/t5225_2.scala7
-rw-r--r--test/files/run/t5419.check1
-rw-r--r--test/files/run/t5419.scala9
-rw-r--r--test/pending/run/reify_classfileann_b.check0
-rw-r--r--test/pending/run/reify_classfileann_b.scala28
-rw-r--r--test/pending/run/t5224.scala8
-rw-r--r--test/pending/run/t5225_1.check4
-rw-r--r--test/pending/run/t5225_1.scala8
-rw-r--r--test/pending/run/t5225_2.check4
-rw-r--r--test/pending/run/t5225_2.scala8
36 files changed, 1129 insertions, 531 deletions
diff --git a/src/compiler/scala/reflect/internal/AnnotationInfos.scala b/src/compiler/scala/reflect/internal/AnnotationInfos.scala
index c3dde3e6d1..9a7c79d856 100644
--- a/src/compiler/scala/reflect/internal/AnnotationInfos.scala
+++ b/src/compiler/scala/reflect/internal/AnnotationInfos.scala
@@ -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 "") +
@@ -130,7 +135,7 @@ 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
@@ -138,10 +143,12 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable =>
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
}
@@ -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"
diff --git a/src/compiler/scala/reflect/internal/Importers.scala b/src/compiler/scala/reflect/internal/Importers.scala
index 6c843e6f15..63efaede07 100644
--- a/src/compiler/scala/reflect/internal/Importers.scala
+++ b/src/compiler/scala/reflect/internal/Importers.scala
@@ -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
@@ -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)
@@ -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(_)) =>
@@ -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) =>
@@ -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
}
diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala
index e484faff2e..2b1d833c73 100644
--- a/src/compiler/scala/reflect/internal/TreePrinters.scala
+++ b/src/compiler/scala/reflect/internal/TreePrinters.scala
@@ -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
diff --git a/src/compiler/scala/tools/nsc/ast/Reifiers.scala b/src/compiler/scala/tools/nsc/ast/Reifiers.scala
index 105d2cb62b..b82d78b786 100644
--- a/src/compiler/scala/tools/nsc/ast/Reifiers.scala
+++ b/src/compiler/scala/tools/nsc/ast/Reifiers.scala
@@ -22,278 +22,413 @@ import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple }
trait Reifiers { self: Global =>
def reify(tree: Tree): Tree = {
- if (tree.tpe != null) {
- val saved = printTypings
- try {
- val reifyDebug = settings.Yreifydebug.value
- val debugTrace = util.trace when reifyDebug
- debugTrace("transforming = ")(if (settings.Xshowtrees.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
- debugTrace("transformed = ") {
- val reifier = new Reifier()
- val untyped = reifier.reifyTopLevel(tree)
-
- val reifyCopypaste = settings.Yreifycopypaste.value
- if (reifyCopypaste) {
- if (reifyDebug) println("=======================")
- println(reifiedNodeToString(untyped))
- if (reifyDebug) println("=======================")
- }
-
- untyped
- }
- } finally {
- printTypings = saved
+ class Reifier {
+ import definitions._
+ import Reifier._
+
+ final val scalaPrefix = "scala."
+ final val localPrefix = "$local"
+ final val memoizerName = "$memo"
+
+ val reifyDebug = settings.Yreifydebug.value
+
+ private val reifiableSyms = mutable.ArrayBuffer[Symbol]() // the symbols that are reified with the tree
+ private val symIndex = mutable.HashMap[Symbol, Int]() // the index of a reifiable symbol in `reifiableSyms`
+ private var boundSyms = Set[Symbol]() // set of all symbols that are bound in tree to be reified
+
+ private def definedInLiftedCode(tpe: Type) =
+ tpe exists (tp => boundSyms contains tp.typeSymbol)
+
+ private def definedInLiftedCode(sym: Symbol) =
+ boundSyms contains sym
+
+ /**
+ * Generate tree of the form
+ *
+ * { val $mr = scala.reflect.runtime.Mirror
+ * $local1 = new TypeSymbol(owner1, NoPosition, name1)
+ * ...
+ * $localN = new TermSymbol(ownerN, NoPositiion, nameN)
+ * $local1.setInfo(tpe1)
+ * ...
+ * $localN.setInfo(tpeN)
+ * $localN.setAnnotations(annotsN)
+ * rtree
+ * }
+ *
+ * where
+ *
+ * - `$localI` are free type symbols in the environment, as well as local symbols
+ * of refinement types.
+ * - `tpeI` are the info's of `symI`
+ * - `rtree` is code that generates `data` at runtime, maintaining all attributes.
+ * - `data` is typically a tree or a type.
+ */
+ def reifyTopLevel(data: Any): Tree = {
+ val rtree = reify(data)
+ Block(mirrorAlias :: reifySymbolTableSetup, rtree)
}
- } else {
- CannotReifyPreTyperTrees(tree)
- }
- }
-
- class Reifier() {
- import definitions._
-
- final val scalaPrefix = "scala."
- final val localPrefix = "$local"
- final val memoizerName = "$memo"
-
- val reifyDebug = settings.Yreifydebug.value
-
- private val reifiableSyms = mutable.ArrayBuffer[Symbol]() // the symbols that are reified with the tree
- private val symIndex = mutable.HashMap[Symbol, Int]() // the index of a reifiable symbol in `reifiableSyms`
- private var boundSyms = Set[Symbol]() // set of all symbols that are bound in tree to be reified
-
- /**
- * Generate tree of the form
- *
- * { val $mr = scala.reflect.runtime.Mirror
- * $local1 = new TypeSymbol(owner1, NoPosition, name1)
- * ...
- * $localN = new TermSymbol(ownerN, NoPositiion, nameN)
- * $local1.setInfo(tpe1)
- * ...
- * $localN.setInfo(tpeN)
- * $localN.setAnnotations(annotsN)
- * rtree
- * }
- *
- * where
- *
- * - `$localI` are free type symbols in the environment, as well as local symbols
- * of refinement types.
- * - `tpeI` are the info's of `symI`
- * - `rtree` is code that generates `data` at runtime, maintaining all attributes.
- * - `data` is typically a tree or a type.
- */
- def reifyTopLevel(data: Any): Tree = {
- val rtree = reify(data)
- Block(mirrorAlias :: reifySymbolTableSetup, rtree)
- }
- private def isLocatable(sym: Symbol) =
- sym.isPackageClass || sym.owner.isClass || sym.isTypeParameter && sym.paramPos >= 0
+ private def isLocatable(sym: Symbol) =
+ sym.isPackageClass || sym.owner.isClass || sym.isTypeParameter && sym.paramPos >= 0
- private def registerReifiableSymbol(sym: Symbol): Unit =
- if (!(symIndex contains sym)) {
- sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol
- symIndex(sym) = reifiableSyms.length
- reifiableSyms += sym
- }
+ private def registerReifiableSymbol(sym: Symbol): Unit =
+ if (!(symIndex contains sym)) {
+ sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol
+ symIndex(sym) = reifiableSyms.length
+ reifiableSyms += sym
+ }
- // helper methods
-
- private def localName(sym: Symbol): TermName =
- newTermName(localPrefix + symIndex(sym))
-
- private def call(fname: String, args: Tree*): Tree =
- Apply(termPath(fname), args.toList)
-
- private def mirrorSelect(name: String): Tree =
- termPath(nme.MIRROR_PREFIX + name)
-
- private def mirrorCall(name: TermName, args: Tree*): Tree =
- call("" + (nme.MIRROR_PREFIX append name), args: _*)
-
- private def mirrorCall(name: String, args: Tree*): Tree =
- call(nme.MIRROR_PREFIX + name, args: _*)
-
- private def mirrorFactoryCall(value: Product, args: Tree*): Tree =
- mirrorCall(value.productPrefix, args: _*)
-
- private def scalaFactoryCall(name: String, args: Tree*): Tree =
- call(scalaPrefix + name + ".apply", args: _*)
-
- private def mkList(args: List[Tree]): Tree =
- scalaFactoryCall("collection.immutable.List", args: _*)
-
- private def reifyModifiers(m: Modifiers) =
- mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations))
-
- private def reifyAggregate(name: String, args: Any*) =
- scalaFactoryCall(name, (args map reify).toList: _*)
-
- /**
- * Reify a list
- */
- private def reifyList(xs: List[Any]): Tree =
- mkList(xs map reify)
-
- /** Reify a name */
- private def reifyName(name: Name) =
- mirrorCall(if (name.isTypeName) "newTypeName" else "newTermName", Literal(Constant(name.toString)))
-
- private def isFree(sym: Symbol) =
- !(symIndex contains sym)
-
- /**
- * Reify a reference to a symbol
- */
- private def reifySymRef(sym: Symbol): Tree = {
- symIndex get sym match {
- case Some(idx) =>
- Ident(localName(sym))
- case None =>
- if (sym == NoSymbol)
- mirrorSelect("NoSymbol")
- else if (sym == RootPackage)
- mirrorSelect("definitions.RootPackage")
- else if (sym == RootClass)
- mirrorSelect("definitions.RootClass")
- else if (sym == EmptyPackage)
- mirrorSelect("definitions.EmptyPackage")
- else if (sym.isModuleClass)
- Select(reifySymRef(sym.sourceModule), "moduleClass")
- else if (sym.isStatic && sym.isClass)
- mirrorCall("staticClass", reify(sym.fullName))
- else if (sym.isStatic && sym.isModule)
- mirrorCall("staticModule", reify(sym.fullName))
- else if (isLocatable(sym))
- if (sym.isTypeParameter)
- mirrorCall("selectParam", reify(sym.owner), reify(sym.paramPos))
+ // helper methods
+
+ private def localName(sym: Symbol): TermName =
+ newTermName(localPrefix + symIndex(sym))
+
+ private def call(fname: String, args: Tree*): Tree =
+ Apply(termPath(fname), args.toList)
+
+ private def mirrorSelect(name: String): Tree =
+ termPath(nme.MIRROR_PREFIX + name)
+
+ private def mirrorCall(name: TermName, args: Tree*): Tree =
+ call("" + (nme.MIRROR_PREFIX append name), args: _*)
+
+ private def mirrorCall(name: String, args: Tree*): Tree =
+ call(nme.MIRROR_PREFIX + name, args: _*)
+
+ private def mirrorFactoryCall(value: Product, args: Tree*): Tree =
+ mirrorFactoryCall(value.productPrefix, args: _*)
+
+ private def mirrorFactoryCall(prefix: String, args: Tree*): Tree =
+ mirrorCall(prefix, args: _*)
+
+ private def scalaFactoryCall(name: String, args: Tree*): Tree =
+ call(scalaPrefix + name + ".apply", args: _*)
+
+ private def mkList(args: List[Tree]): Tree =
+ scalaFactoryCall("collection.immutable.List", args: _*)
+
+ private def reifyModifiers(m: Modifiers) =
+ mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations))
+
+ private def reifyAggregate(name: String, args: Any*) =
+ scalaFactoryCall(name, (args map reify).toList: _*)
+
+ /**
+ * Reify a list
+ */
+ private def reifyList(xs: List[Any]): Tree =
+ mkList(xs map reify)
+
+ /**
+ * Reify an array
+ */
+ private def reifyArray(xs: Array[_]): Tree =
+ // @xeno.by: doesn't work for Array(LiteralAnnotArg(...))
+ // because we cannot generate manifests for path-dependent types
+ scalaFactoryCall(nme.Array, xs map reify: _*)
+
+ /** Reify a name */
+ private def reifyName(name: Name) =
+ mirrorCall(if (name.isTypeName) "newTypeName" else "newTermName", Literal(Constant(name.toString)))
+
+ private def isFree(sym: Symbol) =
+ !(symIndex contains sym)
+
+ /**
+ * Reify a reference to a symbol
+ */
+ private def reifySymRef(sym: Symbol): Tree = {
+ symIndex get sym match {
+ case Some(idx) =>
+ Ident(localName(sym))
+ case None =>
+ if (sym == NoSymbol)
+ mirrorSelect("NoSymbol")
+ else if (sym == RootPackage)
+ mirrorSelect("definitions.RootPackage")
+ else if (sym == RootClass)
+ mirrorSelect("definitions.RootClass")
+ else if (sym == EmptyPackage)
+ mirrorSelect("definitions.EmptyPackage")
+ else if (sym.isModuleClass)
+ Select(reifySymRef(sym.sourceModule), "moduleClass")
+ else if (sym.isStatic && sym.isClass)
+ mirrorCall("staticClass", reify(sym.fullName))
+ else if (sym.isStatic && sym.isModule)
+ mirrorCall("staticModule", reify(sym.fullName))
+ else if (isLocatable(sym))
+ if (sym.isTypeParameter)
+ mirrorCall("selectParam", reify(sym.owner), reify(sym.paramPos))
+ else {
+ if (reifyDebug) println("locatable: " + sym + " " + sym.isPackageClass + " " + sym.owner + " " + sym.isTypeParameter)
+ val rowner = reify(sym.owner)
+ val rname = reify(sym.name.toString)
+ if (sym.isType)
+ mirrorCall("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("selectOverloadedMethod", rowner, rname, reify(index))
+ } else
+ mirrorCall("selectTerm", rowner, rname)
+ }
else {
- if (reifyDebug) println("locatable: " + sym + " " + sym.isPackageClass + " " + sym.owner + " " + sym.isTypeParameter)
- val rowner = reify(sym.owner)
- val rname = reify(sym.name.toString)
- if (sym.isType)
- mirrorCall("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("selectOverloadedMethod", rowner, rname, reify(index))
- } else
- mirrorCall("selectTerm", rowner, rname)
- }
- else {
- if (sym.isTerm) {
- if (reifyDebug) println("Free: " + sym)
- val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false)
- def markIfCaptured(arg: Ident): Tree =
- if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg
- mirrorCall("newFreeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym)))
- } else {
- if (reifyDebug) println("Late local: " + sym)
- registerReifiableSymbol(sym)
- reifySymRef(sym)
+ if (sym.isTerm) {
+ if (reifyDebug) println("Free: " + sym)
+ val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false)
+ def markIfCaptured(arg: Ident): Tree =
+ if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg
+ mirrorCall("newFreeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym)))
+ } else {
+ if (reifyDebug) println("Late local: " + sym)
+ registerReifiableSymbol(sym)
+ reifySymRef(sym)
+ }
}
- }
+ }
}
- }
- /**
- * reify the creation of a symbol
- */
- private def reifySymbolDef(sym: Symbol): Tree = {
- if (reifyDebug) println("reify sym def " + sym)
-
- ValDef(NoMods, localName(sym), TypeTree(),
- Apply(
- Select(reify(sym.owner), "newNestedSymbol"),
- List(reify(sym.name), reify(sym.pos), Literal(Constant(sym.flags)))
+ /**
+ * reify the creation of a symbol
+ */
+ private def reifySymbolDef(sym: Symbol): Tree = {
+ if (reifyDebug) println("reify sym def " + sym)
+
+ ValDef(NoMods, localName(sym), TypeTree(),
+ Apply(
+ Select(reify(sym.owner), "newNestedSymbol"),
+ List(reify(sym.name), reify(sym.pos), Literal(Constant(sym.flags)))
+ )
)
- )
- }
+ }
- /**
- * Generate code to add type and annotation info to a reified symbol
- */
- private def fillInSymbol(sym: Symbol): Tree = {
- val rset = Apply(Select(reifySymRef(sym), nme.setTypeSignature), List(reifyType(sym.info)))
- if (sym.annotations.isEmpty) rset
- else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations)))
- }
+ /**
+ * Generate code to add type and annotation info to a reified symbol
+ */
+ private def fillInSymbol(sym: Symbol): Tree = {
+ val rset = Apply(Select(reifySymRef(sym), nme.setTypeSignature), List(reifyType(sym.info)))
+ if (sym.annotations.isEmpty) rset
+ else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations)))
+ }
- /** Reify a scope */
- private def reifyScope(scope: Scope): Tree = {
- scope foreach registerReifiableSymbol
- mirrorCall(nme.newScopeWith, scope.toList map reifySymRef: _*)
- }
+ /** Reify a scope */
+ private def reifyScope(scope: Scope): Tree = {
+ scope foreach registerReifiableSymbol
+ mirrorCall(nme.newScopeWith, scope.toList map reifySymRef: _*)
+ }
- /** Reify a list of symbols that need to be created */
- private def reifySymbols(syms: List[Symbol]): Tree = {
- syms foreach registerReifiableSymbol
- mkList(syms map reifySymRef)
- }
+ /** Reify a list of symbols that need to be created */
+ private def reifySymbols(syms: List[Symbol]): Tree = {
+ syms foreach registerReifiableSymbol
+ mkList(syms map reifySymRef)
+ }
- /** Reify a type that defines some symbols */
- private def reifyTypeBinder(value: Product, bound: List[Symbol], underlying: Type): Tree =
- mirrorFactoryCall(value, reifySymbols(bound), reify(underlying))
-
- /** Reify a type */
- private def reifyType(tpe0: Type): Tree = {
- val tpe = tpe0.normalize
- val tsym = tpe.typeSymbol
- if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic)
- Select(reifySymRef(tpe.typeSymbol), nme.asTypeConstructor)
- else tpe match {
- case t @ NoType =>
- reifyMirrorObject(t)
- case t @ NoPrefix =>
- reifyMirrorObject(t)
- case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic =>
- mirrorCall(nme.thisModuleType, reify(clazz.fullName))
- case t @ RefinedType(parents, decls) =>
- registerReifiableSymbol(tpe.typeSymbol)
- mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
- case t @ ClassInfoType(parents, decls, clazz) =>
- registerReifiableSymbol(clazz)
- mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
- case t @ ExistentialType(tparams, underlying) =>
- reifyTypeBinder(t, tparams, underlying)
- case t @ PolyType(tparams, underlying) =>
- reifyTypeBinder(t, tparams, underlying)
- case t @ MethodType(params, restpe) =>
- reifyTypeBinder(t, params, restpe)
- case _ =>
- reifyProductUnsafe(tpe)
+ /** Reify a type that defines some symbols */
+ private def reifyTypeBinder(value: Product, bound: List[Symbol], underlying: Type): Tree =
+ mirrorFactoryCall(value, reifySymbols(bound), reify(underlying))
+
+ /** Reify a type */
+ private def reifyType(tpe0: Type): Tree = {
+ val tpe = tpe0.normalize
+
+ if (tpe.isErroneous)
+ CannotReifyErroneousType(tpe)
+ if (definedInLiftedCode(tpe))
+ CannotReifyTypeInvolvingBoundType(tpe)
+
+ val tsym = tpe.typeSymbol
+ if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic)
+ Select(reifySymRef(tpe.typeSymbol), nme.asTypeConstructor)
+ else tpe match {
+ case t @ NoType =>
+ reifyMirrorObject(t)
+ case t @ NoPrefix =>
+ reifyMirrorObject(t)
+ case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic =>
+ mirrorCall(nme.thisModuleType, reify(clazz.fullName))
+ case t @ RefinedType(parents, decls) =>
+ registerReifiableSymbol(tpe.typeSymbol)
+ mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
+ case t @ ClassInfoType(parents, decls, clazz) =>
+ registerReifiableSymbol(clazz)
+ mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
+ case t @ ExistentialType(tparams, underlying) =>
+ reifyTypeBinder(t, tparams, underlying)
+ case t @ PolyType(tparams, underlying) =>
+ reifyTypeBinder(t, tparams, underlying)
+ case t @ MethodType(params, restpe) =>
+ reifyTypeBinder(t, params, restpe)
+ case t @ AnnotatedType(anns, underlying, selfsym) =>
+ val saved1 = reifySymbols
+ val saved2 = reifyTypes
+
+ 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.
+ reifySymbols = true
+ reifyTypes = true
+ if (reifyDebug) println("reify AnnotatedType: " + tpe)
+ reifyProductUnsafe(tpe)
+ } finally {
+ reifySymbols = saved1
+ reifyTypes = saved2
+ }
+ case _ =>
+ reifyProductUnsafe(tpe)
+ }
}
- }
- private def definedInLiftedCode(tpe: Type) =
- tpe exists (tp => boundSyms contains tp.typeSymbol)
+ var reifySymbols = false
+ var reifyTypes = false
+
+ /** Reify a tree */
+ private def reifyTree(tree: Tree): Tree = {
+ def reifyDefault(tree: Tree) =
+ reifyProduct(tree)
+
+ var rtree = tree match {
+ case tree if tree.isErroneous =>
+ CannotReifyErroneousTree(tree)
+ case self.EmptyTree =>
+ reifyMirrorObject(EmptyTree)
+ case self.emptyValDef =>
+ mirrorSelect(nme.emptyValDef)
+ case This(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
+ reifyFree(tree)
+ case Ident(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
+ if (tree.symbol.isVariable && tree.symbol.owner.isTerm) {
+ if (reifyDebug) println("captured variable: " + tree.symbol)
+ captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reifyTree here.
+ mirrorCall("Select", reifyFree(tree), reifyName(nme.elem))
+ } else reifyFree(tree)
+ case tt: TypeTree if (tt.tpe != null) =>
+ reifyTypeTree(tt)
+ case ta @ TypeApply(hk, ts) =>
+ def isErased(tt: TypeTree) = tt.tpe != null && definedInLiftedCode(tt.tpe) && tt.original == null
+ val discard = ts collect { case tt: TypeTree => tt } exists isErased
+ if (reifyDebug && discard) println("discarding TypeApply: " + tree)
+ if (discard) reifyTree(hk) else reifyDefault(ta)
+ case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) =>
+ CannotReifyClassOfBoundType(tree, tpe)
+ case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym =>
+ CannotReifyClassOfBoundEnum(tree, constant.tpe)
+ 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))))
+ // registerReifiableSymbol(tree.symbol)
+ boundSyms += tree.symbol
+
+ val prefix = tree.productPrefix
+ val elements = (tree.productIterator map {
+ // annotations exist in two flavors:
+ // 1) pre-typer ones that populate: a) Modifiers, b) Annotated nodes (irrelevant in this context)
+ // 2) post-typer ones that dwell inside: a) sym.annotations, b) AnnotatedTypes (irrelevant in this context)
+ //
+ // here we process Modifiers that are involved in deftrees
+ // AnnotatedTypes get reified elsewhere (currently, in ``reifyTypeTree'')
+ case Modifiers(flags, privateWithin, annotations) =>
+ assert(annotations.isEmpty) // should've been eliminated by the typer
+ val postTyper = tree.symbol.annotations filter (_.original != EmptyTree)
+ if (reifyDebug && !postTyper.isEmpty) println("reify symbol annotations for %s: %s".format(tree.symbol, tree.symbol.annotations))
+ val preTyper = postTyper map toPreTyperAnnotation
+ Modifiers(flags, privateWithin, preTyper)
+ case x =>
+ x
+ }).toList
+ reifyProduct(prefix, elements)
+ case _ =>
+ reifyDefault(tree)
+ }
- private def isErased(tree: Tree) = tree match {
- case tt: TypeTree => definedInLiftedCode(tt.tpe) && tt.original == null
- case _ => false
- }
+ // 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 (reifySymbols && 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 (reifyTypes && 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)))
+ }
- /** Reify a tree */
- private def reifyTree(tree: Tree): Tree = tree match {
- case EmptyTree =>
- reifyMirrorObject(EmptyTree)
- case This(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
- reifyFree(tree)
- case Ident(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
- if (tree.symbol.isVariable && tree.symbol.owner.isTerm) {
- captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reifyTree here.
- mirrorCall("Select", reifyFree(tree), reifyName(nme.elem))
- } else reifyFree(tree)
- case tt: TypeTree if (tt.tpe != null) =>
+ rtree
+ }
+
+ /** Reify pre-typer representation of a type.
+ *
+ * NB: This is the trickiest part of reification!
+ *
+ * In most cases, we're perfectly fine to reify a Type itself (see ``reifyType'').
+ * However if the type involves a symbol declared inside the quasiquote (i.e. registered in ``boundSyms''),
+ * then we cannot reify it, or otherwise subsequent reflective compilation will fail.
+ *
+ * Why will it fail? Because reified deftrees (e.g. ClassDef(...)) will generate fresh symbols during that compilation,
+ * so naively reified symbols will become out of sync, which brings really funny compilation errors and/or crashes, e.g.:
+ * https://issues.scala-lang.org/browse/SI-5230
+ *
+ * To deal with this unpleasant fact, we need to fall back from types to equivalent trees (after all, parser trees don't contain any types, just trees, so it should be possible).
+ * Luckily, these original trees get preserved for us in the ``original'' field when Trees get transformed into TypeTrees.
+ * And if an original of a type tree is empty, we can safely assume that this type is non-essential (e.g. was inferred/generated by the compiler).
+ * In that case the type can be omitted (e.g. reified as an empty TypeTree), since it will be inferred again later on.
+ *
+ * An important property of the original is that it isn't just a pre-typer tree.
+ * It's actually kind of a post-typer tree with symbols assigned to its Idents (e.g. Ident("List") will contain a symbol that points to immutable.this.List).
+ * This is very important, since subsequent reflective compilation won't have to resolve these symbols.
+ * In general case, such resolution cannot be performed, since reification doesn't preserve lexical context,
+ * which means that reflective compilation won't be aware of, say, imports that were provided when the reifee has been compiled.
+ *
+ * This workaround worked surprisingly well and allowed me to fix several important reification bugs, until the abstraction has leaked.
+ * Suddenly I found out that in certain contexts original trees do not contain symbols, but are just parser trees.
+ * To the moment I know two such situations:
+ * 1) Unapplies: https://issues.scala-lang.org/browse/SI-5273?focusedCommentId=56057#comment-56057
+ * 2) Annotations: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless
+ * 3) <sigh, what will I discover next?>
+ */
+ private def reifyTypeTree(tt: TypeTree): Tree = {
if (definedInLiftedCode(tt.tpe)) {
- // erase non-essential (i.e. inferred) types
- // reify symless counterparts of essential types
- // @xeno.by: in general case reflective compiler lacks the context to typecheck the originals
- // more info here: https://issues.scala-lang.org/browse/SI-5273?focusedCommentId=56057#comment-56057
- // this is A BIG BAD TODO!
- if (tt.original != null) reify(tt.original) else mirrorCall("TypeTree")
+ if (reifyDebug) println("reifyTypeTree, defined in lifted code: " + tt.tpe)
+ if (tt.original != null) {
+ val annotations = tt.tpe filter { _.isInstanceOf[AnnotatedType] } collect { case atp: AnnotatedType => atp.annotations } flatten
+ val annmap = annotations map { ann => (ann.original, ann) } toMap
+
+ // annotations exist in two flavors:
+ // 1) pre-typer ones that populate: a) Modifiers (irrelevant in this context), b) Annotated nodes
+ // 2) post-typer ones that dwell inside: a) sym.annotations (irrelevant in this context), b) AnnotatedTypes
+ //
+ // here we process AnnotatedTypes, since only they can be involved in TypeTrees
+ // Modifiers get reified elsewhere (currently, in the "isDef" case of ``reifyTree'')
+ //
+ // the problem with annotations is that their originals don't preserve any symbols at all
+ // read the comment to this method to find out why it's bad
+ // that's why we transplant typechecked, i.e. symful, annotations onto original trees
+ class AnnotationFixup extends self.Transformer {
+ override def transform(tree: Tree) = tree match {
+ case Annotated(ann0, args) =>
+ assert(annmap contains ann0)
+ val ann1 = annmap(ann0)
+ val ann = toPreTyperAnnotation(ann1)
+ Annotated(ann, transform(args))
+ case _ =>
+ tree
+ }
+ }
+
+ if (reifyDebug) println("verdict: essential, reify as original")
+ val patchedOriginal = new AnnotationFixup().transform(tt.original)
+ reifyTree(patchedOriginal)
+ } else {
+ // type is deemed to be non-essential
+ // erase it and hope that subsequent reflective compilation will be able to recreate it again
+ if (reifyDebug) println("verdict: non-essential, discard")
+ mirrorCall("TypeTree")
+ }
} else {
var rtt = mirrorCall(nme.TypeTree, reifyType(tt.tpe))
// @xeno.by: originals get typechecked during subsequent reflective compilation, which leads to subtle bugs
@@ -306,124 +441,219 @@ trait Reifiers { self: Global =>
// }
rtt
}
- case ta @ TypeApply(hk, ts) =>
- if (ts exists isErased) reifyTree(hk) else reifyProduct(ta)
- case self.emptyValDef =>
- mirrorSelect(nme.emptyValDef)
- case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) =>
- CannotReifyClassOfBoundType(tree, tpe)
- case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym =>
- CannotReifyClassOfBoundEnum(tree, constant.tpe)
- case _ =>
- if (tree.isDef) {
- if (reifyDebug) println("boundSym: " + tree.symbol)
- boundSyms += tree.symbol
+ }
+
+ /** Reify post-typer representation of an annotation */
+ private def reifyAnnotation(ann: AnnotationInfo): Tree =
+ // @xeno.by: if you reify originals, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important
+ mirrorFactoryCall("AnnotationInfo", reifyType(ann.atp), reifyList(ann.args), reify(ann.assocs))
+
+ /** Reify pre-typer representation of an annotation.
+ * The trick here is to retain the symbols that have been populated during typechecking of the annotation.
+ * If we do not do that, subsequent reflective compilation will fail.
+ */
+ private def toPreTyperAnnotation(ann: AnnotationInfo): Tree = {
+ if (definedInLiftedCode(ann.atp)) {
+ // todo. deconstruct reifiable tree from ann.original and ann.args+ann.assocs
+ //
+ // keep in mind that we can't simply use ann.original, because its args are symless
+ // which means that any imported symbol (e.g. List) will crash subsequent reflective compilation
+ // hint: if I had enough time, I'd try to extract reifiable annotation type from ann.original
+ // and to apply its constructor to ann.args (that are symful, i.e. suitable for reification)
+ //
+ // also, if we pursue the route of reifying annotations defined in lifted code
+ // we should think about how to provide types for all nodes of the return value
+ // this will be necessary for reifying AnnotatedTypes, since ASTs inside ATs must all have non-null tpes
+ // an alternative would be downgrading ATs to Annotated nodes, but this needs careful thinking
+ // for now I just leave this as an implementation restriction
+ CannotReifyAnnotationInvolvingBoundType(ann)
+ } else {
+ val args = if (ann.assocs.isEmpty) {
+ ann.args
+ } else {
+ def toScalaAnnotation(jann: ClassfileAnnotArg): Tree = jann match {
+ case LiteralAnnotArg(const) =>
+ Literal(const)
+ case ArrayAnnotArg(arr) =>
+ Apply(Ident(definitions.ArrayModule), arr.toList map toScalaAnnotation)
+ case NestedAnnotArg(ann) =>
+ toPreTyperAnnotation(ann)
+ }
+
+ ann.assocs map { case (nme, arg) => AssignOrNamedArg(Ident(nme), toScalaAnnotation(arg)) }
+ }
+
+ New(TypeTree(ann.atp), List(args))
+ }
+ }
+
+ /**
+ * Reify a free reference. The result will be either a mirror reference
+ * to a global value, or else a mirror Literal.
+ */
+ private def reifyFree(tree: Tree): Tree = tree match {
+ case This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass =>
+ val sym = tree.symbol
+ if (reifyDebug) println("This for %s, reified as freeVar".format(sym))
+ if (reifyDebug) println("Free: " + sym)
+ val freeVar = mirrorCall("newFreeVar", reify(sym.name.toString), reify(sym.tpe), This(sym))
+ mirrorCall(nme.Ident, freeVar)
+ case This(_) =>
+ if (reifyDebug) println("This for %s, reified as This".format(tree.symbol))
+ mirrorCall(nme.This, reifySymRef(tree.symbol))
+ case _ =>
+ mirrorCall(nme.Ident, reifySymRef(tree.symbol))
+ }
+
+ // todo: consider whether we should also reify positions
+ private def reifyPosition(pos: Position): Tree =
+ reifyMirrorObject(NoPosition)
+
+ // !!! we must eliminate these casts.
+ private def reifyProductUnsafe(x: Any): Tree =
+ if (x.isInstanceOf[Product]) reifyProduct(x.asInstanceOf[Product])
+ else throw new Exception("%s of type %s cannot be cast to Product".format(x, x.getClass))
+ private def reifyProduct(x: Product): Tree =
+ reifyProduct(x.productPrefix, x.productIterator.toList)
+ private def reifyProduct(prefix: String, elements: List[Any]): Tree = {
+ // @xeno.by: reflection would be more robust, but, hey, this is a hot path
+ if (prefix.startsWith("Tuple")) reifyAggregate(prefix, elements: _*)
+ else mirrorCall(prefix, (elements map reify): _*)
+ }
+
+ /**
+ * Reify a case object defined in Mirror
+ */
+ private def reifyMirrorObject(name: String): Tree = mirrorSelect(name)
+ private def reifyMirrorObject(x: Product): Tree = reifyMirrorObject(x.productPrefix)
+
+ private def isReifiableConstant(value: Any) = value match {
+ case null => true // seems pretty reifable to me?
+ case _: String => true
+ case _ => isAnyVal(value)
+ }
+
+ /** Reify an arbitary value */
+ private def reify(value: Any): Tree = value match {
+ case tree: Tree => reifyTree(tree)
+ case sym: Symbol => reifySymRef(sym)
+ case tpe: Type => reifyType(tpe)
+ case xs: List[_] => reifyList(xs)
+ case xs: Array[_] => reifyArray(xs)
+ case scope: Scope => reifyScope(scope)
+ case x: Name => reifyName(x)
+ case x: Position => reifyPosition(x)
+ case x: Modifiers => reifyModifiers(x)
+ case x: AnnotationInfo => reifyAnnotation(x)
+ case _ =>
+ if (isReifiableConstant(value)) Literal(Constant(value))
+ else reifyProductUnsafe(value)
+ }
+
+ /**
+ * An (unreified) path that refers to definition with given fully qualified name
+ * @param mkName Creator for last portion of name (either TermName or TypeName)
+ */
+ private 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 */
+ private def termPath(fullname: String): Tree = path(fullname, newTermName)
+
+ /** An (unreified) path that refers to type definition with given fully qualified name */
+ private def typePath(fullname: String): Tree = path(fullname, newTypeName)
+
+ private def mirrorAlias =
+ ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(termPath(fullnme.MirrorPackage)), termPath(fullnme.MirrorPackage))
+
+ /**
+ * Generate code that generates a symbol table of all symbols registered in `reifiableSyms`
+ */
+ private def reifySymbolTableSetup: List[Tree] = {
+ val symDefs, fillIns = new mutable.ArrayBuffer[Tree]
+ var i = 0
+ while (i < reifiableSyms.length) {
+ // fillInSymbol might create new reifiableSyms, that's why this is done iteratively
+ symDefs += reifySymbolDef(reifiableSyms(i))
+ fillIns += fillInSymbol(reifiableSyms(i))
+ i += 1
}
- reifyProduct(tree)
- /*
- if (tree.isDef || tree.isInstanceOf[Function])
- registerReifiableSymbol(tree.symbol)
- if (tree.hasSymbol)
- rtree = Apply(Select(rtree, nme.setSymbol), List(reifySymRef(tree.symbol)))
- Apply(Select(rtree, nme.setType), List(reifyType(tree.tpe)))
-*/
- }
+ symDefs.toList ++ fillIns.toList
+ }
+ } // end of Reifier
- /**
- * Reify a free reference. The result will be either a mirror reference
- * to a global value, or else a mirror Literal.
- */
- private def reifyFree(tree: Tree): Tree = tree match {
- case This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass =>
- val sym = tree.symbol
- if (reifyDebug) println("This for %s, reified as freeVar".format(sym))
- if (reifyDebug) println("Free: " + sym)
- val freeVar = mirrorCall("newFreeVar", reify(sym.name.toString), reify(sym.tpe), This(sym))
- mirrorCall(nme.Ident, freeVar)
- case This(_) =>
- if (reifyDebug) println("This for %s, reified as This".format(tree.symbol))
- mirrorCall(nme.This, reifySymRef(tree.symbol))
- case _ =>
- mirrorCall(nme.Ident, reifySymRef(tree.symbol))
- }
+ object Reifier {
+ def CannotReifyPreTyperTree(tree: Tree) = {
+ val msg = "pre-typer trees are not supported, consider typechecking the tree before passing it to the reifier"
+ throw new ReifierError(tree.pos, msg)
+ }
- // todo: consider whether we should also reify positions
- private def reifyPosition(pos: Position): Tree =
- reifyMirrorObject(NoPosition)
-
- // !!! we must eliminate these casts.
- private def reifyProductUnsafe(x: Any): Tree =
- reifyProduct(x.asInstanceOf[Product])
- private def reifyProduct(x: Product): Tree =
- mirrorCall(x.productPrefix, (x.productIterator map reify).toList: _*)
-
- /**
- * Reify a case object defined in Mirror
- */
- private def reifyMirrorObject(name: String): Tree = mirrorSelect(name)
- private def reifyMirrorObject(x: Product): Tree = reifyMirrorObject(x.productPrefix)
-
- private def isReifiableConstant(value: Any) = value match {
- case null => true // seems pretty reifable to me?
- case _: String => true
- case _ => isAnyVal(value)
- }
+ def CannotReifyErroneousTree(tree: Tree) = {
+ val msg = "erroneous trees are not supported, make sure that your tree typechecks successfully before passing it to the reifier"
+ throw new ReifierError(tree.pos, msg)
+ }
- /** Reify an arbitary value */
- private def reify(value: Any): Tree = value match {
- case tree: Tree => reifyTree(tree)
- case sym: Symbol => reifySymRef(sym)
- case tpe: Type => reifyType(tpe)
- case xs: List[_] => reifyList(xs)
- case xs: Array[_] => scalaFactoryCall(nme.Array, xs map reify: _*)
- case scope: Scope => reifyScope(scope)
- case x: Name => reifyName(x)
- case x: Position => reifyPosition(x)
- case x: Modifiers => reifyModifiers(x)
- case _ =>
- if (isReifiableConstant(value)) Literal(Constant(value))
- else reifyProductUnsafe(value)
- }
+ def CannotReifyErroneousType(tpe: Type) = {
+ val msg = "erroneous types are not supported, make sure that your tree typechecks successfully before passing it to the reifier"
+ throw new ReifierError(NoPosition, msg)
+ }
- /**
- * An (unreified) path that refers to definition with given fully qualified name
- * @param mkName Creator for last portion of name (either TermName or TypeName)
- */
- private 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)
+ def CannotReifyClassOfBoundType(tree: Tree, tpe: Type) = {
+ val msg = "implementation restriction: cannot reify classOf[%s] which refers to a type declared inside the block being reified".format(tpe)
+ throw new ReifierError(tree.pos, msg)
+ }
+
+ def CannotReifyClassOfBoundEnum(tree: Tree, tpe: Type) = {
+ val msg = "implementation restriction: cannot reify classOf[%s] which refers to an enum declared inside the block being reified".format(tpe)
+ throw new ReifierError(tree.pos, msg)
+ }
+
+ def CannotReifyTypeInvolvingBoundType(tpe: Type) = {
+ val msg = "implementation restriction: cannot reify type %s which involves a symbol declared inside the block being reified".format(tpe)
+ throw new ReifierError(NoPosition, msg)
}
- }
- /** An (unreified) path that refers to term definition with given fully qualified name */
- private def termPath(fullname: String): Tree = path(fullname, newTermName)
-
- /** An (unreified) path that refers to type definition with given fully qualified name */
- private def typePath(fullname: String): Tree = path(fullname, newTypeName)
-
- private def mirrorAlias =
- ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(termPath(fullnme.MirrorPackage)), termPath(fullnme.MirrorPackage))
-
- /**
- * Generate code that generates a symbol table of all symbols registered in `reifiableSyms`
- */
- private def reifySymbolTableSetup: List[Tree] = {
- val symDefs, fillIns = new mutable.ArrayBuffer[Tree]
- var i = 0
- while (i < reifiableSyms.length) {
- // fillInSymbol might create new reifiableSyms, that's why this is done iteratively
- symDefs += reifySymbolDef(reifiableSyms(i))
- fillIns += fillInSymbol(reifiableSyms(i))
- i += 1
+ def CannotReifyAnnotationInvolvingBoundType(ann: AnnotationInfo) = {
+ val msg = "implementation restriction: cannot reify annotation @%s which involves a symbol declared inside the block being reified".format(ann)
+ throw new ReifierError(ann.original.pos, msg)
}
+ } // end of Reifier
+
+ // begin reify
+ import Reifier._
+ if (tree.tpe != null) {
+ val saved = printTypings
+ try {
+ val reifyDebug = settings.Yreifydebug.value
+ val debugTrace = util.trace when reifyDebug
+ debugTrace("transforming = ")(if (settings.Xshowtrees.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
+ debugTrace("transformed = ") {
+ val reifier = new Reifier()
+ val untyped = reifier.reifyTopLevel(tree)
+
+ val reifyCopypaste = settings.Yreifycopypaste.value
+ if (reifyCopypaste) {
+ if (reifyDebug) println("=======================")
+ println(reifiedNodeToString(untyped))
+ if (reifyDebug) println("=======================")
+ }
- symDefs.toList ++ fillIns.toList
+ untyped
+ }
+ } finally {
+ printTypings = saved
+ }
+ } else {
+ CannotReifyPreTyperTree(tree)
}
}
@@ -431,19 +661,4 @@ trait Reifiers { self: Global =>
class ReifierError(var pos: Position, val msg: String) extends Throwable(msg) {
def this(msg: String) = this(NoPosition, msg)
}
-
- def CannotReifyPreTyperTrees(tree: Tree) = {
- val msg = "pre-typer trees are not supported, consider typechecking the tree before passing it to the reifier"
- throw new ReifierError(tree.pos, msg)
- }
-
- def CannotReifyClassOfBoundType(tree: Tree, tpe: Type) = {
- val msg = "cannot reify classOf[%s] which refers to a type declared inside the block being reified".format(tpe)
- throw new ReifierError(tree.pos, msg)
- }
-
- def CannotReifyClassOfBoundEnum(tree: Tree, tpe: Type) = {
- val msg = "cannot reify classOf[%s] which refers to an enum declared inside the block being reified".format(tpe)
- throw new ReifierError(tree.pos, msg)
- }
}
diff --git a/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala
index 98135fadda..fce59bb099 100644
--- a/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala
+++ b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala
@@ -34,22 +34,22 @@ trait ReifyPrinters { self: NodePrinters =>
s = s.replace("modifiersFromInternalFlags", "Modifiers")
s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()")
s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => {
- val buf = new StringBuilder
+ val buf = new collection.mutable.ListBuffer[String]
- val flags = m.group(1).toLong
- val s_flags = Flags.modifiersOfFlags(flags) map (_.sourceString) mkString ", "
- if (s_flags != "")
- buf.append("Set(" + s_flags + ")")
+ val annotations = m.group(3)
+ if (buf.nonEmpty || annotations.nonEmpty)
+ buf.append("List(" + annotations + ")")
val privateWithin = "" + m.group(2)
- if (privateWithin != "")
- buf.append(", newTypeName(\"" + privateWithin + "\")")
+ if (buf.nonEmpty || privateWithin != "")
+ buf.append("newTypeName(\"" + privateWithin + "\")")
- val annotations = m.group(3)
- if (annotations.nonEmpty)
- buf.append(", List(" + annotations + ")")
+ val flags = m.group(1).toLong
+ val s_flags = Flags.modifiersOfFlags(flags) map (_.sourceString) mkString ", "
+ if (buf.nonEmpty || s_flags != "")
+ buf.append("Set(" + s_flags + ")")
- "Modifiers(" + buf.toString + ")"
+ "Modifiers(" + buf.reverse.mkString(", ") + ")"
})
s = """setInternalFlags\((\d+)L\)""".r.replaceAllIn(s, m => {
val flags = m.group(1).toLong
diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
index c060e938bb..3371353f25 100644
--- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
@@ -51,9 +51,6 @@ trait TreePrinters extends reflect.internal.TreePrinters { this: Global =>
treePrinter.println()
treePrinter.print(definition)
- case AssignOrNamedArg(lhs, rhs) =>
- treePrinter.print(lhs, " = ", rhs)
-
case TypeTreeWithDeferredRefCheck() =>
treePrinter.print("<tree with deferred refcheck>")
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 83b6252b26..9e304a0eb5 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -30,12 +30,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
override def isType = definition.isType
}
- /** Either an assignment or a named argument. Only appears in argument lists,
- * eliminated by typecheck (doTypedApply)
- */
- case class AssignOrNamedArg(lhs: Tree, rhs: Tree)
- extends TermTree
-
/** Array selection <qualifier> . <name> only used during erasure */
case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type)
extends TermTree with RefTree
@@ -155,8 +149,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
traverser.traverseTrees(ts)
case DocDef(comment, definition) =>
traverser.traverse(definition)
- case AssignOrNamedArg(lhs, rhs) =>
- traverser.traverse(lhs); traverser.traverse(rhs)
case SelectFromArray(qualifier, selector, erasure) =>
traverser.traverse(qualifier)
case ReferenceToBoxed(idt) =>
@@ -168,7 +160,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
trait TreeCopier extends super.TreeCopierOps {
def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef
- def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree): AssignOrNamedArg
def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray
def ReferenceToBoxed(tree: Tree, idt: Ident): ReferenceToBoxed
def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck
@@ -180,8 +171,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
class StrictTreeCopier extends super.StrictTreeCopier with TreeCopier {
def DocDef(tree: Tree, comment: DocComment, definition: Tree) =
new DocDef(comment, definition).copyAttrs(tree)
- def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) =
- new AssignOrNamedArg(lhs, rhs).copyAttrs(tree)
def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) =
new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree)
def ReferenceToBoxed(tree: Tree, idt: Ident) =
@@ -197,11 +186,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
if (comment0 == comment) && (definition0 == definition) => t
case _ => this.treeCopy.DocDef(tree, comment, definition)
}
- def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = tree match {
- case t @ AssignOrNamedArg(lhs0, rhs0)
- if (lhs0 == lhs) && (rhs0 == rhs) => t
- case _ => this.treeCopy.AssignOrNamedArg(tree, lhs, rhs)
- }
def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = tree match {
case t @ SelectFromArray(qualifier0, selector0, _)
if (qualifier0 == qualifier) && (selector0 == selector) => t
@@ -232,8 +216,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
override protected def xtransform(transformer: super.Transformer, tree: Tree): Tree = tree match {
case DocDef(comment, definition) =>
transformer.treeCopy.DocDef(tree, comment, transformer.transform(definition))
- case AssignOrNamedArg(lhs, rhs) =>
- transformer.treeCopy.AssignOrNamedArg(tree, transformer.transform(lhs), transformer.transform(rhs))
case SelectFromArray(qualifier, selector, erasure) =>
transformer.treeCopy.SelectFromArray(
tree, transformer.transform(qualifier), selector, erasure)
@@ -333,7 +315,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
case Parens(expr) (only used during parsing)
case DocDef(comment, defn) => (eliminated by typer)
- case AssignOrNamedArg(lhs, rhs) => (eliminated by typer)
case TypeTreeWithDeferredRefCheck() => (created and eliminated by typer)
case SelectFromArray(_, _, _) => (created and eliminated by erasure)
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 485a1f3a5c..e949cb3eb2 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -167,6 +167,7 @@ trait ScalaSettings extends AbsScalaSettings
val Yreifycopypaste =
BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
val Yreifydebug = BooleanSetting ("-Yreify-debug", "Trace reification.")
+ val Ymacrodebug = BooleanSetting ("-Ymacro-debug", "Trace macro-related activities: generation of synthetics, expansion, exceptions.")
val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup")
val Yrepldebug = BooleanSetting ("-Yrepl-debug", "Trace all repl activity.") .
withPostSetHook(_ => interpreter.replProps.debug setValue true)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 48ec59234e..ed249796a8 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -49,7 +49,7 @@ trait Macros { self: Analyzer =>
* If `foo` is declared in an object, the second parameter list is () instead of (_this: _context.Tree).
* If macro has no type arguments, the third parameter list is omitted (it's not empty, but omitted altogether).
*
- * To find out the desugared representation of your particular macro, compile it with -Ydebug.
+ * To find out the desugared representation of your particular macro, compile it with -Ymacro-debug.
*/
def macroMethDef(mdef: DefDef): Tree = {
def paramDef(name: Name, tpt: Tree) = ValDef(Modifiers(PARAM), name, tpt, EmptyTree)
@@ -86,7 +86,7 @@ trait Macros { self: Analyzer =>
def addMacroMethods(templ: Template, namer: Namer): Unit = {
for (ddef @ DefDef(mods, _, _, _, _, _) <- templ.body if mods hasFlag MACRO) {
- val trace = scala.tools.nsc.util.trace when settings.debug.value
+ val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value
val sym = namer.enterSyntheticSym(trace("macro def: ")(macroMethDef(ddef)))
trace("added to "+namer.context.owner.enclClass+": ")(sym)
}
@@ -106,16 +106,43 @@ trait Macros { self: Analyzer =>
* not contain the macro implementation.
*/
def macroImpl(mac: Symbol): Option[(AnyRef, mirror.Symbol)] = {
+ val debug = settings.Ymacrodebug.value
+ val trace = scala.tools.nsc.util.trace when debug
+ trace("looking for macro implementation: ")(mac.fullNameString)
+
try {
val mmeth = macroMeth(mac)
+ trace("found implementation at: ")(mmeth.fullNameString)
+
if (mmeth == NoSymbol) None
else {
val receiverClass: mirror.Symbol = mirror.symbolForName(mmeth.owner.fullName)
+ if (debug) {
+ println("receiverClass is: " + receiverClass.fullNameString)
+
+ val jreceiverClass = mirror.classToJava(receiverClass)
+ val jreceiverSource = jreceiverClass.getProtectionDomain.getCodeSource
+ println("jreceiverClass is %s from %s".format(jreceiverClass, jreceiverSource))
+
+ val jreceiverClasspath = jreceiverClass.getClassLoader match {
+ case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
+ case _ => "<unknown>"
+ }
+ println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, jreceiverClasspath))
+ }
+
val receiverObj = receiverClass.companionModule
+ trace("receiverObj is: ")(receiverObj.fullNameString)
+
if (receiverObj == mirror.NoSymbol) None
else {
val receiver = mirror.companionInstance(receiverClass)
val rmeth = receiverObj.info.member(mirror.newTermName(mmeth.name.toString))
+ if (debug) {
+ println("rmeth is: " + rmeth.fullNameString)
+ println("jrmeth is: " + mirror.methodToJava(rmeth))
+ }
+
if (rmeth == mirror.NoSymbol) None
else {
Some((receiver, rmeth))
@@ -133,16 +160,21 @@ trait Macros { self: Analyzer =>
* tree that calls this method instead of the macro.
*/
def macroExpand(tree: Tree, context: Context): Option[Any] = {
+ val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value
+ trace("macroExpand: ")(tree)
+
val macroDef = tree.symbol
macroImpl(macroDef) match {
case Some((receiver, rmeth)) =>
val argss = List(global) :: macroArgs(tree)
val paramss = macroMeth(macroDef).paramss
+ trace("paramss: ")(paramss)
val rawArgss = for ((as, ps) <- argss zip paramss) yield {
if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1)
else as
}
val rawArgs: Seq[Any] = rawArgss.flatten
+ trace("rawArgs: ")(rawArgs)
val savedInfolevel = nodePrinters.infolevel
try {
// @xeno.by: InfoLevel.Verbose examines and prints out infos of symbols
@@ -156,16 +188,19 @@ trait Macros { self: Analyzer =>
} catch {
case ex =>
val realex = ReflectionUtils.unwrapThrowable(ex)
- val stacktrace = new java.io.StringWriter()
- realex.printStackTrace(new java.io.PrintWriter(stacktrace))
- val msg = System.getProperty("line.separator") + stacktrace
+ val msg = if (settings.Ymacrodebug.value) {
+ val stacktrace = new java.io.StringWriter()
+ realex.printStackTrace(new java.io.PrintWriter(stacktrace))
+ System.getProperty("line.separator") + stacktrace
+ } else {
+ realex.getMessage
+ }
context.unit.error(tree.pos, "exception during macro expansion: " + msg)
None
} finally {
nodePrinters.infolevel = savedInfolevel
}
case None =>
- val trace = scala.tools.nsc.util.trace when settings.debug.value
def notFound() = {
context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name)
None
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 6f6edc62c7..ef69e1525e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1404,7 +1404,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def typedClassDef(cdef: ClassDef): Tree = {
// attributes(cdef)
val clazz = cdef.symbol
- val typedMods = removeAnnotations(cdef.mods)
+ val typedMods = typedModifiers(cdef.mods)
assert(clazz != NoSymbol, cdef)
reenterTypeParams(cdef.tparams)
val tparams1 = cdef.tparams mapConserve (typedTypeDef)
@@ -1441,7 +1441,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
linkedClass.info.decl(nme.CONSTRUCTOR).alternatives foreach (_.initialize)
val clazz = mdef.symbol.moduleClass
- val typedMods = removeAnnotations(mdef.mods)
+ val typedMods = typedModifiers(mdef.mods)
assert(clazz != NoSymbol, mdef)
val impl1 = typerReportAnyContextErrors(context.make(mdef.impl, clazz, newScope)) {
_.typedTemplate(mdef.impl, {
@@ -1541,8 +1541,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
/** Remove definition annotations from modifiers (they have been saved
* into the symbol's ``annotations'' in the type completer / namer)
+ *
+ * However reification does need annotation definitions to proceed.
+ * Unfortunately, AnnotationInfo doesn't provide enough info to reify it in general case.
+ * The biggest problem is with the "atp: Type" field, which cannot be reified in some situations
+ * that involve locally defined annotations. See more about that in Reifiers.scala.
+ *
+ * That's why the original tree gets saved into ``original'' field of AnnotationInfo (happens elsewhere).
+ * The field doesn't get pickled/unpickled and exists only during a single compilation run.
+ * This simultaneously allows us to reify annotations and to preserve backward compatibility.
*/
- def removeAnnotations(mods: Modifiers): Modifiers =
+ def typedModifiers(mods: Modifiers): Modifiers =
mods.copy(annotations = Nil) setPositions mods.positions
/**
@@ -1553,7 +1562,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// attributes(vdef)
val sym = vdef.symbol.initialize
val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor)
- val typedMods = removeAnnotations(vdef.mods)
+ val typedMods = typedModifiers(vdef.mods)
// complete lazy annotations
val annots = sym.annotations
@@ -1764,7 +1773,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt))
checkNonCyclic(ddef, tpt1)
ddef.tpt.setType(tpt1.tpe)
- val typedMods = removeAnnotations(ddef.mods)
+ val typedMods = typedModifiers(ddef.mods)
var rhs1 =
if (ddef.name == nme.CONSTRUCTOR && !ddef.symbol.hasStaticFlag) { // need this to make it possible to generate static ctors
if (!meth.isPrimaryConstructor &&
@@ -1813,7 +1822,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
tdef.symbol.initialize
reenterTypeParams(tdef.tparams)
val tparams1 = tdef.tparams mapConserve typedTypeDef
- val typedMods = removeAnnotations(tdef.mods)
+ val typedMods = typedModifiers(tdef.mods)
// complete lazy annotations
val annots = tdef.symbol.annotations
@@ -2770,7 +2779,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
if (hasError) annotationError
- else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setPos(ann.pos)
+ else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(ann).setPos(ann.pos)
}
} else if (requireJava) {
reportAnnotationError(NestedAnnotationError(ann, annType))
@@ -2810,7 +2819,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def annInfo(t: Tree): AnnotationInfo = t match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
- AnnotationInfo(annType, args, List()).setPos(t.pos)
+ AnnotationInfo(annType, args, List()).setOriginal(ann).setPos(t.pos)
case Block(stats, expr) =>
context.warning(t.pos, "Usage of named or default arguments transformed this annotation\n"+
diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala
index 32940cbcd6..2c960392ec 100644
--- a/src/library/scala/reflect/api/Trees.scala
+++ b/src/library/scala/reflect/api/Trees.scala
@@ -439,6 +439,12 @@ trait Trees { self: Universe =>
case class Assign(lhs: Tree, rhs: Tree)
extends TermTree
+ /** Either an assignment or a named argument. Only appears in argument lists,
+ * eliminated by typecheck (doTypedApply)
+ */
+ case class AssignOrNamedArg(lhs: Tree, rhs: Tree)
+ extends TermTree
+
/** Conditional expression */
case class If(cond: Tree, thenp: Tree, elsep: Tree)
extends TermTree
@@ -716,6 +722,8 @@ trait Trees { self: Universe =>
}
case Assign(lhs, rhs) =>
traverse(lhs); traverse(rhs)
+ case AssignOrNamedArg(lhs, rhs) =>
+ traverse(lhs); traverse(rhs)
case If(cond, thenp, elsep) =>
traverse(cond); traverse(thenp); traverse(elsep)
case Match(selector, cases) =>
@@ -814,6 +822,7 @@ trait Trees { self: Universe =>
def ArrayValue(tree: Tree, elemtpt: Tree, trees: List[Tree]): ArrayValue
def Function(tree: Tree, vparams: List[ValDef], body: Tree): Function
def Assign(tree: Tree, lhs: Tree, rhs: Tree): Assign
+ def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree): AssignOrNamedArg
def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree): If
def Match(tree: Tree, selector: Tree, cases: List[CaseDef]): Match
def Return(tree: Tree, expr: Tree): Return
@@ -876,6 +885,8 @@ trait Trees { self: Universe =>
new Function(vparams, body).copyAttrs(tree)
def Assign(tree: Tree, lhs: Tree, rhs: Tree) =
new Assign(lhs, rhs).copyAttrs(tree)
+ def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) =
+ new AssignOrNamedArg(lhs, rhs).copyAttrs(tree)
def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree) =
new If(cond, thenp, elsep).copyAttrs(tree)
def Match(tree: Tree, selector: Tree, cases: List[CaseDef]) =
@@ -1021,6 +1032,11 @@ trait Trees { self: Universe =>
if (lhs0 == lhs) && (rhs0 == rhs) => t
case _ => treeCopy.Assign(tree, lhs, rhs)
}
+ def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = tree match {
+ case t @ AssignOrNamedArg(lhs0, rhs0)
+ if (lhs0 == lhs) && (rhs0 == rhs) => t
+ case _ => treeCopy.AssignOrNamedArg(tree, lhs, rhs)
+ }
def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree) = tree match {
case t @ If(cond0, thenp0, elsep0)
if (cond0 == cond) && (thenp0 == thenp) && (elsep0 == elsep) => t
@@ -1205,6 +1221,8 @@ trait Trees { self: Universe =>
}
case Assign(lhs, rhs) =>
treeCopy.Assign(tree, transform(lhs), transform(rhs))
+ case AssignOrNamedArg(lhs, rhs) =>
+ treeCopy.AssignOrNamedArg(tree, transform(lhs), transform(rhs))
case If(cond, thenp, elsep) =>
treeCopy.If(tree, transform(cond), transform(thenp), transform(elsep))
case Match(selector, cases) =>
@@ -1372,6 +1390,8 @@ trait Trees { self: Universe =>
// vparams => body where vparams:List[ValDef]
case Assign(lhs, rhs) =>
// lhs = rhs
+ case AssignOrNamedArg(lhs, rhs) => (eliminated by typer, resurrected by reifier)
+ // @annotation(lhs = rhs)
case If(cond, thenp, elsep) =>
// if (cond) thenp else elsep
case Match(selector, cases) =>
diff --git a/test/files/neg/reify_ann2a.check b/test/files/neg/reify_ann2a.check
new file mode 100644
index 0000000000..2afe37e1d8
--- /dev/null
+++ b/test/files/neg/reify_ann2a.check
@@ -0,0 +1,4 @@
+reify_ann2a.scala:9: error: exception during macro expansion: implementation restriction: cannot reify annotation @ann(immutable.this.List.apply[String]("1a")) which involves a symbol declared inside the block being reified
+ val tree = scala.reflect.Code.lift{
+ ^
+one error found
diff --git a/test/files/neg/reify_ann2a.scala b/test/files/neg/reify_ann2a.scala
new file mode 100644
index 0000000000..071919eb76
--- /dev/null
+++ b/test/files/neg/reify_ann2a.scala
@@ -0,0 +1,30 @@
+import scala.reflect._
+import scala.reflect.api._
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ // test 1: reify
+ val tree = scala.reflect.Code.lift{
+ class ann(bar: List[String]) extends StaticAnnotation
+
+ @ann(bar=List("1a")) @ann(bar=List("1b")) class C[@ann(bar=List("2a")) @ann(bar=List("2b")) T](@ann(bar=List("3a")) @ann(bar=List("3b")) x: T @ann(bar=List("4a")) @ann(bar=List("4b"))) {
+ @ann(bar=List("5a")) @ann(bar=List("5b")) def f(x: Int @ann(bar=List("6a")) @ann(bar=List("6b"))) = {
+ @ann(bar=List("7a")) @ann(bar=List("7b")) val r = (x + 3): @ann(bar=List("8a")) @ann(bar=List("8b"))
+ val s = 4: Int @ann(bar=List("9a")) @ann(bar=List("9b"))
+ r + s
+ }
+ }
+ }.tree
+ println(tree.toString)
+
+ // test 2: import and typecheck
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(tree)
+ println(ttree.toString)
+
+ // test 3: import and compile
+ toolbox.runExpr(ttree)
+} \ No newline at end of file
diff --git a/test/files/neg/reify_ann2b.check b/test/files/neg/reify_ann2b.check
new file mode 100644
index 0000000000..ceb70689f1
--- /dev/null
+++ b/test/files/neg/reify_ann2b.check
@@ -0,0 +1,7 @@
+reify_ann2b.scala:10: error: inner classes cannot be classfile annotations
+ class ann(bar: String) extends ClassfileAnnotation
+ ^
+reify_ann2b.scala:9: error: exception during macro expansion: implementation restriction: cannot reify annotation @ann(bar = "1a") which involves a symbol declared inside the block being reified
+ val tree = scala.reflect.Code.lift{
+ ^
+two errors found
diff --git a/test/files/neg/reify_ann2b.scala b/test/files/neg/reify_ann2b.scala
new file mode 100644
index 0000000000..74273ad6ec
--- /dev/null
+++ b/test/files/neg/reify_ann2b.scala
@@ -0,0 +1,30 @@
+import scala.reflect._
+import scala.reflect.api._
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+object Test extends App {
+ // test 1: reify
+ val tree = scala.reflect.Code.lift{
+ class ann(bar: String) extends ClassfileAnnotation
+
+ @ann(bar="1a") @ann(bar="1b") class C[@ann(bar="2a") @ann(bar="2b") T](@ann(bar="3a") @ann(bar="3b") x: T @ann(bar="4a") @ann(bar="4b")) {
+ @ann(bar="5a") @ann(bar="5b") def f(x: Int @ann(bar="6a") @ann(bar="6b")) = {
+ @ann(bar="7a") @ann(bar="7b") val r = (x + 3): @ann(bar="8a") @ann(bar="8b")
+ val s = 4: Int @ann(bar="9a") @ann(bar="9b")
+ r + s
+ }
+ }
+ }.tree
+ println(tree.toString)
+
+ // test 2: import and typecheck
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(tree)
+ println(ttree.toString)
+
+ // test 3: import and compile
+ toolbox.runExpr(ttree)
+} \ No newline at end of file
diff --git a/test/files/run/reify_ann1a.check b/test/files/run/reify_ann1a.check
new file mode 100644
index 0000000000..2822238706
--- /dev/null
+++ b/test/files/run/reify_ann1a.check
@@ -0,0 +1,30 @@
+{
+ @new ann(immutable.this.List.apply[String]("1a")) @new ann(immutable.this.List.apply[String]("1b")) class C[@new ann(immutable.this.List.apply[String]("2a")) @new ann(immutable.this.List.apply[String]("2b")) T>: Nothing <: Any] extends Object with ScalaObject {
+ @new ann(immutable.this.List.apply[String]("3a")) @new ann(immutable.this.List.apply[String]("3b")) <paramaccessor> private[this] val x: T @ann(immutable.this.List.apply[String]("4a")) @ann(immutable.this.List.apply[String]("4b")) = _;
+ def <init>(@new ann(immutable.this.List.apply[String]("3a")) @new ann(immutable.this.List.apply[String]("3b")) x: T @ann(immutable.this.List.apply[String]("4a")) @ann(immutable.this.List.apply[String]("4b"))) = {
+ super.<init>();
+ ()
+ };
+ @new ann(immutable.this.List.apply[String]("5a")) @new ann(immutable.this.List.apply[String]("5b")) def f(x: Int @ann(immutable.this.List.apply[String]("6b")) @ann(immutable.this.List.apply[String]("6a"))): Int = {
+ @new ann(immutable.this.List.apply[String]("7a")) @new ann(immutable.this.List.apply[String]("7b")) val r: Int @ann(immutable.this.List.apply[String]("8b")) @ann(immutable.this.List.apply[String]("8a")) = ((x.$plus(3): Int @ann(immutable.this.List.apply[String]("8a"))): Int @ann(immutable.this.List.apply[String]("8b")) @ann(immutable.this.List.apply[String]("8a")));
+ val s: Int @ann(immutable.this.List.apply[String]("9b")) @ann(immutable.this.List.apply[String]("9a")) = (4: Int @ann(immutable.this.List.apply[String]("9b")) @ann(immutable.this.List.apply[String]("9a")));
+ r.$plus(s)
+ }
+ };
+ ()
+}
+{
+ @ann(immutable.this.List.apply[String]("1a")) @ann(immutable.this.List.apply[String]("1b")) class C[@ann(immutable.this.List.apply[String]("2a")) @ann(immutable.this.List.apply[String]("2b")) T>: Nothing <: Any] extends Object with ScalaObject {
+ @ann(immutable.this.List.apply[String]("3a")) @ann(immutable.this.List.apply[String]("3b")) <paramaccessor> private[this] val x: T @ann(immutable.this.List.apply[String]("4b")) @ann(immutable.this.List.apply[String]("4a")) = _;
+ def <init>(@ann(immutable.this.List.apply[String]("3a")) @ann(immutable.this.List.apply[String]("3b")) x: T @ann(immutable.this.List.apply[String]("4b")) @ann(immutable.this.List.apply[String]("4a"))): C[T] = {
+ C.super.<init>();
+ ()
+ };
+ @ann(immutable.this.List.apply[String]("5a")) @ann(immutable.this.List.apply[String]("5b")) def f(x: Int @ann(immutable.this.List.apply[String]("6b")) @ann(immutable.this.List.apply[String]("6a"))): Int = {
+ @ann(immutable.this.List.apply[String]("7a")) @ann(immutable.this.List.apply[String]("7b")) val r: Int @ann(immutable.this.List.apply[String]("8b")) @ann(immutable.this.List.apply[String]("8a")) = ((x.+(3): Int @ann(immutable.this.List.apply[String]("8a"))): Int @ann(immutable.this.List.apply[String]("8b")) @ann(immutable.this.List.apply[String]("8a")));
+ val s: Int @ann(immutable.this.List.apply[String]("9b")) @ann(immutable.this.List.apply[String]("9a")) = (4: Int @ann(immutable.this.List.apply[String]("9b")) @ann(immutable.this.List.apply[String]("9a")));
+ r.+(s)
+ }
+ };
+ ()
+}
diff --git a/test/files/run/reify_ann1a.scala b/test/files/run/reify_ann1a.scala
new file mode 100644
index 0000000000..933ea21b20
--- /dev/null
+++ b/test/files/run/reify_ann1a.scala
@@ -0,0 +1,30 @@
+import scala.reflect._
+import scala.reflect.api._
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+class ann(bar: List[String]) extends StaticAnnotation
+
+object Test extends App {
+ // test 1: reify
+ val tree = scala.reflect.Code.lift{
+ @ann(bar=List("1a")) @ann(bar=List("1b")) class C[@ann(bar=List("2a")) @ann(bar=List("2b")) T](@ann(bar=List("3a")) @ann(bar=List("3b")) x: T @ann(bar=List("4a")) @ann(bar=List("4b"))) {
+ @ann(bar=List("5a")) @ann(bar=List("5b")) def f(x: Int @ann(bar=List("6a")) @ann(bar=List("6b"))) = {
+ @ann(bar=List("7a")) @ann(bar=List("7b")) val r = (x + 3): @ann(bar=List("8a")) @ann(bar=List("8b"))
+ val s = 4: Int @ann(bar=List("9a")) @ann(bar=List("9b"))
+ r + s
+ }
+ }
+ }.tree
+ println(tree.toString)
+
+ // test 2: import and typecheck
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(tree)
+ println(ttree.toString)
+
+ // test 3: import and compile
+ toolbox.runExpr(ttree)
+} \ No newline at end of file
diff --git a/test/files/run/reify_ann1b.check b/test/files/run/reify_ann1b.check
new file mode 100644
index 0000000000..e240e1e0ce
--- /dev/null
+++ b/test/files/run/reify_ann1b.check
@@ -0,0 +1,30 @@
+{
+ @new ann(bar = "1a") @new ann(bar = "1b") class C[@new ann(bar = "2a") @new ann(bar = "2b") T>: Nothing <: Any] extends Object with ScalaObject {
+ @new ann(bar = "3a") @new ann(bar = "3b") <paramaccessor> private[this] val x: T @ann(bar = "4a") @ann(bar = "4b") = _;
+ def <init>(@new ann(bar = "3a") @new ann(bar = "3b") x: T @ann(bar = "4a") @ann(bar = "4b")) = {
+ super.<init>();
+ ()
+ };
+ @new ann(bar = "5a") @new ann(bar = "5b") def f(x: Int @ann(bar = "6b") @ann(bar = "6a")): Int = {
+ @new ann(bar = "7a") @new ann(bar = "7b") val r: Int @ann(bar = "8b") @ann(bar = "8a") = ((x.$plus(3): Int @ann(bar = "8a")): Int @ann(bar = "8b") @ann(bar = "8a"));
+ val s: Int @ann(bar = "9b") @ann(bar = "9a") = (4: Int @ann(bar = "9b") @ann(bar = "9a"));
+ r.$plus(s)
+ }
+ };
+ ()
+}
+{
+ @ann(bar = "1a") @ann(bar = "1b") class C[@ann(bar = "2a") @ann(bar = "2b") T>: Nothing <: Any] extends Object with ScalaObject {
+ @ann(bar = "3a") @ann(bar = "3b") <paramaccessor> private[this] val x: T @ann(bar = "4b") @ann(bar = "4a") = _;
+ def <init>(@ann(bar = "3a") @ann(bar = "3b") x: T @ann(bar = "4b") @ann(bar = "4a")): C[T] = {
+ C.super.<init>();
+ ()
+ };
+ @ann(bar = "5a") @ann(bar = "5b") def f(x: Int @ann(bar = "6b") @ann(bar = "6a")): Int = {
+ @ann(bar = "7a") @ann(bar = "7b") val r: Int @ann(bar = "8b") @ann(bar = "8a") = ((x.+(3): Int @ann(bar = "8a")): Int @ann(bar = "8b") @ann(bar = "8a"));
+ val s: Int @ann(bar = "9b") @ann(bar = "9a") = (4: Int @ann(bar = "9b") @ann(bar = "9a"));
+ r.+(s)
+ }
+ };
+ ()
+}
diff --git a/test/files/run/reify_ann1b.scala b/test/files/run/reify_ann1b.scala
new file mode 100644
index 0000000000..53dfe08086
--- /dev/null
+++ b/test/files/run/reify_ann1b.scala
@@ -0,0 +1,30 @@
+import scala.reflect._
+import scala.reflect.api._
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+class ann(bar: String) extends ClassfileAnnotation
+
+object Test extends App {
+ // test 1: reify
+ val tree = scala.reflect.Code.lift{
+ @ann(bar="1a") @ann(bar="1b") class C[@ann(bar="2a") @ann(bar="2b") T](@ann(bar="3a") @ann(bar="3b") x: T @ann(bar="4a") @ann(bar="4b")) {
+ @ann(bar="5a") @ann(bar="5b") def f(x: Int @ann(bar="6a") @ann(bar="6b")) = {
+ @ann(bar="7a") @ann(bar="7b") val r = (x + 3): @ann(bar="8a") @ann(bar="8b")
+ val s = 4: Int @ann(bar="9a") @ann(bar="9b")
+ r + s
+ }
+ }
+ }.tree
+ println(tree.toString)
+
+ // test 2: import and typecheck
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(tree)
+ println(ttree.toString)
+
+ // test 3: import and compile
+ toolbox.runExpr(ttree)
+} \ No newline at end of file
diff --git a/test/files/run/reify_classfileann_a.check b/test/files/run/reify_classfileann_a.check
new file mode 100644
index 0000000000..1773263a94
--- /dev/null
+++ b/test/files/run/reify_classfileann_a.check
@@ -0,0 +1,18 @@
+{
+ @new ann(bar = "1", quux = Array("2", "3"), baz = new ann(bar = "4")) class C extends Object with ScalaObject {
+ def <init>() = {
+ super.<init>();
+ ()
+ }
+ };
+ ()
+}
+{
+ @ann(bar = "1", quux = ["2", "3"], baz = ann(bar = "4")) class C extends Object with ScalaObject {
+ def <init>(): C = {
+ C.super.<init>();
+ ()
+ }
+ };
+ ()
+}
diff --git a/test/files/run/reify_classfileann_a.scala b/test/files/run/reify_classfileann_a.scala
new file mode 100644
index 0000000000..6bf4750bbc
--- /dev/null
+++ b/test/files/run/reify_classfileann_a.scala
@@ -0,0 +1,24 @@
+import scala.reflect._
+import scala.reflect.api._
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+class ann(bar: String, quux: Array[String] = Array(), baz: ann = null) extends ClassfileAnnotation
+
+object Test extends App {
+ // test 1: reify
+ val tree = scala.reflect.Code.lift{
+ @ann(bar="1", quux=Array("2", "3"), baz = new ann(bar = "4")) class C
+ }.tree
+ println(tree.toString)
+
+ // test 2: import and typecheck
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(tree)
+ println(ttree.toString)
+
+ // test 3: import and compile
+ toolbox.runExpr(ttree)
+} \ No newline at end of file
diff --git a/test/pending/run/t5224.check b/test/files/run/t5224.check
index 2b920773c0..5bead91b36 100644
--- a/test/pending/run/t5224.check
+++ b/test/files/run/t5224.check
@@ -1,9 +1,9 @@
{
- @serializable class C extends Object with ScalaObject {
+ @new Foo(bar = "qwe") class C extends Object with ScalaObject {
def <init>() = {
super.<init>();
()
}
};
()
-} \ No newline at end of file
+}
diff --git a/test/files/run/t5224.scala b/test/files/run/t5224.scala
new file mode 100644
index 0000000000..2226a69a05
--- /dev/null
+++ b/test/files/run/t5224.scala
@@ -0,0 +1,9 @@
+import scala.reflect._
+import scala.reflect.api._
+
+class Foo(bar: String) extends ClassfileAnnotation
+
+object Test extends App {
+ val tree = scala.reflect.Code.lift{@Foo(bar = "qwe") class C}.tree
+ println(tree.toString)
+} \ No newline at end of file
diff --git a/test/files/run/t5225_1.check b/test/files/run/t5225_1.check
new file mode 100644
index 0000000000..719da572c7
--- /dev/null
+++ b/test/files/run/t5225_1.check
@@ -0,0 +1,4 @@
+{
+ @new transient() @new volatile() var x: Int = 2;
+ ()
+}
diff --git a/test/files/run/t5225_1.scala b/test/files/run/t5225_1.scala
new file mode 100644
index 0000000000..a655b7dd71
--- /dev/null
+++ b/test/files/run/t5225_1.scala
@@ -0,0 +1,7 @@
+import scala.reflect._
+import scala.reflect.api._
+
+object Test extends App {
+ val tree = scala.reflect.Code.lift{@transient @volatile var x = 2}.tree
+ println(tree.toString)
+} \ No newline at end of file
diff --git a/test/files/run/t5225_2.check b/test/files/run/t5225_2.check
new file mode 100644
index 0000000000..c4f6b4761e
--- /dev/null
+++ b/test/files/run/t5225_2.check
@@ -0,0 +1,4 @@
+{
+ def foo(@new cloneable() x: Int): String = "";
+ ()
+}
diff --git a/test/files/run/t5225_2.scala b/test/files/run/t5225_2.scala
new file mode 100644
index 0000000000..65ea9b2f73
--- /dev/null
+++ b/test/files/run/t5225_2.scala
@@ -0,0 +1,7 @@
+import scala.reflect._
+import scala.reflect.api._
+
+object Test extends App {
+ val tree = scala.reflect.Code.lift{def foo(@cloneable x: Int) = ""}.tree
+ println(tree.toString)
+} \ No newline at end of file
diff --git a/test/files/run/t5419.check b/test/files/run/t5419.check
new file mode 100644
index 0000000000..7e6d739354
--- /dev/null
+++ b/test/files/run/t5419.check
@@ -0,0 +1 @@
+(5: Int(5) @Foo)
diff --git a/test/files/run/t5419.scala b/test/files/run/t5419.scala
new file mode 100644
index 0000000000..695786e5c4
--- /dev/null
+++ b/test/files/run/t5419.scala
@@ -0,0 +1,9 @@
+import scala.reflect._
+import scala.reflect.api._
+
+class Foo extends StaticAnnotation
+
+object Test extends App {
+ val tree = scala.reflect.Code.lift{5: @Foo}.tree
+ println(tree.toString)
+} \ No newline at end of file
diff --git a/test/pending/run/reify_classfileann_b.check b/test/pending/run/reify_classfileann_b.check
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/pending/run/reify_classfileann_b.check
diff --git a/test/pending/run/reify_classfileann_b.scala b/test/pending/run/reify_classfileann_b.scala
new file mode 100644
index 0000000000..b76dd8fc9f
--- /dev/null
+++ b/test/pending/run/reify_classfileann_b.scala
@@ -0,0 +1,28 @@
+import scala.reflect._
+import scala.reflect.api._
+import scala.tools.nsc.reporters._
+import scala.tools.nsc.Settings
+import reflect.runtime.Mirror.ToolBox
+
+class ann(bar: String, quux: Array[String] = Array(), baz: ann = null) extends ClassfileAnnotation
+
+object Test extends App {
+ // test 1: reify
+ val tree = scala.reflect.Code.lift{
+ class C {
+ def x: Int = {
+ 2: @ann(bar="1", quux=Array("2", "3"), baz = new ann(bar = "4"))
+ }
+ }
+ }.tree
+ println(tree.toString)
+
+ // test 2: import and typecheck
+ val reporter = new ConsoleReporter(new Settings)
+ val toolbox = new ToolBox(reporter)
+ val ttree = toolbox.typeCheck(tree)
+ println(ttree.toString)
+
+ // test 3: import and compile
+ toolbox.runExpr(ttree)
+} \ No newline at end of file
diff --git a/test/pending/run/t5224.scala b/test/pending/run/t5224.scala
deleted file mode 100644
index 865ce4bfe9..0000000000
--- a/test/pending/run/t5224.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-import scala.reflect._
-import scala.reflect.api._
-
-object Test extends App {
- println(scala.reflect.Code.lift{
- @serializable class C
- }.tree.toString)
-} \ No newline at end of file
diff --git a/test/pending/run/t5225_1.check b/test/pending/run/t5225_1.check
deleted file mode 100644
index b29cd9c365..0000000000
--- a/test/pending/run/t5225_1.check
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- @transient @volatile var x: Int = 2;
- ()
-} \ No newline at end of file
diff --git a/test/pending/run/t5225_1.scala b/test/pending/run/t5225_1.scala
deleted file mode 100644
index 454502e810..0000000000
--- a/test/pending/run/t5225_1.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-import scala.reflect._
-import scala.reflect.api._
-
-object Test extends App {
- println(scala.reflect.Code.lift{
- @transient @volatile var x = 2
- }.tree.toString)
-} \ No newline at end of file
diff --git a/test/pending/run/t5225_2.check b/test/pending/run/t5225_2.check
deleted file mode 100644
index 88972fd27f..0000000000
--- a/test/pending/run/t5225_2.check
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- def foo(@cloneable x: Int): String = "";
- ()
-}
diff --git a/test/pending/run/t5225_2.scala b/test/pending/run/t5225_2.scala
deleted file mode 100644
index 82bad0f353..0000000000
--- a/test/pending/run/t5225_2.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-import scala.reflect._
-import scala.reflect.api._
-
-object Test extends App {
- println(scala.reflect.Code.lift{
- def foo(@cloneable x: Int) = ""
- }.tree.toString)
-} \ No newline at end of file