summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-04-15 22:27:42 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-04-17 21:34:39 +0200
commit40c1fe388be22c0ea9c68afcb08b41b69148026e (patch)
tree6849e9cbaae7dbf18f84ac07383cdbc525570ea4
parentb37350b4126a1030d1060fd982d2ade6e2e5bd8e (diff)
downloadscala-40c1fe388be22c0ea9c68afcb08b41b69148026e.tar.gz
scala-40c1fe388be22c0ea9c68afcb08b41b69148026e.tar.bz2
scala-40c1fe388be22c0ea9c68afcb08b41b69148026e.zip
assorted stability fixes
-rw-r--r--src/compiler/scala/reflect/internal/StdAttachments.scala12
-rw-r--r--src/compiler/scala/reflect/internal/SymbolTable.scala1
-rw-r--r--src/compiler/scala/reflect/makro/runtime/Enclosures.scala2
-rw-r--r--src/compiler/scala/reflect/makro/runtime/Typers.scala10
-rw-r--r--src/compiler/scala/reflect/reify/codegen/Symbols.scala23
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala14
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala730
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala8
-rw-r--r--src/library/scala/reflect/api/Attachment.scala9
-rw-r--r--src/library/scala/reflect/api/Trees.scala43
-rw-r--r--test/pending/run/macro-expand-implicit-macro-defeats-type-inference.check6
-rw-r--r--test/pending/run/macro-expand-implicit-macro-defeats-type-inference.flags1
-rw-r--r--test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Impls_1.scala10
-rw-r--r--test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Macros_Test_2.scala6
16 files changed, 510 insertions, 371 deletions
diff --git a/src/compiler/scala/reflect/internal/StdAttachments.scala b/src/compiler/scala/reflect/internal/StdAttachments.scala
new file mode 100644
index 0000000000..488195b7dd
--- /dev/null
+++ b/src/compiler/scala/reflect/internal/StdAttachments.scala
@@ -0,0 +1,12 @@
+package scala.reflect
+package internal
+
+import scala.reflect.makro.runtime.{Context => MacroContext}
+
+trait StdAttachments {
+ self: SymbolTable =>
+
+ case class ReifyAttachment(original: Symbol)
+
+ case class MacroAttachment(delayed: Boolean, context: Option[MacroContext])
+} \ No newline at end of file
diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala
index 0268339ed0..0688d13ae5 100644
--- a/src/compiler/scala/reflect/internal/SymbolTable.scala
+++ b/src/compiler/scala/reflect/internal/SymbolTable.scala
@@ -38,6 +38,7 @@ abstract class SymbolTable extends api.Universe
with TreeBuildUtil
with Reporters
with CapturedVariables
+ with StdAttachments
{
def rootLoader: LazyType
def log(msg: => AnyRef): Unit
diff --git a/src/compiler/scala/reflect/makro/runtime/Enclosures.scala b/src/compiler/scala/reflect/makro/runtime/Enclosures.scala
index f9a6987e48..72e9e568c0 100644
--- a/src/compiler/scala/reflect/makro/runtime/Enclosures.scala
+++ b/src/compiler/scala/reflect/makro/runtime/Enclosures.scala
@@ -11,7 +11,7 @@ trait Enclosures {
val macroApplication: Tree = expandee
- val enclosingMacros: List[Context] = this :: mirror.analyzer.openMacros
+ val enclosingMacros: List[Context] = this :: mirror.analyzer.openMacros // include self
val enclosingImplicits: List[(Type, Tree)] = callsiteTyper.context.openImplicits
diff --git a/src/compiler/scala/reflect/makro/runtime/Typers.scala b/src/compiler/scala/reflect/makro/runtime/Typers.scala
index b32d4fb7b1..98dbd65b72 100644
--- a/src/compiler/scala/reflect/makro/runtime/Typers.scala
+++ b/src/compiler/scala/reflect/makro/runtime/Typers.scala
@@ -4,9 +4,9 @@ package runtime
trait Typers {
self: Context =>
- val openMacros: List[Context] = this :: mirror.analyzer.openMacros
+ def openMacros: List[Context] = this :: mirror.analyzer.openMacros
- val openImplicits: List[(Type, Tree)] = callsiteTyper.context.openImplicits
+ def openImplicits: List[(Type, Tree)] = callsiteTyper.context.openImplicits
def typeCheck(tree: Tree, pt: Type = mirror.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = {
def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg)
@@ -34,7 +34,7 @@ trait Typers {
def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg)
trace("inferring implicit value of type %s, macros = %s".format(pt, !withMacrosDisabled))
import mirror.analyzer.SearchResult
- val context = callsiteTyper.context.makeImplicit(true)
+ val context = callsiteTyper.context
val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _)
def wrapper (inference: => SearchResult) = wrapper1(inference)
wrapper(mirror.analyzer.inferImplicit(mirror.EmptyTree, pt, true, false, context, !silent, pos)) match {
@@ -51,12 +51,12 @@ trait Typers {
def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg)
trace("inferring implicit view from %s to %s for %s, macros = %s, reportAmbiguous = %s".format(from, to, tree, !withMacrosDisabled, reportAmbiguous))
import mirror.analyzer.SearchResult
- val context = callsiteTyper.context.makeImplicit(reportAmbiguous)
+ val context = callsiteTyper.context
val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _)
def wrapper (inference: => SearchResult) = wrapper1(inference)
val fun1 = mirror.definitions.FunctionClass(1)
val viewTpe = mirror.TypeRef(fun1.typeConstructor.prefix, fun1, List(from, to))
- wrapper(mirror.analyzer.inferImplicit(mirror.EmptyTree, viewTpe, reportAmbiguous, true, context, !silent, pos)) match {
+ wrapper(mirror.analyzer.inferImplicit(tree, viewTpe, reportAmbiguous, true, context, !silent, pos)) match {
case failure if failure.tree.isEmpty =>
trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits")
if (context.hasErrors) throw new mirror.TypeError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg)
diff --git a/src/compiler/scala/reflect/reify/codegen/Symbols.scala b/src/compiler/scala/reflect/reify/codegen/Symbols.scala
index 0513f99366..7f8b9c53b6 100644
--- a/src/compiler/scala/reflect/reify/codegen/Symbols.scala
+++ b/src/compiler/scala/reflect/reify/codegen/Symbols.scala
@@ -105,9 +105,9 @@ trait Symbols {
filledIn = false
newSymbolTable foreach {
case entry =>
- val att = entry.attachment
+ val att = entry.attachmentOpt[ReifyAttachment]
att match {
- case sym: Symbol =>
+ case Some(ReifyAttachment(sym)) =>
// don't duplicate reified symbols when merging inlined reifee
if (!(locallyReified contains sym)) {
val ValDef(_, name, _, _) = entry
@@ -134,7 +134,7 @@ trait Symbols {
// todo. tried to declare a private class here to carry an attachment, but it's path-dependent
// so got troubles with exchanging free variables between nested and enclosing quasiquotes
// attaching just Symbol isn't good either, so we need to think of a principled solution
- val local = ValDef(NoMods, name, TypeTree(), reified) setAttachment sym
+ val local = ValDef(NoMods, name, TypeTree(), reified) withAttachment ReifyAttachment(sym)
localReifications += local
filledIn = false
locallyReified(sym) = Ident(name)
@@ -149,8 +149,9 @@ trait Symbols {
while (i < localReifications.length) {
// fillInSymbol might create new locallyReified symbols, that's why this is done iteratively
val reified = localReifications(i)
- reified.attachment match {
- case sym: Symbol => fillIns += fillInSymbol(sym)
+ val att = reified.attachmentOpt[ReifyAttachment]
+ att match {
+ case Some(ReifyAttachment(sym)) => fillIns += fillInSymbol(sym)
case other => // do nothing
}
i += 1
@@ -169,9 +170,15 @@ trait Symbols {
if (sym.annotations.isEmpty) EmptyTree
else Apply(Select(locallyReified(sym), nme.setAnnotations), List(reify(sym.annotations)))
} else {
- val rset = Apply(Select(locallyReified(sym), nme.setTypeSignature), List(reify(sym.info)))
- if (sym.annotations.isEmpty) rset
- else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations)))
+ import scala.reflect.internal.Flags._
+ if (sym hasFlag LOCKED) {
+ // [Eugene] better to have a symbol without a type signature, than to crash with a CyclicReference
+ EmptyTree
+ } else {
+ val rset = Apply(Select(locallyReified(sym), nme.setTypeSignature), List(reify(sym.info)))
+ if (sym.annotations.isEmpty) rset
+ else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations)))
+ }
}
}
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 73c68f44d4..c9394b37b5 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -8,7 +8,6 @@ package scala.tools.nsc
import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundException }
import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException }
import compat.Platform.currentTime
-
import scala.tools.util.{ Profiling, PathResolver }
import scala.collection.{ mutable, immutable }
import io.{ SourceReader, AbstractFile, Path }
@@ -16,7 +15,6 @@ import reporters.{ Reporter => NscReporter, ConsoleReporter }
import util.{ NoPosition, Exceptional, ClassPath, SourceFile, NoSourceFile, Statistics, StatisticsInfo, BatchSourceFile, ScriptSourceFile, ShowPickled, ScalaClassLoader, returning }
import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat }
import settings.{ AestheticSettings }
-
import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers }
import symtab.classfile.Pickler
import dependencies.DependencyAnalysis
@@ -25,13 +23,13 @@ import ast._
import ast.parser._
import typechecker._
import transform._
-
import backend.icode.{ ICodes, GenICode, ICodeCheckers }
import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform }
import backend.jvm.GenJVM
import backend.opt.{ Inliners, InlineExceptionHandlers, ClosureElimination, DeadCodeElimination }
import backend.icode.analysis._
import language.postfixOps
+import reflect.internal.StdAttachments
class Global(var currentSettings: Settings, var reporter: NscReporter) extends SymbolTable
with ClassLoaders
@@ -135,6 +133,16 @@ class Global(var currentSettings: Settings, var reporter: NscReporter) extends S
infolevel = InfoLevel.Verbose
}
+ def withInfoLevel[T](infolevel: nodePrinters.InfoLevel.Value)(op: => T) = {
+ val saved = nodePrinters.infolevel
+ try {
+ nodePrinters.infolevel = infolevel
+ op
+ } finally {
+ nodePrinters.infolevel = saved
+ }
+ }
+
/** Representing ASTs as graphs */
object treeBrowsers extends {
val global: Global.this.type = Global.this
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
index 0e44933ac6..4e03dc8788 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
@@ -246,6 +246,7 @@ trait ModelFactoryImplicitSupport {
val appliedTree = new ApplyImplicitView(viewTree, List(Ident("<argument>") setType viewTree.tpe.paramTypes.head))
val appliedTreeTyped: Tree = {
val newContext = context.makeImplicit(context.ambiguousErrors)
+ newContext.macrosEnabled = false // [Eugene] I assume you want macro signature, not macro expansion
val newTyper = global.analyzer.newTyper(newContext)
newTyper.silent(_.typed(appliedTree, global.analyzer.EXPRmode, WildcardType), false) match {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 30a79917c9..651120db4f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -32,10 +32,10 @@ trait Implicits {
import global.typer.{ printTyping, deindentTyping, indentTyping, printInference }
def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult =
- inferImplicit(tree, pt, reportAmbiguous, isView, context, true, NoPosition)
+ inferImplicit(tree, pt, reportAmbiguous, isView, context, true, tree.pos)
def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult =
- inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, NoPosition)
+ inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, tree.pos)
/** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch`
* for more info how the search is conducted.
@@ -633,6 +633,7 @@ trait Implicits {
else {
val subst = new TreeTypeSubstituter(okParams, okArgs)
subst traverse itree2
+ notifyUndetparamsInferred(okParams, okArgs)
subst
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 62a0e08aad..539ddfb19c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -294,6 +294,8 @@ trait Macros { self: Analyzer =>
} finally {
openMacros = openMacros.tail
}
+ case Delay(result) =>
+ result
case Fallback(fallback) =>
typer.typed1(fallback, EXPRmode, WildcardType)
case Other(result) =>
@@ -641,139 +643,147 @@ trait Macros { self: Analyzer =>
* @return Some(runtime) if macro implementation can be loaded successfully from either of the mirrors,
* None otherwise.
*/
- private def macroRuntime(macroDef: Symbol): Option[List[Any] => Any] = {
- macroTrace("looking for macro implementation: ")(macroDef)
- macroTrace("macroDef is annotated with: ")(macroDef.annotations)
-
- val ann = macroDef.getAnnotation(MacroImplAnnotation)
- if (ann == None) {
- macroTrace("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef)
- return None
- }
-
- val macroImpl = ann.get.args(0).symbol
- if (macroImpl == NoSymbol) {
- macroTrace("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef)
- return None
- }
-
- if (macroDebug) println("resolved implementation %s at %s".format(macroImpl, macroImpl.pos))
- if (macroImpl.isErroneous) {
- macroTrace("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef)
- return None
- }
-
- def loadMacroImpl(macroMirror: Mirror): Option[(Object, macroMirror.Symbol)] = {
- try {
- // this logic relies on the assumptions that were valid for the old macro prototype
- // namely that macro implementations can only be defined in top-level classes and modules
- // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different
- // for example, a macro def could be defined in a trait that is implemented by an object
- // there are some more clever cases when seemingly non-static method ends up being statically accessible
- // however, the code below doesn't account for these guys, because it'd take a look of time to get it right
- // for now I leave it as a todo and move along to more the important stuff
-
- macroTrace("loading implementation class from %s: ".format(macroMirror))(macroImpl.owner.fullName)
- macroTrace("classloader is: ")("%s of type %s".format(macroMirror.classLoader, if (macroMirror.classLoader != null) macroMirror.classLoader.getClass.toString else "primordial classloader"))
- def inferClasspath(cl: ClassLoader) = cl match {
- case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
- case null => "[" + scala.tools.util.PathResolver.Environment.javaBootClassPath + "]"
- case _ => "<unknown>"
+ private type MacroRuntime = List[Any] => Any
+ private val macroRuntimesCache = perRunCaches.newWeakMap[Symbol, Option[MacroRuntime]]
+ private def macroRuntime(macroDef: Symbol): Option[MacroRuntime] =
+ macroRuntimesCache.getOrElseUpdate(macroDef, {
+ val runtime = {
+ macroTrace("looking for macro implementation: ")(macroDef)
+ macroTrace("macroDef is annotated with: ")(macroDef.annotations)
+
+ val ann = macroDef.getAnnotation(MacroImplAnnotation)
+ if (ann == None) {
+ macroTrace("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef)
+ return None
}
- macroTrace("classpath is: ")(inferClasspath(macroMirror.classLoader))
-
- // [Eugene] relies on the fact that macro implementations can only be defined in static classes
- // [Martin to Eugene] There's similar logic buried in Symbol#flatname. Maybe we can refactor?
- def classfile(sym: Symbol): String = {
- def recur(sym: Symbol): String = sym match {
- case sym if sym.owner.isPackageClass =>
- val suffix = if (sym.isModuleClass) "$" else ""
- sym.fullName + suffix
- case sym =>
- val separator = if (sym.owner.isModuleClass) "" else "$"
- recur(sym.owner) + separator + sym.javaSimpleName.toString
- }
- if (sym.isClass || sym.isModule) recur(sym)
- else recur(sym.enclClass)
+ val macroImpl = ann.get.args(0).symbol
+ if (macroImpl == NoSymbol) {
+ macroTrace("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef)
+ return None
}
- // [Eugene] this doesn't work for inner classes
- // neither does macroImpl.owner.javaClassName, so I had to roll my own implementation
- //val receiverName = macroImpl.owner.fullName
- val implClassName = classfile(macroImpl.owner)
- val implClassSymbol: macroMirror.Symbol = macroMirror.symbolForName(implClassName)
-
- if (macroDebug) {
- println("implClassSymbol is: " + implClassSymbol.fullNameString)
-
- if (implClassSymbol != macroMirror.NoSymbol) {
- val implClass = macroMirror.classToJava(implClassSymbol)
- val implSource = implClass.getProtectionDomain.getCodeSource
- println("implClass is %s from %s".format(implClass, implSource))
- println("implClassLoader is %s with classpath %s".format(implClass.getClassLoader, inferClasspath(implClass.getClassLoader)))
- }
+ if (macroDebug) println("resolved implementation %s at %s".format(macroImpl, macroImpl.pos))
+ if (macroImpl.isErroneous) {
+ macroTrace("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef)
+ return None
}
- val implObjSymbol = implClassSymbol.companionModule
- macroTrace("implObjSymbol is: ")(implObjSymbol.fullNameString)
+ def loadMacroImpl(macroMirror: Mirror): Option[(Object, macroMirror.Symbol)] = {
+ try {
+ // this logic relies on the assumptions that were valid for the old macro prototype
+ // namely that macro implementations can only be defined in top-level classes and modules
+ // with the new prototype that materialized in a SIP, macros need to be statically accessible, which is different
+ // for example, a macro def could be defined in a trait that is implemented by an object
+ // there are some more clever cases when seemingly non-static method ends up being statically accessible
+ // however, the code below doesn't account for these guys, because it'd take a look of time to get it right
+ // for now I leave it as a todo and move along to more the important stuff
+
+ macroTrace("loading implementation class from %s: ".format(macroMirror))(macroImpl.owner.fullName)
+ macroTrace("classloader is: ")("%s of type %s".format(macroMirror.classLoader, if (macroMirror.classLoader != null) macroMirror.classLoader.getClass.toString else "primordial classloader"))
+ def inferClasspath(cl: ClassLoader) = cl match {
+ case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
+ case null => "[" + scala.tools.util.PathResolver.Environment.javaBootClassPath + "]"
+ case _ => "<unknown>"
+ }
+ macroTrace("classpath is: ")(inferClasspath(macroMirror.classLoader))
+
+ // [Eugene] relies on the fact that macro implementations can only be defined in static classes
+ // [Martin to Eugene] There's similar logic buried in Symbol#flatname. Maybe we can refactor?
+ def classfile(sym: Symbol): String = {
+ def recur(sym: Symbol): String = sym match {
+ case sym if sym.owner.isPackageClass =>
+ val suffix = if (sym.isModuleClass) "$" else ""
+ sym.fullName + suffix
+ case sym =>
+ val separator = if (sym.owner.isModuleClass) "" else "$"
+ recur(sym.owner) + separator + sym.javaSimpleName.toString
+ }
- if (implObjSymbol == macroMirror.NoSymbol) None
- else {
- // yet another reflection method that doesn't work for inner classes
- //val receiver = macroMirror.companionInstance(receiverClass)
- val implObj = try {
- val implObjClass = java.lang.Class.forName(implClassName, true, macroMirror.classLoader)
- implObjClass getField "MODULE$" get null
- } catch {
- case ex: NoSuchFieldException => macroTrace("exception when loading implObj: ")(ex); null
- case ex: NoClassDefFoundError => macroTrace("exception when loading implObj: ")(ex); null
- case ex: ClassNotFoundException => macroTrace("exception when loading implObj: ")(ex); null
- }
+ if (sym.isClass || sym.isModule) recur(sym)
+ else recur(sym.enclClass)
+ }
+
+ // [Eugene] this doesn't work for inner classes
+ // neither does macroImpl.owner.javaClassName, so I had to roll my own implementation
+ //val receiverName = macroImpl.owner.fullName
+ val implClassName = classfile(macroImpl.owner)
+ val implClassSymbol: macroMirror.Symbol = macroMirror.symbolForName(implClassName)
- if (implObj == null) None
- else {
- val implMethSymbol = implObjSymbol.info.member(macroMirror.newTermName(macroImpl.name.toString))
if (macroDebug) {
- println("implMethSymbol is: " + implMethSymbol.fullNameString)
- println("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol))
+ println("implClassSymbol is: " + implClassSymbol.fullNameString)
+
+ if (implClassSymbol != macroMirror.NoSymbol) {
+ val implClass = macroMirror.classToJava(implClassSymbol)
+ val implSource = implClass.getProtectionDomain.getCodeSource
+ println("implClass is %s from %s".format(implClass, implSource))
+ println("implClassLoader is %s with classpath %s".format(implClass.getClassLoader, inferClasspath(implClass.getClassLoader)))
+ }
}
- if (implMethSymbol == macroMirror.NoSymbol) None
+ val implObjSymbol = implClassSymbol.companionModule
+ macroTrace("implObjSymbol is: ")(implObjSymbol.fullNameString)
+
+ if (implObjSymbol == macroMirror.NoSymbol) None
else {
- if (macroDebug) println("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol))
- Some((implObj, implMethSymbol))
+ // yet another reflection method that doesn't work for inner classes
+ //val receiver = macroMirror.companionInstance(receiverClass)
+ val implObj = try {
+ val implObjClass = java.lang.Class.forName(implClassName, true, macroMirror.classLoader)
+ implObjClass getField "MODULE$" get null
+ } catch {
+ case ex: NoSuchFieldException => macroTrace("exception when loading implObj: ")(ex); null
+ case ex: NoClassDefFoundError => macroTrace("exception when loading implObj: ")(ex); null
+ case ex: ClassNotFoundException => macroTrace("exception when loading implObj: ")(ex); null
+ }
+
+ if (implObj == null) None
+ else {
+ val implMethSymbol = implObjSymbol.info.member(macroMirror.newTermName(macroImpl.name.toString))
+ if (macroDebug) {
+ println("implMethSymbol is: " + implMethSymbol.fullNameString)
+ println("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol))
+ }
+
+ if (implMethSymbol == macroMirror.NoSymbol) None
+ else {
+ if (macroDebug) println("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol))
+ Some((implObj, implMethSymbol))
+ }
+ }
}
+ } catch {
+ case ex: ClassNotFoundException =>
+ macroTrace("implementation class failed to load: ")(ex.toString)
+ None
}
}
- } catch {
- case ex: ClassNotFoundException =>
- macroTrace("implementation class failed to load: ")(ex.toString)
- None
- }
- }
- val primary = loadMacroImpl(primaryMirror)
- primary match {
- case Some((implObj, implMethSymbol)) =>
- def runtime(args: List[Any]) = primaryMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any]
- Some(runtime)
- case None =>
- if (settings.XmacroFallbackClasspath.value != "") {
- if (macroDebug) println("trying to load macro implementation from the fallback mirror: %s".format(settings.XmacroFallbackClasspath.value))
- val fallback = loadMacroImpl(fallbackMirror)
- fallback match {
- case Some((implObj, implMethSymbol)) =>
- def runtime(args: List[Any]) = fallbackMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any]
- Some(runtime)
- case None =>
+ val primary = loadMacroImpl(primaryMirror)
+ primary match {
+ case Some((implObj, implMethSymbol)) =>
+ def runtime(args: List[Any]) = primaryMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any]
+ Some(runtime _)
+ case None =>
+ if (settings.XmacroFallbackClasspath.value != "") {
+ if (macroDebug) println("trying to load macro implementation from the fallback mirror: %s".format(settings.XmacroFallbackClasspath.value))
+ val fallback = loadMacroImpl(fallbackMirror)
+ fallback match {
+ case Some((implObj, implMethSymbol)) =>
+ def runtime(args: List[Any]) = fallbackMirror.invoke(implObj, implMethSymbol)(args: _*).asInstanceOf[Any]
+ Some(runtime _)
+ case None =>
+ None
+ }
+ } else {
None
- }
- } else {
- None
+ }
}
- }
- }
+ }
+
+ if (runtime == None) macroDef setFlag IS_ERROR
+ runtime
+ })
/** Should become private again once we're done with migrating typetag generation from implicits */
def macroContext(typer: Typer, prefixTree: Tree, expandeeTree: Tree): MacroContext { val mirror: global.type } =
@@ -784,7 +794,7 @@ trait Macros { self: Analyzer =>
val prefix = Expr(prefixTree)(TypeTag.Nothing)
val expandee = expandeeTree
} with MacroContext {
- override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, openMacros.length - 1 /* exclude myself */)
+ override def toString = "MacroContext(%s@%s +%d)".format(expandee.symbol.name, expandee.pos, enclosingMacros.length - 1 /* exclude myself */)
}
/** Calculate the arguments to pass to a macro implementation when expanding the provided tree.
@@ -795,6 +805,10 @@ trait Macros { self: Analyzer =>
* @return list of runtime objects to pass to the implementation obtained by ``macroRuntime''
*/
private def macroArgs(typer: Typer, expandee: Tree): Option[List[Any]] = {
+ val macroDef = expandee.symbol
+ val runtime = macroRuntime(macroDef)
+ if (runtime == None) return None
+
var prefixTree: Tree = EmptyTree
var typeArgs = List[Tree]()
val exprArgs = new ListBuffer[List[Expr[_]]]
@@ -811,11 +825,10 @@ trait Macros { self: Analyzer =>
case _ =>
}
collectMacroArgs(expandee)
- val context = macroContext(typer, prefixTree, expandee)
+ val context = expandee.attachmentOpt[MacroAttachment].flatMap(_.context).getOrElse(macroContext(typer, prefixTree, expandee))
var argss: List[List[Any]] = List(context) :: exprArgs.toList
macroTrace("argss: ")(argss)
- val macroDef = expandee.symbol
val ann = macroDef.getAnnotation(MacroImplAnnotation).getOrElse(throw new Error("assertion failed. %s: %s".format(macroDef, macroDef.annotations)))
val macroImpl = ann.args(0).symbol
var paramss = macroImpl.paramss
@@ -923,62 +936,92 @@ trait Macros { self: Analyzer =>
* the expandee with an error marker set if the expansion has been cancelled due malformed arguments or implementation
* the expandee with an error marker set if there has been an error
*/
- def macroExpand(typer: Typer, expandee: Tree, pt: Type): Tree =
- macroExpand1(typer, expandee) match {
- case Success(expanded) =>
- try {
- var expectedTpe = expandee.tpe
-
- // [Eugene] weird situation. what's the conventional way to deal with it?
- val isNullaryInvocation = expandee match {
- case TypeApply(Select(_, _), _) => true
- case Select(_, _) => true
- case _ => false
- }
- if (isNullaryInvocation) expectedTpe match {
- case MethodType(Nil, restpe) =>
- macroTrace("nullary invocation of a method with an empty parameter list. unwrapping expectedTpe from " + expectedTpe + " to:")(restpe)
- expectedTpe = restpe
- case _ => ;
- }
+ def macroExpand(typer: Typer, expandee: Tree, mode: Int = EXPRmode, pt: Type = WildcardType): Tree = {
+ val start = startTimer(macroExpandNanos)
+ incCounter(macroExpandCount)
+ try {
+ macroExpand1(typer, expandee) match {
+ case Success(expanded) =>
+ try {
+ var expectedTpe = expandee.tpe
+
+ // [Eugene] weird situation. what's the conventional way to deal with it?
+ val isNullaryInvocation = expandee match {
+ case TypeApply(Select(_, _), _) => true
+ case TypeApply(Ident(_), _) => true
+ case Select(_, _) => true
+ case Ident(_) => true
+ case _ => false
+ }
+ if (isNullaryInvocation) expectedTpe match {
+ case NullaryMethodType(restpe) =>
+ macroTrace("nullary invocation of a nullary method. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe)
+ expectedTpe = restpe
+ case MethodType(Nil, restpe) =>
+ macroTrace("nullary invocation of a method with an empty parameter list. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe)
+ expectedTpe = restpe
+ case _ => ;
+ }
- var typechecked = typer.context.withImplicitsEnabled(typer.typed(expanded, EXPRmode, expectedTpe))
- if (macroDebug) {
- println("typechecked1:")
- println(typechecked)
- println(showRaw(typechecked))
- }
+ def fail(what: String): Tree = {
+ val err = typer.context.errBuffer.head
+ this.fail(typer, expanded, "failed to perform %s: %s at %s".format(what, err.errMsg, err.errPos))
+ return expandee
+ }
- typechecked = typer.context.withImplicitsEnabled(typer.typed(typechecked, EXPRmode, pt))
- if (macroDebug) {
- println("typechecked2:")
- println(typechecked)
- println(showRaw(typechecked))
- }
+ if (macroDebug) println("typechecking1 against %s: %s".format(expectedTpe, expanded))
+ var typechecked = typer.context.withImplicitsEnabled(typer.typed(expanded, EXPRmode, expectedTpe))
+ if (typer.context.hasErrors) fail("typecheck1")
+ if (macroDebug) {
+ println("typechecked1:")
+ println(typechecked)
+ println(showRaw(typechecked))
+ }
- typechecked
- } finally {
- openMacros = openMacros.tail
- }
- case Fallback(fallback) =>
- typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt))
- case Other(result) =>
- result
+ if (macroDebug) println("typechecking2 against %s: %s".format(pt, expanded))
+ typechecked = typer.context.withImplicitsEnabled(typer.typed(typechecked, EXPRmode, pt))
+ if (typer.context.hasErrors) fail("typecheck2")
+ if (macroDebug) {
+ println("typechecked2:")
+ println(typechecked)
+ println(showRaw(typechecked))
+ }
+
+ typechecked
+ } finally {
+ openMacros = openMacros.tail
+ }
+ case Delay(expandee) =>
+ // need to save the context to preserve enclosures
+ val args = macroArgs(typer, expandee)
+ assert(args.isDefined, expandee)
+ val context = args.get.head.asInstanceOf[MacroContext]
+ var result = expandee withAttachment MacroAttachment(delayed = true, context = Some(context))
+ // adapting here would be premature, we must wait until undetparams are inferred
+// result = typer.adapt(result, mode, pt)
+ result
+ case Fallback(fallback) =>
+ typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt))
+ case Other(result) =>
+ result
+ }
+ } finally {
+ stopTimer(macroExpandNanos, start)
}
+ }
private sealed abstract class MacroExpansionResult extends Product with Serializable
private case class Success(expanded: Tree) extends MacroExpansionResult
private case class Fallback(fallback: Tree) extends MacroExpansionResult
+ private case class Delay(expandee: Tree) extends MacroExpansionResult
private case class Other(result: Tree) extends MacroExpansionResult
- private def Delay(expandee: Tree) = Other(expandee)
private def Skip(expanded: Tree) = Other(expanded)
private def Cancel(expandee: Tree) = Other(expandee)
private def Failure(expandee: Tree) = Other(expandee)
private def fail(typer: Typer, expandee: Tree, msg: String = null) = {
if (macroDebug || macroCopypaste) {
var msg1 = if (msg contains "exception during macro expansion") msg.split(EOL).drop(1).headOption.getOrElse("?") else msg
- if (macroDebug) msg1 = msg
- println("macro expansion has failed: %s".format(msg1))
+ if (macroDebug) println("macro expansion has failed: %s".format(msg1))
}
val pos = if (expandee.pos != NoPosition) expandee.pos else openMacros.find(c => c.expandee.pos != NoPosition).map(_.expandee.pos).getOrElse(NoPosition)
if (msg != null) typer.context.error(pos, msg)
@@ -989,191 +1032,200 @@ trait Macros { self: Analyzer =>
/** Does the same as ``macroExpand'', but without typechecking the expansion
* Meant for internal use within the macro infrastructure, don't use it elsewhere.
*/
- private def macroExpand1(typer: Typer, expandee: Tree): MacroExpansionResult = {
- // if a macro implementation is incompatible or any of the arguments are erroneous
- // there is no sense to expand the macro itself => it will only make matters worse
- if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) {
- val reason = if (expandee.symbol.isErroneous) "incompatible macro implementation" else "erroneous arguments"
- macroTrace("cancelled macro expansion because of %s: ".format(reason))(expandee)
- return Cancel(typer.infer.setError(expandee))
- }
+ private def macroExpand1(typer: Typer, expandee: Tree): MacroExpansionResult =
+ // InfoLevel.Verbose examines and prints out infos of symbols
+ // by the means of this'es these symbols can climb up the lexical scope
+ // when these symbols will be examined by a node printer
+ // they will enumerate and analyze their children (ask for infos and tpes)
+ // if one of those children involves macro expansion, things might get nasty
+ // that's why I'm temporarily turning this behavior off
+ withInfoLevel(nodePrinters.InfoLevel.Quiet) {
+ // if a macro implementation is incompatible or any of the arguments are erroneous
+ // there is no sense to expand the macro itself => it will only make matters worse
+ if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) {
+ val reason = if (expandee.symbol.isErroneous) "incompatible macro implementation" else "erroneous arguments"
+ macroTrace("cancelled macro expansion because of %s: ".format(reason))(expandee)
+ return Cancel(typer.infer.setError(expandee))
+ }
- if (!isDelayed(expandee)) {
- if (macroDebug || macroCopypaste) println("typechecking macro expansion %s at %s".format(expandee, expandee.pos))
+ macroRuntime(expandee.symbol) match {
+ case Some(runtime) =>
+ macroExpandWithRuntime(typer, expandee, runtime)
+ case None =>
+ macroExpandWithoutRuntime(typer, expandee)
+ }
+ }
+ /** Expands a macro when a runtime (i.e. the macro implementation) can be successfully loaded
+ * Meant for internal use within the macro infrastructure, don't use it elsewhere.
+ */
+ private def macroExpandWithRuntime(typer: Typer, expandee: Tree, runtime: MacroRuntime): MacroExpansionResult =
+ try {
+ val wasDelayed = isDelayed(expandee)
val undetparams = calculateUndetparams(expandee)
- if (undetparams.size != 0) {
- macroTrace("macro expansion is delayed: ")(expandee)
- delayed += expandee -> (typer.context, undetparams)
- Delay(expandee)
- } else {
- val start = startTimer(macroExpandNanos)
- incCounter(macroExpandCount)
- try {
- val macroDef = expandee.symbol
- macroRuntime(macroDef) match {
- case Some(runtime) =>
- val savedInfolevel = nodePrinters.infolevel
- try {
- // InfoLevel.Verbose examines and prints out infos of symbols
- // by the means of this'es these symbols can climb up the lexical scope
- // when these symbols will be examined by a node printer
- // they will enumerate and analyze their children (ask for infos and tpes)
- // if one of those children involves macro expansion, things might get nasty
- // that's why I'm temporarily turning this behavior off
- nodePrinters.infolevel = nodePrinters.InfoLevel.Quiet
- val args = macroArgs(typer, expandee)
- args match {
- case Some(args) =>
- // adding stuff to openMacros is easy, but removing it is a nightmare
- // it needs to be sprinkled over several different code locations
- val (context: MacroContext) :: _ = args
- openMacros = context :: openMacros
- val expanded: MacroExpansionResult = try {
- val prevNumErrors = reporter.ERROR.count
- val expanded = runtime(args)
+ val nowDelayed = !typer.context.macrosEnabled || undetparams.size != 0
+
+ if (!wasDelayed) {
+ if (macroDebug || macroCopypaste) println("typechecking macro expansion %s at %s".format(expandee, expandee.pos))
+ if (nowDelayed) {
+ if (macroDebug || macroCopypaste) println("macro expansion is delayed: %s".format(expandee))
+ delayed += expandee -> (typer.context, undetparams)
+ Delay(expandee)
+ } else {
+ val args = macroArgs(typer, expandee)
+ args match {
+ case Some(args) =>
+ // adding stuff to openMacros is easy, but removing it is a nightmare
+ // it needs to be sprinkled over several different code locations
+ val (context: MacroContext) :: _ = args
+ openMacros = context :: openMacros
+ val expanded: MacroExpansionResult = try {
+ val prevNumErrors = reporter.ERROR.count
+ expandee.detach(null)
+ val expanded = runtime(args)
+ val currNumErrors = reporter.ERROR.count
+ if (currNumErrors != prevNumErrors) {
+ fail(typer, expandee) // errors have been reported by the macro itself
+ } else {
+ expanded match {
+ case expanded: Expr[_] =>
+ if (macroDebug || macroCopypaste) {
+ if (macroDebug) println("original:")
+ println(expanded.tree)
+ println(showRaw(expanded.tree))
+ }
+
+ freeTerms(expanded.tree) foreach (fte => typer.context.error(expandee.pos,
+ ("macro expansion contains free term variable %s %s. "+
+ "have you forgot to use eval when splicing this variable into a reifee? " +
+ "if you have troubles tracking free term variables, consider using -Xlog-free-terms").format(fte.name, fte.origin)))
+ freeTypes(expanded.tree) foreach (fty => typer.context.error(expandee.pos,
+ ("macro expansion contains free type variable %s %s. "+
+ "have you forgot to use c.TypeTag annotation for this type parameter? " +
+ "if you have troubles tracking free type variables, consider using -Xlog-free-types").format(fty.name, fty.origin)))
+
val currNumErrors = reporter.ERROR.count
if (currNumErrors != prevNumErrors) {
- fail(typer, expandee) // errors have been reported by the macro itself
+ fail(typer, expandee)
} else {
- expanded match {
- case expanded: Expr[_] =>
- if (macroDebug || macroCopypaste) {
- if (macroDebug) println("original:")
- println(expanded.tree)
- println(showRaw(expanded.tree))
- }
-
- freeTerms(expanded.tree) foreach (fte => typer.context.error(expandee.pos,
- ("macro expansion contains free term variable %s %s. "+
- "have you forgot to use eval when splicing this variable into a reifee? " +
- "if you have troubles tracking free term variables, consider using -Xlog-free-terms").format(fte.name, fte.origin)))
- freeTypes(expanded.tree) foreach (fty => typer.context.error(expandee.pos,
- ("macro expansion contains free type variable %s %s. "+
- "have you forgot to use c.TypeTag annotation for this type parameter? " +
- "if you have troubles tracking free type variables, consider using -Xlog-free-types").format(fty.name, fty.origin)))
-
- val currNumErrors = reporter.ERROR.count
- if (currNumErrors != prevNumErrors) {
- fail(typer, expandee)
- } else {
- // inherit the position from the first position-ful expandee in macro callstack
- // this is essential for sane error messages
- var tree = expanded.tree
- var position = openMacros.find(c => c.expandee.pos != NoPosition).map(_.expandee.pos).getOrElse(NoPosition)
- tree = atPos(position.focus)(tree)
-
- // now macro expansion gets typechecked against the macro definition return type
- // however, this happens in macroExpand, not here in macroExpand1
- Success(tree)
- }
- case expanded if expanded.isInstanceOf[Expr[_]] =>
- val msg = "macro must return a compiler-specific expr; returned value is Expr, but it doesn't belong to this compiler's universe"
- fail(typer, expandee, msg)
- case expanded =>
- val msg = "macro must return a compiler-specific expr; returned value is of class: %s".format(expanded.getClass)
- fail(typer, expandee, msg)
- }
+ // inherit the position from the first position-ful expandee in macro callstack
+ // this is essential for sane error messages
+ var tree = expanded.tree
+ var position = openMacros.find(c => c.expandee.pos != NoPosition).map(_.expandee.pos).getOrElse(NoPosition)
+ tree = atPos(position.focus)(tree)
+
+ // now macro expansion gets typechecked against the macro definition return type
+ // however, this happens in macroExpand, not here in macroExpand1
+ Success(tree)
}
- } catch {
- case ex: Throwable =>
- openMacros = openMacros.tail
- throw ex
- }
- if (!expanded.isInstanceOf[Success]) openMacros = openMacros.tail
- expanded
- case None =>
- fail(typer, expandee) // error has been reported by macroArgs
+ case expanded if expanded.isInstanceOf[Expr[_]] =>
+ val msg = "macro must return a compiler-specific expr; returned value is Expr, but it doesn't belong to this compiler's universe"
+ fail(typer, expandee, msg)
+ case expanded =>
+ val msg = "macro must return a compiler-specific expr; returned value is of class: %s".format(expanded.getClass)
+ fail(typer, expandee, msg)
+ }
}
} catch {
- case ex =>
- // [Eugene] any ideas about how to improve this one?
- val realex = ReflectionUtils.unwrapThrowable(ex)
- realex match {
- case realex: reflect.makro.runtime.AbortMacroException =>
- if (macroDebug || macroCopypaste) println("macro expansion has failed: %s".format(realex.msg))
- fail(typer, expandee) // error has been reported by abort
- case _ =>
- val message = {
- try {
- // the most reliable way of obtaining currently executing method
- // http://stackoverflow.com/questions/442747/getting-the-name-of-the-current-executing-method
- val currentMethodName = new Object(){}.getClass().getEnclosingMethod().getName
- val relevancyThreshold = realex.getStackTrace().indexWhere(este => este.getMethodName == currentMethodName)
- if (relevancyThreshold == -1) None
- else {
- var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1)
- var framesTillReflectiveInvocationOfMacroImpl = relevantElements.reverse.indexWhere(_.isNativeMethod) + 1
- relevantElements = relevantElements dropRight framesTillReflectiveInvocationOfMacroImpl
-
- realex.setStackTrace(relevantElements)
- val message = new java.io.StringWriter()
- realex.printStackTrace(new java.io.PrintWriter(message))
- Some(EOL + message)
- }
- } catch {
- // if the magic above goes boom, just fall back to uninformative, but better than nothing, getMessage
- case ex: Throwable =>
- None
- }
- } getOrElse realex.getMessage
- fail(typer, expandee, "exception during macro expansion: " + message)
- }
- } finally {
- nodePrinters.infolevel = savedInfolevel
+ case ex: Throwable =>
+ openMacros = openMacros.tail
+ throw ex
}
+ if (!expanded.isInstanceOf[Success]) openMacros = openMacros.tail
+ expanded
case None =>
- def notFound() = {
- typer.context.error(expandee.pos, "macro implementation not found: " + macroDef.name + " " +
- "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)\n" +
- "if you do need to define macro implementations along with the rest of your program, consider two-phase compilation with -Xmacro-fallback-classpath " +
- "in the second phase pointing to the output of the first phase")
- None
- }
- def fallBackToOverridden(tree: Tree): Option[Tree] = {
- tree match {
- case Select(qual, name) if (macroDef.isTermMacro) =>
- macroDef.allOverriddenSymbols match {
- case first :: _ =>
- Some(Select(qual, name) setPos tree.pos setSymbol first)
- case _ =>
- macroTrace("macro is not overridden: ")(tree)
- notFound()
- }
- case Apply(fn, args) =>
- fallBackToOverridden(fn) match {
- case Some(fn1) => Some(Apply(fn1, args) setPos tree.pos)
- case _ => None
- }
- case TypeApply(fn, args) =>
- fallBackToOverridden(fn) match {
- case Some(fn1) => Some(TypeApply(fn1, args) setPos tree.pos)
- case _ => None
- }
- case _ =>
- macroTrace("unexpected tree in fallback: ")(tree)
- notFound()
- }
- }
- fallBackToOverridden(expandee) match {
- case Some(tree1) =>
- macroTrace("falling back to ")(tree1)
- currentRun.macroExpansionFailed = true
- Fallback(tree1)
- case None =>
- fail(typer, expandee)
- }
+ fail(typer, expandee) // error has been reported by macroArgs
}
- } finally {
- stopTimer(macroExpandNanos, start)
}
+ } else {
+ if (nowDelayed)
+ Delay(expandee)
+ else
+ Skip(macroExpandAll(typer, expandee))
}
- } else {
- val undetparams = calculateUndetparams(expandee)
- if (undetparams.size != 0)
- Delay(expandee)
- else
- Skip(macroExpandAll(typer, expandee))
+ } catch {
+ case ex => handleMacroExpansionException(typer, expandee, ex)
+ } finally {
+ expandee.detach(classOf[MacroAttachment])
+ }
+
+ private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroExpansionResult = {
+ val macroDef = expandee.symbol
+ def notFound() = {
+ typer.context.error(expandee.pos, "macro implementation not found: " + macroDef.name + " " +
+ "(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)\n" +
+ "if you do need to define macro implementations along with the rest of your program, consider two-phase compilation with -Xmacro-fallback-classpath " +
+ "in the second phase pointing to the output of the first phase")
+ None
+ }
+ def fallBackToOverridden(tree: Tree): Option[Tree] = {
+ tree match {
+ case Select(qual, name) if (macroDef.isTermMacro) =>
+ macroDef.allOverriddenSymbols match {
+ case first :: _ =>
+ Some(Select(qual, name) setPos tree.pos setSymbol first)
+ case _ =>
+ macroTrace("macro is not overridden: ")(tree)
+ notFound()
+ }
+ case Apply(fn, args) =>
+ fallBackToOverridden(fn) match {
+ case Some(fn1) => Some(Apply(fn1, args) setPos tree.pos)
+ case _ => None
+ }
+ case TypeApply(fn, args) =>
+ fallBackToOverridden(fn) match {
+ case Some(fn1) => Some(TypeApply(fn1, args) setPos tree.pos)
+ case _ => None
+ }
+ case _ =>
+ macroTrace("unexpected tree in fallback: ")(tree)
+ notFound()
+ }
+ }
+ fallBackToOverridden(expandee) match {
+ case Some(tree1) =>
+ macroTrace("falling back to: ")(tree1)
+ currentRun.macroExpansionFailed = true
+ Fallback(tree1)
+ case None =>
+ fail(typer, expandee)
+ }
+ }
+
+ private def handleMacroExpansionException(typer: Typer, expandee: Tree, ex: Throwable): MacroExpansionResult = {
+ // [Eugene] any ideas about how to improve this one?
+ val realex = ReflectionUtils.unwrapThrowable(ex)
+ realex match {
+ case realex: reflect.makro.runtime.AbortMacroException =>
+ if (macroDebug || macroCopypaste) println("macro expansion has failed: %s".format(realex.msg))
+ fail(typer, expandee) // error has been reported by abort
+ case err: TypeError =>
+ if (macroDebug || macroCopypaste) println("macro expansion has failed: %s at %s".format(err.msg, err.pos))
+ throw err // error should be propagated, don't report
+ case _ =>
+ val message = {
+ try {
+ // [Eugene] is there a better way?
+ val relevancyThreshold = realex.getStackTrace().indexWhere(este => este.getMethodName == "macroExpand1")
+ if (relevancyThreshold == -1) None
+ else {
+ var relevantElements = realex.getStackTrace().take(relevancyThreshold + 1)
+ var framesTillReflectiveInvocationOfMacroImpl = relevantElements.reverse.indexWhere(_.isNativeMethod) + 1
+ relevantElements = relevantElements dropRight framesTillReflectiveInvocationOfMacroImpl
+
+ realex.setStackTrace(relevantElements)
+ val message = new java.io.StringWriter()
+ realex.printStackTrace(new java.io.PrintWriter(message))
+ Some(EOL + message)
+ }
+ } catch {
+ // if the magic above goes boom, just fall back to uninformative, but better than nothing, getMessage
+ case ex: Throwable =>
+ None
+ }
+ } getOrElse realex.getMessage
+ fail(typer, expandee, "exception during macro expansion: " + message)
}
}
@@ -1194,18 +1246,23 @@ trait Macros { self: Analyzer =>
private def isDelayed(expandee: Tree) = delayed contains expandee
private def calculateUndetparams(expandee: Tree): collection.mutable.Set[Int] =
delayed.get(expandee).map(_._2).getOrElse {
- val calculated = collection.mutable.Set[Int]()
+ val calculated = collection.mutable.Set[Symbol]()
expandee foreach (sub => {
- def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym.id
+ def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym
if (sub.symbol != null) traverse(sub.symbol)
if (sub.tpe != null) sub.tpe foreach (sub => traverse(sub.typeSymbol))
})
- calculated
+ if (macroDebug) println("calculateUndetparams: %s".format(calculated))
+ calculated map (_.id)
}
private val undetparams = perRunCaches.newSet[Int]
- def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = undetparams ++= newUndets map (_.id)
+ def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = {
+ undetparams ++= newUndets map (_.id)
+ if (macroDebug) newUndets foreach (sym => println("undetParam added: %s".format(sym)))
+ }
def notifyUndetparamsInferred(undetNoMore: List[Symbol], inferreds: List[Type]): Unit = {
undetparams --= undetNoMore map (_.id)
+ if (macroDebug) (undetNoMore zip inferreds) foreach {case (sym, tpe) => println("undetParam inferred: %s as %s".format(sym, tpe))}
if (!delayed.isEmpty)
delayed.toList foreach {
case (expandee, (_, undetparams)) if !undetparams.isEmpty =>
@@ -1230,7 +1287,10 @@ trait Macros { self: Analyzer =>
case wannabe if (delayed contains wannabe) && calculateUndetparams(wannabe).isEmpty =>
val (context, _) = delayed(wannabe)
delayed -= wannabe
- macroExpand(newTyper(context), wannabe, WildcardType)
+ context.implicitsEnabled = typer.context.implicitsEnabled
+ context.enrichmentEnabled = typer.context.enrichmentEnabled
+ context.macrosEnabled = typer.context.macrosEnabled
+ macroExpand(newTyper(context), wannabe, EXPRmode, WildcardType)
case _ =>
tree
})
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 8f5b3fb519..9a59d8f28a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1082,10 +1082,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
if (tree.isType)
adaptType()
- else if (context.macrosEnabled && // when macros are enabled
- inExprModeButNot(mode, FUNmode) && !tree.isDef && // and typechecking application
- tree.symbol != null && tree.symbol.isTermMacro) // of a term macro
- macroExpand(this, tree, pt)
+ else if (
+ inExprModeButNot(mode, FUNmode) && !tree.isDef && // typechecking application
+ tree.symbol != null && tree.symbol.isTermMacro) // of a macro
+ macroExpand(this, tree, mode, pt)
else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode))
adaptConstrPattern()
else if (inAllModes(mode, EXPRmode | FUNmode) &&
diff --git a/src/library/scala/reflect/api/Attachment.scala b/src/library/scala/reflect/api/Attachment.scala
index 9fa5ceb0fb..50f55b4aa5 100644
--- a/src/library/scala/reflect/api/Attachment.scala
+++ b/src/library/scala/reflect/api/Attachment.scala
@@ -7,8 +7,6 @@ package api
* Attachments have to carry positions, because we don't want to introduce even a single additional field in Tree
* imposing an unnecessary memory tax because of something that will not be used in most cases.
*/
-// [Eugene] with the introduction of `attach` and `payload[T]` users don't need to create custom attachments anymore
-// however, we cannot move attachments to scala.reflect.internal, because they are used in Trees, which are implemented completely in scala.reflect.api
trait Attachment {
/** Gets the underlying position */
def pos: Position
@@ -22,3 +20,10 @@ trait Attachment {
/** Creates a copy of this attachment with its payload updated */
def withPayload(newPayload: Any): Attachment
}
+
+// [Eugene] with the introduction of `attach` and `attachment[T]` users don't need to create custom attachments anymore
+// however, we cannot move attachments to scala.reflect.internal, because they are used in Trees, which are implemented completely in scala.reflect.api
+private[scala] case class NontrivialAttachment(pos: api.Position, payload: collection.mutable.ListBuffer[Any]) extends Attachment {
+ def withPos(newPos: api.Position) = copy(pos = newPos, payload = payload)
+ def withPayload(newPayload: Any) = copy(pos = pos, payload = newPayload.asInstanceOf[collection.mutable.ListBuffer[Any]])
+} \ No newline at end of file
diff --git a/src/library/scala/reflect/api/Trees.scala b/src/library/scala/reflect/api/Trees.scala
index 7548a6bdc0..6e2e8261e7 100644
--- a/src/library/scala/reflect/api/Trees.scala
+++ b/src/library/scala/reflect/api/Trees.scala
@@ -85,18 +85,39 @@ trait Trees { self: Universe =>
def pos_=(pos: Position): Unit = rawatt = (rawatt withPos pos) // the "withPos" part is crucial to robustness
def setPos(newpos: Position): this.type = { pos = newpos; this }
+ // [Eugene] can we make this more type-safe
private var rawatt: Attachment = NoPosition
- private case class NontrivialAttachment(pos: api.Position, payload: Any) extends Attachment {
- def withPos(newPos: api.Position) = copy(pos = newPos, payload = payload)
- def withPayload(newPayload: Any) = copy(pos = pos, payload = newPayload)
- }
- // todo. annotate T with ClassTag and make pattern matcher use it
- // todo. support multiple attachments, and remove the assignment. only leave attach/detach
-// def attachment[T]: T = rawatt.payload.asInstanceOf[T]
-// def attachmentOpt[T]: Option[T] = try { Some(rawatt.payload.asInstanceOf[T]) } catch { case _: Throwable => None }
- def attachment: Any = rawatt.payload
- def attachment_=(att: Any): Unit = rawatt = NontrivialAttachment(pos, att)
- def setAttachment(att: Any): this.type = { attachment = att; this }
+ def attach(att: Any): Unit =
+ rawatt match {
+ case NontrivialAttachment(pos, payload) =>
+ val index = payload.indexWhere(p => p.getClass == att.getClass)
+ if (index == -1) payload += att
+ else payload(index) = att
+ case _ =>
+ rawatt = NontrivialAttachment(pos, collection.mutable.ListBuffer[Any](att))
+ }
+ def withAttachment(att: Any): this.type = { attach(att); this }
+ def detach(att: Any): Unit =
+ detach(att.getClass)
+ def detach(clazz: java.lang.Class[_]): Unit =
+ rawatt match {
+ case NontrivialAttachment(pos, payload) =>
+ val index = payload.indexWhere(p => p.getClass == clazz)
+ if (index != -1) payload.remove(index)
+ case _ =>
+ // do nothing
+ }
+ def withoutAttachment(att: Any): this.type = { detach(att); this }
+ def attachment[T: ClassTag]: T = attachmentOpt[T] getOrElse { throw new Error("no attachment of type %s".format(classTag[T].erasure)) }
+ def attachmentOpt[T: ClassTag]: Option[T] =
+ rawatt match {
+ case NontrivialAttachment(pos, payload) =>
+ val index = payload.indexWhere(p => p.getClass == classTag[T].erasure)
+ if (index != -1) Some(payload(index).asInstanceOf[T])
+ else None
+ case _ =>
+ None
+ }
private[this] var rawtpe: Type = _
diff --git a/test/pending/run/macro-expand-implicit-macro-defeats-type-inference.check b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference.check
new file mode 100644
index 0000000000..08f7cb9e3e
--- /dev/null
+++ b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference.check
@@ -0,0 +1,6 @@
+openImplicits are: List()
+enclosingImplicits are: List((List[Int],scala.this.Predef.implicitly[List[Int]]))
+typetag is: ConcreteTypeTag[Nothing]
+openImplicits are: List()
+enclosingImplicits are: List((List[String],Test.this.bar[String]))
+typetag is: ConcreteTypeTag[Nothing]
diff --git a/test/pending/run/macro-expand-implicit-macro-defeats-type-inference.flags b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference.flags
new file mode 100644
index 0000000000..cd66464f2f
--- /dev/null
+++ b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference.flags
@@ -0,0 +1 @@
+-language:experimental.macros \ No newline at end of file
diff --git a/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Impls_1.scala b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Impls_1.scala
new file mode 100644
index 0000000000..c096a83c5e
--- /dev/null
+++ b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Impls_1.scala
@@ -0,0 +1,10 @@
+import scala.reflect.makro.Context
+
+object Impls {
+ def foo[T: c.TypeTag](c: Context): c.Expr[List[T]] = c.reify {
+ println("openImplicits are: " + c.literal(c.openImplicits.toString).eval)
+ println("enclosingImplicits are: " + c.literal(c.enclosingImplicits.toString).eval)
+ println("typetag is: " + c.literal(c.tag[T].toString).eval)
+ null
+ }
+} \ No newline at end of file
diff --git a/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Macros_Test_2.scala b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Macros_Test_2.scala
new file mode 100644
index 0000000000..27d0662799
--- /dev/null
+++ b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Macros_Test_2.scala
@@ -0,0 +1,6 @@
+object Test extends App {
+ implicit def foo[T]: List[T] = macro Impls.foo[T]
+ def bar[T](implicit foo: List[T]) {}
+ implicitly[List[Int]]
+ bar[String]
+} \ No newline at end of file