diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-04-26 10:03:56 +0300 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-04-26 10:03:56 +0300 |
commit | 99388a81d42cde0cb0e8ecd57f290f6df25b9bdd (patch) | |
tree | 37eb4cab4c3ae68a024f3e810ce18d8140aa7128 | |
parent | 8fc543b5dd7e6a8fa1827cc9e9d65e721cae140e (diff) | |
download | scala-99388a81d42cde0cb0e8ecd57f290f6df25b9bdd.tar.gz scala-99388a81d42cde0cb0e8ecd57f290f6df25b9bdd.tar.bz2 scala-99388a81d42cde0cb0e8ecd57f290f6df25b9bdd.zip |
cleaning up macros, one step at a time
11 files changed, 135 insertions, 137 deletions
diff --git a/src/compiler/scala/reflect/makro/runtime/Context.scala b/src/compiler/scala/reflect/makro/runtime/Context.scala index 15a4118b85..ca02822788 100644 --- a/src/compiler/scala/reflect/makro/runtime/Context.scala +++ b/src/compiler/scala/reflect/makro/runtime/Context.scala @@ -14,7 +14,8 @@ abstract class Context extends scala.reflect.makro.Context with Settings with Symbols with Typers - with Util { + with Util + with Traces { val mirror: Global diff --git a/src/compiler/scala/reflect/makro/runtime/Traces.scala b/src/compiler/scala/reflect/makro/runtime/Traces.scala new file mode 100644 index 0000000000..6b61842316 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Traces.scala @@ -0,0 +1,8 @@ +package scala.reflect.makro +package runtime + +trait Traces extends util.Traces { + self: Context => + + def globalSettings = mirror.settings +} diff --git a/src/compiler/scala/reflect/makro/runtime/Typers.scala b/src/compiler/scala/reflect/makro/runtime/Typers.scala index 98dbd65b72..c61e492250 100644 --- a/src/compiler/scala/reflect/makro/runtime/Typers.scala +++ b/src/compiler/scala/reflect/makro/runtime/Typers.scala @@ -9,8 +9,7 @@ trait Typers { 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) - trace("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) + macroLogVerbose("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) val wrapper1 = if (!withImplicitViewsDisabled) (callsiteTyper.context.withImplicitsEnabled[Tree] _) else (callsiteTyper.context.withImplicitsDisabled[Tree] _) val wrapper2 = if (!withMacrosDisabled) (callsiteTyper.context.withMacrosEnabled[Tree] _) else (callsiteTyper.context.withMacrosDisabled[Tree] _) def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) @@ -21,25 +20,24 @@ trait Typers { // (also see reflect.runtime.ToolBoxes.typeCheckExpr for a workaround that might work for you) wrapper(callsiteTyper.silent(_.typed(tree, mirror.analyzer.EXPRmode, pt)) match { case mirror.analyzer.SilentResultValue(result) => - trace(result) + macroLogVerbose(result) result case error @ mirror.analyzer.SilentTypeError(_) => - trace(error.err.errMsg) + macroLogVerbose(error.err.errMsg) if (!silent) throw new mirror.TypeError(error.err.errPos, error.err.errMsg) mirror.EmptyTree }) } def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { - def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg) - trace("inferring implicit value of type %s, macros = %s".format(pt, !withMacrosDisabled)) + macroLogVerbose("inferring implicit value of type %s, macros = %s".format(pt, !withMacrosDisabled)) import mirror.analyzer.SearchResult 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 { case failure if failure.tree.isEmpty => - trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits") + macroLogVerbose("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) mirror.EmptyTree case success => @@ -48,8 +46,7 @@ trait Typers { } def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, reportAmbiguous: Boolean = true, pos: Position = enclosingPosition): Tree = { - 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)) + macroLogVerbose("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 val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _) @@ -58,7 +55,7 @@ trait Typers { val viewTpe = mirror.TypeRef(fun1.typeConstructor.prefix, fun1, List(from, to)) 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") + macroLogVerbose("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) mirror.EmptyTree case success => diff --git a/src/compiler/scala/reflect/makro/util/Traces.scala b/src/compiler/scala/reflect/makro/util/Traces.scala new file mode 100644 index 0000000000..2363cc4bac --- /dev/null +++ b/src/compiler/scala/reflect/makro/util/Traces.scala @@ -0,0 +1,18 @@ +package scala.reflect.makro +package util + +trait Traces { + def globalSettings: tools.nsc.Settings + + // [Eugene] lots of ways to log: + // 1) trace(...) + // 2) log(...) + // 3) if (foo) { doStuff(); includingSomeLogs(); } + // what is the conventional way of unifying this? + val macroDebugLite = globalSettings.YmacrodebugLite.value + val macroDebugVerbose = globalSettings.YmacrodebugVerbose.value + val macroTraceLite = scala.tools.nsc.util.trace when (macroDebugLite || macroDebugVerbose) + val macroTraceVerbose = scala.tools.nsc.util.trace when macroDebugVerbose + @inline final def macroLogLite(msg: => Any) { if (macroDebugLite || macroDebugVerbose) println(msg) } + @inline final def macroLogVerbose(msg: => Any) { if (macroDebugVerbose) println(msg) } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 1a881455f2..4466f281b8 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -10,8 +10,7 @@ trait Errors { import mirror._ import definitions._ - lazy val defaultErrorPosition: Position = - mirror.analyzer.openMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication.pos).getOrElse(NoPosition) + def defaultErrorPosition = analyzer.enclosingMacroPosition // expected errors: these can happen if the user casually writes whatever.reify(...) // hence we don't crash here, but nicely report a typechecking error and bail out asap diff --git a/src/compiler/scala/reflect/reify/codegen/Types.scala b/src/compiler/scala/reflect/reify/codegen/Types.scala index 8fa24c5b00..1f336f4b0f 100644 --- a/src/compiler/scala/reflect/reify/codegen/Types.scala +++ b/src/compiler/scala/reflect/reify/codegen/Types.scala @@ -99,7 +99,7 @@ trait Types { // if this fails, it might produce the dreaded "erroneous or inaccessible type" error // to find out the whereabouts of the error run scalac with -Ydebug if (reifyDebug) println("launching implicit search for %s.%s[%s]".format(prefix, tagClass.name, tpe)) - typer.resolveTypeTag(positionBearer.pos, prefix.tpe, tpe, concrete) match { + typer.resolveTypeTag(defaultErrorPosition, prefix.tpe, tpe, concrete) match { case failure if failure.isEmpty => if (reifyDebug) println("implicit search was fruitless") EmptyTree diff --git a/src/compiler/scala/reflect/reify/codegen/Util.scala b/src/compiler/scala/reflect/reify/codegen/Util.scala index 8d7cf39ff7..bb369a1adb 100644 --- a/src/compiler/scala/reflect/reify/codegen/Util.scala +++ b/src/compiler/scala/reflect/reify/codegen/Util.scala @@ -14,8 +14,6 @@ trait Util { object reifiedNodePrinters extends { val global: mirror.type = mirror } with tools.nsc.ast.NodePrinters with NodePrinters val reifiedNodeToString = reifiedNodePrinters.reifiedNodeToString - val positionBearer = mirror.analyzer.openMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication).getOrElse(EmptyTree).asInstanceOf[Tree] - def reifyList(xs: List[Any]): Tree = mkList(xs map reify) diff --git a/src/compiler/scala/reflect/reify/package.scala b/src/compiler/scala/reflect/reify/package.scala index fa11c6313d..3f9d6200cd 100644 --- a/src/compiler/scala/reflect/reify/package.scala +++ b/src/compiler/scala/reflect/reify/package.scala @@ -31,8 +31,12 @@ package object reify { def reifyErasure(global: Global)(typer0: global.analyzer.Typer, tpe: global.Type, concrete: Boolean = true): global.Tree = { import global._ import definitions._ - val positionBearer = analyzer.openMacros.find(_.macroApplication.pos != NoPosition).map(_.macroApplication).getOrElse(EmptyTree).asInstanceOf[Tree] - val inScope = typer0.context.withMacrosDisabled(typer0.resolveErasureTag(positionBearer.pos, tpe, concrete = concrete), typer0.resolveArrayTag(positionBearer.pos, tpe)) + import analyzer.enclosingMacroPosition + + def erasureTagInScope = typer0.context.withMacrosDisabled(typer0.resolveErasureTag(enclosingMacroPosition, tpe, concrete = concrete)) + def arrayTagInScope = typer0.context.withMacrosDisabled(typer0.resolveArrayTag(enclosingMacroPosition, tpe)) + val inScope = (erasureTagInScope, arrayTagInScope) + inScope match { case (success, _) if !success.isEmpty => Select(success, nme.erasure) @@ -44,7 +48,7 @@ package object reify { val componentErasure = reifyErasure(global)(typer0, componentTpe, concrete) gen.mkMethodCall(arrayClassMethod, List(componentErasure)) } else { - if (tpe.isSpliceable && concrete) throw new ReificationError(positionBearer.pos, "tpe %s is an unresolved spliceable type".format(tpe)) + if (tpe.isSpliceable && concrete) throw new ReificationError(enclosingMacroPosition, "tpe %s is an unresolved spliceable type".format(tpe)) var erasure = tpe.erasure if (tpe.typeSymbol.isDerivedValueClass && global.phase.id < global.currentRun.erasurePhase.id) erasure = tpe gen.mkNullaryCall(Predef_classOf, List(erasure)) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6a5c4c92bc..91e31cae97 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -138,7 +138,7 @@ trait ScalaSettings extends AbsScalaSettings val termConflict = ChoiceSetting ("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val inline = BooleanSetting ("-Yinline", "Perform inlining when possible.") val inlineHandlers = BooleanSetting ("-Yinline-handlers", "Perform exception handler inlining when possible.") - val YinlinerWarnings= BooleanSetting ("-Yinline-warnings", "Emit inlining warnings. (Normally surpressed due to high volume)") + val YinlinerWarnings= BooleanSetting ("-Yinline-warnings", "Emit inlining warnings. (Normally surpressed due to high volume)") val Xlinearizer = ChoiceSetting ("-Ylinearizer", "which", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo") val log = PhasesSetting ("-Ylog", "Log operations during") val Ylogcp = BooleanSetting ("-Ylog-classpath", "Output information about what classpath is being applied.") @@ -172,7 +172,6 @@ trait ScalaSettings extends AbsScalaSettings val YrichExes = BooleanSetting ("-Yrich-exceptions", "Fancier exceptions. Set source search path with -D" + sys.SystemProperties.traceSourcePath.key) val Ybuilderdebug = ChoiceSetting ("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none") val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") - val Ymacrocopypaste = BooleanSetting ("-Ymacro-copypaste", "Dump macro expansions in copypasteable representation.") val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") val Ynotnull = BooleanSetting ("-Ynotnull", "Enable (experimental and incomplete) scala.NotNull.") val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.") @@ -193,7 +192,8 @@ trait ScalaSettings extends AbsScalaSettings val Yidedebug = BooleanSetting("-Yide-debug", "Generate, validate and output trees using the interactive compiler.") val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") val Yissuedebug = BooleanSetting("-Yissue-debug", "Print stack traces when a context issues an error.") - val Ymacrodebug = BooleanSetting("-Ymacro-debug", "Trace macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.") + val YmacrodebugLite = BooleanSetting("-Ymacro-debug-lite", "Trace essential macro-related activities.") + val YmacrodebugVerbose = BooleanSetting("-Ymacro-debug-verbose", "Trace all macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.") val Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.") val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.") val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.") diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 14f09d9dc9..1b8a43bf27 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -10,6 +10,7 @@ import scala.compat.Platform.EOL import scala.reflect.makro.runtime.{Context => MacroContext} import scala.reflect.runtime.Mirror import util.Statistics._ +import scala.reflect.makro.util._ /** * Code to deal with macros, namely with: @@ -36,13 +37,12 @@ import util.Statistics._ * (Expr(elems)) * (TypeTag(Int)) */ -trait Macros { self: Analyzer => +trait Macros extends Traces { + self: Analyzer => + import global._ import definitions._ - - val macroDebug = settings.Ymacrodebug.value - val macroCopypaste = settings.Ymacrocopypaste.value - val macroTrace = scala.tools.nsc.util.trace when macroDebug + def globalSettings = global.settings val globalMacroCache = collection.mutable.Map[Any, Any]() val perRunMacroCache = perRunCaches.newMap[Symbol, collection.mutable.Map[Any, Any]] @@ -136,11 +136,11 @@ trait Macros { self: Analyzer => } import SigGenerator._ - macroTrace("generating macroImplSigs for: ")(macroDef) - macroTrace("tparams are: ")(tparams) - macroTrace("vparamss are: ")(vparamss) - macroTrace("retTpe is: ")(retTpe) - macroTrace("macroImplSigs are: ")(paramsss, implRetTpe) + macroTraceVerbose("generating macroImplSigs for: ")(macroDef) + macroTraceVerbose("tparams are: ")(tparams) + macroTraceVerbose("vparamss are: ")(vparamss) + macroTraceVerbose("retTpe is: ")(retTpe) + macroTraceVerbose("macroImplSigs are: ")(paramsss, implRetTpe) } private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Option[Symbol]): List[List[Symbol]] = { @@ -183,7 +183,7 @@ trait Macros { self: Analyzer => */ def typedMacroBody(typer: Typer, ddef: DefDef): Tree = { import typer.context - if (macroDebug) println("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) + macroLogVerbose("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) if (!typer.checkFeature(ddef.pos, MacrosFeature, immediate = true)) { ddef.symbol setFlag IS_ERROR @@ -267,7 +267,7 @@ trait Macros { self: Analyzer => val rhs = ddef.rhs validatePreTyper(rhs) - if (hasErrors) macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) + if (hasErrors) macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) // we use typed1 instead of typed, because otherwise adapt is going to mess us up // if adapt sees <qualifier>.<method>, it will want to perform eta-expansion and will fail @@ -284,12 +284,7 @@ trait Macros { self: Analyzer => case Success(expanded) => try { val typechecked = typer.typed1(expanded, EXPRmode, WildcardType) - if (macroDebug) { - println("typechecked1:") - println(typechecked) - println(showRaw(typechecked)) - } - + macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked))) typechecked } finally { openMacros = openMacros.tail @@ -312,7 +307,7 @@ trait Macros { self: Analyzer => var rhs1 = typecheckRhs(rhs) def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors hasErrors = hasErrors || typecheckedWithErrors - if (typecheckedWithErrors) macroTrace("body of a macro def failed to typecheck: ")(ddef) + if (typecheckedWithErrors) macroTraceVerbose("body of a macro def failed to typecheck: ")(ddef) val macroImpl = rhs1.symbol macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(rhs1), Nil) @@ -330,7 +325,7 @@ trait Macros { self: Analyzer => validatePostTyper(rhs1) } if (hasErrors) - macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) + macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) } if (!hasErrors) { @@ -352,7 +347,7 @@ trait Macros { self: Analyzer => compatibilityError("number of parameter sections differ") def checkSubType(slot: String, reqtpe: Type, acttpe: Type): Unit = { - val ok = if (macroDebug) { + val ok = if (macroDebugVerbose) { if (reqtpe eq acttpe) println(reqtpe + " <: " + acttpe + "?" + EOL + "true") withTypesExplained(reqtpe <:< acttpe) } else reqtpe <:< acttpe @@ -437,7 +432,7 @@ trait Macros { self: Analyzer => val implicitParams = actparamss.flatten filter (_.isImplicit) if (implicitParams.length > 0) { reportError(implicitParams.head.pos, "macro implementations cannot have implicit parameters other than TypeTag evidences") - macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) + macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) } if (!hasErrors) { @@ -458,9 +453,9 @@ trait Macros { self: Analyzer => "\n found : "+showMeth(actparamss, actres, false)+ "\n"+addendum) - macroTrace("considering " + reqparamsss.length + " possibilities of compatible macro impl signatures for macro def: ")(ddef.name) + macroTraceVerbose("considering " + reqparamsss.length + " possibilities of compatible macro impl signatures for macro def: ")(ddef.name) val results = reqparamsss map (checkCompatibility(_, actparamss, reqres, actres)) - if (macroDebug) (reqparamsss zip results) foreach { case (reqparamss, result) => + if (macroDebugVerbose) (reqparamsss zip results) foreach { case (reqparamss, result) => println("%s %s".format(if (result.isEmpty) "[ OK ]" else "[FAILED]", reqparamss)) result foreach (errorMsg => println(" " + errorMsg)) } @@ -472,7 +467,7 @@ trait Macros { self: Analyzer => compatibilityError(mostRelevantMessage) } else { assert((results filter (_.isEmpty)).length == 1, results) - if (macroDebug) (reqparamsss zip results) filter (_._2.isEmpty) foreach { case (reqparamss, result) => + if (macroDebugVerbose) (reqparamsss zip results) filter (_._2.isEmpty) foreach { case (reqparamss, result) => println("typechecked macro impl as: " + reqparamss) } } @@ -596,11 +591,11 @@ trait Macros { self: Analyzer => val libraryClassLoader = { if (settings.XmacroPrimaryClasspath.value != "") { - if (macroDebug) println("primary macro mirror: initializing from -Xmacro-primary-classpath: %s".format(settings.XmacroPrimaryClasspath.value)) + macroLogVerbose("primary macro mirror: initializing from -Xmacro-primary-classpath: %s".format(settings.XmacroPrimaryClasspath.value)) val classpath = toURLs(settings.XmacroFallbackClasspath.value) ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) } else { - if (macroDebug) println("primary macro mirror: initializing from -cp: %s".format(global.classPath.asURLs)) + macroLogVerbose("primary macro mirror: initializing from -cp: %s".format(global.classPath.asURLs)) val classpath = global.classPath.asURLs var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) @@ -626,7 +621,7 @@ trait Macros { self: Analyzer => throw new UnsupportedOperationException("Scala reflection not available on this platform") val fallbackClassLoader = { - if (macroDebug) println("fallback macro mirror: initializing from -Xmacro-fallback-classpath: %s".format(settings.XmacroFallbackClasspath.value)) + macroLogVerbose("fallback macro mirror: initializing from -Xmacro-fallback-classpath: %s".format(settings.XmacroFallbackClasspath.value)) val classpath = toURLs(settings.XmacroFallbackClasspath.value) ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) } @@ -649,24 +644,24 @@ trait Macros { self: Analyzer => 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) + macroTraceVerbose("looking for macro implementation: ")(macroDef) + macroTraceVerbose("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) + macroTraceVerbose("@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) + macroTraceVerbose("@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)) + macroLogVerbose("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) + macroTraceVerbose("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef) return None } @@ -680,14 +675,14 @@ trait Macros { self: Analyzer => // 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")) + macroTraceVerbose("loading implementation class from %s: ".format(macroMirror))(macroImpl.owner.fullName) + macroTraceVerbose("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)) + macroTraceVerbose("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? @@ -711,7 +706,7 @@ trait Macros { self: Analyzer => val implClassName = classfile(macroImpl.owner) val implClassSymbol: macroMirror.Symbol = macroMirror.symbolForName(implClassName) - if (macroDebug) { + if (macroDebugVerbose) { println("implClassSymbol is: " + implClassSymbol.fullNameString) if (implClassSymbol != macroMirror.NoSymbol) { @@ -723,7 +718,7 @@ trait Macros { self: Analyzer => } val implObjSymbol = implClassSymbol.companionModule - macroTrace("implObjSymbol is: ")(implObjSymbol.fullNameString) + macroTraceVerbose("implObjSymbol is: ")(implObjSymbol.fullNameString) if (implObjSymbol == macroMirror.NoSymbol) None else { @@ -733,29 +728,27 @@ trait Macros { self: Analyzer => 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 + case ex: NoSuchFieldException => macroTraceVerbose("exception when loading implObj: ")(ex); null + case ex: NoClassDefFoundError => macroTraceVerbose("exception when loading implObj: ")(ex); null + case ex: ClassNotFoundException => macroTraceVerbose("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)) - } + macroLogVerbose("implMethSymbol is: " + implMethSymbol.fullNameString) + macroLogVerbose("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol)) if (implMethSymbol == macroMirror.NoSymbol) None else { - if (macroDebug) println("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol)) + macroLogVerbose("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) + macroTraceVerbose("implementation class failed to load: ")(ex.toString) None } } @@ -767,7 +760,7 @@ trait Macros { self: Analyzer => 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)) + macroLogVerbose("trying to load macro implementation from the fallback mirror: %s".format(settings.XmacroFallbackClasspath.value)) val fallback = loadMacroImpl(fallbackMirror) fallback match { case Some((implObj, implMethSymbol)) => @@ -827,13 +820,13 @@ trait Macros { self: Analyzer => collectMacroArgs(expandee) val context = expandee.attachmentOpt[MacroAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee)) var argss: List[List[Any]] = List(context) :: exprArgs.toList - macroTrace("argss: ")(argss) + macroTraceVerbose("argss: ")(argss) 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 val tparams = macroImpl.typeParams - macroTrace("paramss: ")(paramss) + macroTraceVerbose("paramss: ")(paramss) // we need to take care of all possible combos of nullary/empty-paramlist macro defs vs nullary/empty-arglist invocations // nullary def + nullary invocation => paramss and argss match, everything is okay @@ -845,7 +838,7 @@ trait Macros { self: Analyzer => val isEmptyParamlistDef = paramss_without_evidences.nonEmpty && paramss_without_evidences.last.isEmpty val isEmptyArglistInvocation = argss.nonEmpty && argss.last.isEmpty if (isEmptyParamlistDef && !isEmptyArglistInvocation) { - if (macroDebug) println("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") + macroLogVerbose("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") argss = argss :+ Nil } @@ -890,7 +883,7 @@ trait Macros { self: Analyzer => macroDef.owner) } else implRefTarg.tpe - if (macroDebug) println("resolved tparam %s as %s".format(tparam, tpe)) + macroLogVerbose("resolved tparam %s as %s".format(tparam, tpe)) resolved(tparam) = tpe param.tpe.typeSymbol match { case definitions.TypeTagClass => @@ -919,7 +912,7 @@ trait Macros { self: Analyzer => else as } val rawArgs = rawArgss.flatten - macroTrace("rawArgs: ")(rawArgs) + macroTraceVerbose("rawArgs: ")(rawArgs) Some(rawArgs) } @@ -927,16 +920,7 @@ trait Macros { self: Analyzer => * See more informations in comments to ``openMacros'' in ``scala.reflect.makro.Context''. */ var openMacros = List[MacroContext]() - private def openMacroPos = openMacros map (_.expandee.pos) find (_ ne NoPosition) getOrElse NoPosition - - // !!! YOu should use a method like this which manages the stack. - // That you presently need to spread this logic out is a major - // design flaw. - private def pushMacroContext[T](mc: MacroContext)(body: => T): T = { - openMacros ::= mc - try body - finally openMacros = openMacros.tail - } + def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition /** Performs macro expansion: * 1) Checks whether the expansion needs to be delayed (see ``mustDelayMacroExpansion'') @@ -945,13 +929,13 @@ trait Macros { self: Analyzer => * 4) Checks that the result is a tree bound to this universe * 5) Typechecks the result against the return type of the macro definition * - * If -Ymacro-debug is enabled, you will get detailed log of how exactly this function + * If -Ymacro-debug-lite is enabled, you will get basic notifications about macro expansion + * along with macro expansions logged in the form that can be copy/pasted verbatim into REPL. + * + * If -Ymacro-debug-verbose is enabled, you will get detailed log of how exactly this function * performs class loading and method resolution in order to load the macro implementation. * The log will also include other non-trivial steps of macro expansion. * - * If -Ymacro-copypaste is enabled along with -Ymacro-debug, you will get macro expansions - * logged in the form that can be copy/pasted verbatim into REPL (useful for debugging!). - * * @return * the expansion result if the expansion has been successful, * the fallback method invocation if the expansion has been unsuccessful, but there is a fallback, @@ -963,7 +947,7 @@ trait Macros { self: Analyzer => def macroExpand(typer: Typer, expandee: Tree, mode: Int = EXPRmode, pt: Type = WildcardType): Tree = { def fail(what: String, tree: Tree): Tree = { val err = typer.context.errBuffer.head - this.fail(typer, tree, "failed to perform %s: %s at %s".format(what, err.errMsg, err.errPos)) + this.fail(typer, tree, "failed to %s: %s at %s".format(what, err.errMsg, err.errPos)) return expandee } val start = startTimer(macroExpandNanos) @@ -984,31 +968,23 @@ trait Macros { self: Analyzer => } if (isNullaryInvocation) expectedTpe match { case NullaryMethodType(restpe) => - macroTrace("nullary invocation of a nullary method. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe) + macroTraceVerbose("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) + macroTraceVerbose("nullary invocation of a method with an empty parameter list. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe) expectedTpe = restpe case _ => ; } - if (macroDebug) println("typechecking1 against %s: %s".format(expectedTpe, expanded)) + macroLogVerbose("typechecking1 against %s: %s".format(expectedTpe, expanded)) var typechecked = typer.context.withImplicitsEnabled(typer.typed(expanded, EXPRmode, expectedTpe)) - if (typer.context.hasErrors) fail("typecheck1", expanded) - if (macroDebug) { - println("typechecked1:") - println(typechecked) - println(showRaw(typechecked)) - } + if (typer.context.hasErrors) fail("typecheck against macro def return type", expanded) + macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked))) - if (macroDebug) println("typechecking2 against %s: %s".format(pt, expanded)) + macroLogVerbose("typechecking2 against %s: %s".format(pt, expanded)) typechecked = typer.context.withImplicitsEnabled(typer.typed(typechecked, EXPRmode, pt)) - if (typer.context.hasErrors) fail("typecheck2", expanded) - if (macroDebug) { - println("typechecked2:") - println(typechecked) - println(showRaw(typechecked)) - } + if (typer.context.hasErrors) fail("typecheck against expected type", expanded) + macroLogVerbose("typechecked2:%n%s%n%s".format(typechecked, showRaw(typechecked))) typechecked } finally { @@ -1033,11 +1009,9 @@ trait Macros { self: Analyzer => 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 != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg - 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) + def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg + macroLogVerbose("macro expansion has failed: %s".format(msgForLog)) + val pos = if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition if (msg != null) typer.context.error(pos, msg) typer.infer.setError(expandee) Failure(expandee) @@ -1058,7 +1032,7 @@ trait Macros { self: Analyzer => // 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) + macroTraceVerbose("cancelled macro expansion because of %s: ".format(reason))(expandee) return Cancel(typer.infer.setError(expandee)) } @@ -1092,7 +1066,7 @@ trait Macros { self: Analyzer => val wasDelayed = isDelayed(expandee) val undetparams = calculateUndetparams(expandee) val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty - + def failExpansion(msg: String = null) = fail(typer, expandee, msg) def performExpansion(args: List[Any]): MacroExpansionResult = { val numErrors = reporter.ERROR.count @@ -1104,17 +1078,18 @@ trait Macros { self: Analyzer => failExpansion() // errors have been reported by the macro itself else expanded match { case expanded: Expr[_] => - if (macroDebug) println("original:") - macroDebugLog("" + expanded.tree + "\n" + showRaw(expanded.tree)) + macroLogVerbose("original:") + macroLogVerbose("" + expanded.tree + "\n" + showRaw(expanded.tree)) freeTerms(expanded.tree) foreach issueFreeError freeTypes(expanded.tree) foreach issueFreeError + if (hasNewErrors) failExpansion() + // inherit the position from the first position-ful expandee in macro callstack // this is essential for sane error messages // now macro expansion gets typechecked against the macro definition return type // however, this happens in macroExpand, not here in macroExpand1 - if (hasNewErrors) failExpansion() - else Success(atPos(openMacroPos.focus)(expanded.tree)) + else Success(atPos(enclosingMacroPosition.focus)(expanded.tree)) case _ => failExpansion( "macro must return a compiler-specific expr; returned value is " + ( @@ -1130,11 +1105,11 @@ trait Macros { self: Analyzer => else Skip(macroExpandAll(typer, expandee)) } else { - macroDebugLog("typechecking macro expansion %s at %s".format(expandee, expandee.pos)) + macroLogVerbose("typechecking macro expansion %s at %s".format(expandee, expandee.pos)) macroArgs(typer, expandee).fold(failExpansion(): MacroExpansionResult) { case args @ ((context: MacroContext) :: _) => if (nowDelayed) { - macroDebugLog("macro expansion is delayed: %s".format(expandee)) + macroLogVerbose("macro expansion is delayed: %s".format(expandee)) delayed += expandee -> undetparams // need to save typer context for `macroExpandAll` // need to save macro context to preserve enclosures @@ -1144,7 +1119,9 @@ trait Macros { self: Analyzer => else { // adding stuff to openMacros is easy, but removing it is a nightmare // it needs to be sprinkled over several different code locations - openMacros ::= args.head.asInstanceOf[MacroContext] + // why? https://github.com/scala/scala/commit/bd3eacbae21f39b1ac7fe8ade4ed71fa98e1a28d#L2R1137 + // todo. will be improved + openMacros ::= context var isSuccess = false try performExpansion(args) match { case x: Success => isSuccess = true ; x @@ -1158,7 +1135,7 @@ trait Macros { self: Analyzer => } } } - + try macroExpandInternal catch { case ex => handleMacroExpansionException(typer, expandee, ex) } } @@ -1179,7 +1156,7 @@ trait Macros { self: Analyzer => case first :: _ => Some(Select(qual, name) setPos tree.pos setSymbol first) case _ => - macroTrace("macro is not overridden: ")(tree) + macroTraceVerbose("macro is not overridden: ")(tree) notFound() } case Apply(fn, args) => @@ -1193,33 +1170,29 @@ trait Macros { self: Analyzer => case _ => None } case _ => - macroTrace("unexpected tree in fallback: ")(tree) + macroTraceVerbose("unexpected tree in fallback: ")(tree) notFound() } } fallBackToOverridden(expandee) match { case Some(tree1) => - macroTrace("falling back to: ")(tree1) + macroTraceVerbose("falling back to: ")(tree1) currentRun.macroExpansionFailed = true Fallback(tree1) case None => fail(typer, expandee) } } - - @inline final def macroDebugLog(msg: => String) { - if (macroDebug || macroCopypaste) println(msg) - } - private def handleMacroExpansionException(typer: Typer, expandee: Tree, ex: Throwable): MacroExpansionResult = { + 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 => - macroDebugLog("macro expansion has failed: %s".format(realex.msg)) + macroLogVerbose("macro expansion has failed: %s".format(realex.msg)) fail(typer, expandee) // error has been reported by abort case err: TypeError => - macroDebugLog("macro expansion has failed: %s at %s".format(err.msg, err.pos)) + macroLogVerbose("macro expansion has failed: %s at %s".format(err.msg, err.pos)) throw err // error should be propagated, don't report case _ => val message = { @@ -1271,24 +1244,24 @@ trait Macros { self: Analyzer => if (sub.symbol != null) traverse(sub.symbol) if (sub.tpe != null) sub.tpe foreach (sub => traverse(sub.typeSymbol)) }) - if (macroDebug) println("calculateUndetparams: %s".format(calculated)) + macroLogVerbose("calculateUndetparams: %s".format(calculated)) calculated map (_.id) } private val undetparams = perRunCaches.newSet[Int] def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = { undetparams ++= newUndets map (_.id) - if (macroDebug) newUndets foreach (sym => println("undetParam added: %s".format(sym))) + if (macroDebugVerbose) 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 (macroDebugVerbose) (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 => undetparams --= undetNoMore map (_.id) if (undetparams.isEmpty) { hasPendingMacroExpansions = true - macroTrace("macro expansion is pending: ")(expandee) + macroTraceVerbose("macro expansion is pending: ")(expandee) } case _ => // do nothing diff --git a/src/library/scala/reflect/makro/Typers.scala b/src/library/scala/reflect/makro/Typers.scala index 1ced2daccd..c62c5f254c 100644 --- a/src/library/scala/reflect/makro/Typers.scala +++ b/src/library/scala/reflect/makro/Typers.scala @@ -29,7 +29,7 @@ trait Typers { * * If ``silent'' is false, ``TypeError'' will be thrown in case of a typecheck error. * If ``silent'' is true, the typecheck is silent and will return ``EmptyTree'' if an error occurs. - * Such errors don't vanish and can be inspected by turning on -Ymacro-debug. + * Such errors don't vanish and can be inspected by turning on -Ymacro-debug-verbose. * Unlike in ``inferImplicitValue'' and ``inferImplicitView'', ``silent'' is false by default. * * Typechecking can be steered with the following optional parameters: |