Skip to content

Commit

Permalink
improves traces and error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
xeno-by committed Jun 8, 2012
1 parent c74533a commit c7491eb
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 51 deletions.
10 changes: 9 additions & 1 deletion src/compiler/scala/reflect/internal/TreePrinters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable =>

private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = {
val sym = tree.symbol
if (sym != null && sym != NoSymbol) {
if (sym.name.toString == nme.ERROR.toString) {
"<" + quotedName(name, decoded) + ": error>"
} else if (sym != null && sym != NoSymbol) {
val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else ""
var suffix = ""
if (settings.uniqid.value) suffix += ("#" + sym.id)
Expand Down Expand Up @@ -167,6 +169,12 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable =>
}

def printAnnotations(tree: Tree) {
if (inReflexiveMirror && tree.symbol != null && tree.symbol != NoSymbol)
// [Eugene++] todo. this is not 100% correct, but is necessary for sane printing
// the problem is that getting annotations doesn't automatically initialize the symbol
// so we might easily print something as if it doesn't have annotations, whereas it does
tree.symbol.initialize

val annots = tree.symbol.annotations match {
case Nil => tree.asInstanceOf[MemberDef].mods.annotations
case anns => anns
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/scala/reflect/reify/Errors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ trait Errors {
import mirror._
import definitions._

def defaultErrorPosition = analyzer.enclosingMacroPosition
def defaultErrorPosition = {
val stack = currents collect { case t: Tree if t.pos != NoPosition => t.pos }
stack.headOption getOrElse 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
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/reflect/reify/phases/Reify.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ trait Reify extends Symbols
}
def currentQuantified = flatCollect(reifyStack.currents)({ case ExistentialType(quantified, _) => quantified })
def current = reifyStack.currents.head
def currents = reifyStack.currents

/**
* Reifies any supported value.
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/scala/tools/nsc/ast/NodePrinters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ abstract class NodePrinters {
str.toString
}
def printModifiers(tree: MemberDef) {
// [Eugene++] there's most likely a bug here (?)
// see `TreePrinters.printAnnotations` for more information
val annots0 = tree.symbol.annotations match {
case Nil => tree.mods.annotations
case xs => xs map annotationInfoToString
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import scala.collection.{ mutable, immutable }
*
* @author Lex Spoon
*/
class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader)
class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader)
extends ClassLoader(parent)
with ScalaClassLoader
{
Expand Down
18 changes: 13 additions & 5 deletions src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1183,16 +1183,24 @@ trait Implicits {
// if ``pre'' is not a PDT, e.g. if someone wrote
// implicitly[scala.reflect.makro.Context#TypeTag[Int]]
// then we need to fail, because we don't know the prefix to use during type reification
return failure(tp, "tag error: unsupported prefix type %s (%s)".format(pre, pre.kind))
// upd. we also need to fail silently, because this is a very common situation
// e.g. quite often we're searching for BaseUniverse#TypeTag, e.g. for a type tag in any universe
// so that if we find one, we could convert it to whatever universe we need by the means of the `in` method
// if no tag is found in scope, we end up here, where we ask someone to materialize the tag for us
// however, since the original search was about a tag with no particular prefix, we cannot proceed
// this situation happens very often, so emitting an error message here (even if only for -Xlog-implicits) would be too much
//return failure(tp, "tag error: unsupported prefix type %s (%s)".format(pre, pre.kind))
return SearchFailure
}
)
// todo. migrate hardcoded materialization in Implicits to corresponding implicit macros
var materializer = atPos(pos.focus)(
gen.mkMethodCall(TagMaterializers(tagClass), List(tp), List(prefix))
)
var materializer = atPos(pos.focus)(gen.mkMethodCall(TagMaterializers(tagClass), List(tp), List(prefix)))
if (settings.XlogImplicits.value) println("materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer))
if (context.macrosEnabled) success(materializer)
else failure(materializer, "macros are disabled")
// don't call `failure` here. if macros are disabled, we just fail silently
// otherwise -Xlog-implicits will spam the long with zillions of "macros are disabled"
// this is ugly but temporary, since all this code will be removed once I fix implicit macros
else SearchFailure
}

private val ManifestSymbols = Set[Symbol](PartialManifestClass, FullManifestClass, OptManifestClass)
Expand Down
53 changes: 21 additions & 32 deletions src/compiler/scala/tools/nsc/typechecker/Macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ trait Macros extends Traces {
macroLogVerbose("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos))

if (!typer.checkFeature(ddef.pos, MacrosFeature, immediate = true)) {
macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled")
ddef.symbol setFlag IS_ERROR
return EmptyTree
}
Expand Down Expand Up @@ -675,31 +676,20 @@ trait Macros extends Traces {
private def macroRuntime(macroDef: Symbol): Option[MacroRuntime] = {
macroTraceVerbose("looking for macro implementation: ")(macroDef)
if (fastTrack contains macroDef) {
macroLogVerbose("macro expansion serviced by a fast track")
macroLogVerbose("macro expansion is serviced by a fast track")
Some(fastTrack(macroDef))
} else {
macroRuntimesCache.getOrElseUpdate(macroDef, {
val runtime = {
macroTraceVerbose("looking for macro implementation: ")(macroDef)
macroTraceVerbose("macroDef is annotated with: ")(macroDef.annotations)

val ann = macroDef.getAnnotation(MacroImplAnnotation)
if (ann == None) {
macroTraceVerbose("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef)
return None
}
if (ann == None) { 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) {
macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef)
return None
}

if (macroImpl == NoSymbol) { macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef); return None }
macroLogVerbose("resolved implementation %s at %s".format(macroImpl, macroImpl.pos))
if (macroImpl.isErroneous) {
macroTraceVerbose("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef)
return None
}
if (macroImpl.isErroneous) { macroTraceVerbose("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 {
Expand All @@ -711,14 +701,8 @@ trait Macros extends Traces {
// 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

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>"
}
macroTraceVerbose("classpath is: ")(inferClasspath(macroMirror.classLoader))
macroTraceVerbose("loading implementation class: ")(macroImpl.owner.fullName)
macroTraceVerbose("classloader is: ")(ReflectionUtils.show(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?
Expand Down Expand Up @@ -749,7 +733,7 @@ trait Macros extends Traces {
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)))
println("implClassLoader is %s".format(implClass.getClassLoader, ReflectionUtils.show(implClass.getClassLoader)))
}
}

Expand Down Expand Up @@ -786,6 +770,9 @@ trait Macros extends Traces {
case ex: ClassNotFoundException =>
macroTraceVerbose("implementation class failed to load: ")(ex.toString)
None
case ex: NoSuchMethodException =>
macroTraceVerbose("implementation method failed to load: ")(ex.toString)
None
}
}

Expand Down Expand Up @@ -985,15 +972,17 @@ trait Macros extends Traces {
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 %s: %s at %s".format(what, err.errMsg, err.errPos))
this.fail(typer, tree, err.errPos, "failed to %s: %s".format(what, err.errMsg))
return expandee
}
val start = startTimer(macroExpandNanos)
incCounter(macroExpandCount)
try {
macroExpand1(typer, expandee) match {
case Success(expanded) =>
case Success(expanded0) =>
try {
val expanded = expanded0 // virtpatmat swallows the local for expandee from the match
// so I added this dummy local for the ease of debugging
var expectedTpe = expandee.tpe

// [Eugene] weird situation. what's the conventional way to deal with it?
Expand Down Expand Up @@ -1046,11 +1035,11 @@ trait Macros extends Traces {
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) = {
private def fail(typer: Typer, expandee: Tree, pos: Position = NoPosition, msg: String = null) = {
def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg
macroLogLite("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)
val errorPos = if (pos != NoPosition) pos else (if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition)
if (msg != null) typer.context.error(errorPos, msg)
typer.infer.setError(expandee)
Failure(expandee)
}
Expand All @@ -1069,7 +1058,7 @@ trait Macros extends Traces {
// 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"
val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments"
macroTraceVerbose("cancelled macro expansion because of %s: ".format(reason))(expandee)
return Cancel(typer.infer.setError(expandee))
}
Expand Down Expand Up @@ -1105,7 +1094,7 @@ trait Macros extends Traces {
val undetparams = calculateUndetparams(expandee)
val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty

def failExpansion(msg: String = null) = fail(typer, expandee, msg)
def failExpansion(msg: String = null) = fail(typer, expandee, msg = msg)
def performExpansion(args: List[Any]): MacroExpansionResult = {
val numErrors = reporter.ERROR.count
def hasNewErrors = reporter.ERROR.count > numErrors
Expand Down Expand Up @@ -1257,7 +1246,7 @@ trait Macros extends Traces {
None
}
} getOrElse realex.getMessage
fail(typer, expandee, "exception during macro expansion: " + message)
fail(typer, expandee, msg = "exception during macro expansion: " + message)
}
}

Expand Down
16 changes: 11 additions & 5 deletions src/library/scala/reflect/ReflectionUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,26 @@ object ReflectionUtils {
System.getProperties.asScala.iterator
}

private def searchForBootClasspath = (
private def inferBootClasspath: String = (
systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse ""
)

def show(cl: ClassLoader) = {
def inferClasspath(cl: ClassLoader) = cl match {
case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
case _ => "<unknown>"
def inferClasspath(cl: ClassLoader): String = cl match {
case cl: java.net.URLClassLoader =>
"[" + (cl.getURLs mkString ",") + "]"
case cl if cl != null && cl.getClass.getName == "scala.tools.nsc.interpreter.AbstractFileClassLoader" =>
"[" + cl.asInstanceOf[{val root: api.RequiredFile}].root + "] and " + inferClasspath(cl.getParent)
case null =>
inferBootClasspath
case _ =>
"<unknown>"
}
cl match {
case cl if cl != null =>
"%s of type %s with classpath %s".format(cl, cl.getClass, inferClasspath(cl))
case null =>
"primordial classloader with boot classpath [%s]".format(searchForBootClasspath)
"primordial classloader with boot classpath [%s]".format(inferClasspath(cl))
}
}

Expand Down
14 changes: 8 additions & 6 deletions src/library/scala/reflect/makro/internal/Utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ package internal {
else gen.mkMethodCall(staticModule("scala.reflect.package"), newTermName("manifestToConcreteTypeTag"), List(tpe), List(manifestInScope))
}
try c.typeCheck(result)
catch { case terr @ c.TypeError(pos, msg) => failTag(terr) }
catch { case terr @ c.TypeError(pos, msg) => failTag(result, terr) }
}

private def nonSyntheticManifestInScope(tpe: Type) = {
Expand All @@ -116,7 +116,7 @@ package internal {
def materializeExpr(prefix: Tree, expr: Tree): Tree = {
val result = translatingReificationErrors(c.reifyTree(prefix, expr))
try c.typeCheck(result)
catch { case terr @ c.TypeError(pos, msg) => failExpr(terr) }
catch { case terr @ c.TypeError(pos, msg) => failExpr(result, terr) }
}

private def translatingReificationErrors(materializer: => Tree): Tree = {
Expand All @@ -130,17 +130,19 @@ package internal {
}
}

private def failTag(reason: Any): Nothing = {
private def failTag(result: Tree, reason: Any): Nothing = {
val Apply(TypeApply(fun, List(tpeTree)), _) = c.macroApplication
val tpe = tpeTree.tpe
val PolyType(_, MethodType(_, tagTpe)) = fun.tpe
val tagModule = tagTpe.typeSymbol.companionSymbol
if (c.compilerSettings.contains("-Xlog-implicits"))
c.echo(c.enclosingPosition, "cannot materialize " + tagModule.name + "[" + tpe + "] because:\n" + reason)
c.echo(c.enclosingPosition, s"cannot materialize ${tagModule.name}[$tpe] as $result because:\n$reason")
c.abort(c.enclosingPosition, "No %s available for %s".format(tagModule.name, tpe))
}

private def failExpr(reason: Any): Nothing =
c.abort(c.enclosingPosition, "Cannot materialize Expr because:\n" + reason)
private def failExpr(result: Tree, reason: Any): Nothing = {
val Apply(_, expr :: Nil) = c.macroApplication
c.abort(c.enclosingPosition, s"Cannot materialize $expr as $result because:\n$reason")
}
}
}

0 comments on commit c7491eb

Please sign in to comment.