From e9926a5207aadcfe3831b51b1cd6164757278013 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 17 Feb 2012 17:10:49 +0100 Subject: Fixes miscellaneous macro bugs --- .../tools/nsc/interpreter/MemberHandlers.scala | 5 +- .../tools/nsc/typechecker/ContextErrors.scala | 5 -- .../scala/tools/nsc/typechecker/Macros.scala | 77 ++++++++++++++++++---- .../scala/tools/nsc/typechecker/Typers.scala | 19 +++--- 4 files changed, 75 insertions(+), 31 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index c742ab89c0..48a5fa9e34 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -118,8 +118,9 @@ trait MemberHandlers { class DefHandler(member: DefDef) extends MemberDefHandler(member) { private def vparamss = member.vparamss - // true if 0-arity - override def definesValue = vparamss.isEmpty || vparamss.head.isEmpty + private def isMacro = member.mods.hasFlag(scala.reflect.internal.Flags.MACRO) + // true if not a macro and 0-arity + override def definesValue = !isMacro && (vparamss.isEmpty || vparamss.head.isEmpty) override def resultExtractionCode(req: Request) = if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else "" } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 6ee09d064f..09eb504186 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -636,11 +636,6 @@ trait ContextErrors { def CyclicReferenceError(errPos: Position, lockedSym: Symbol) = issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym)) - - def MacroExpandError(tree: Tree, t: Any) = { - issueNormalTypeError(tree, "macros must return a compiler-specific tree; returned class is: " + t.getClass) - setError(tree) - } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 470f3e7117..161e4b7a9a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -90,8 +90,19 @@ trait Macros { self: Analyzer => lazy val mirror = new scala.reflect.runtime.Mirror { lazy val libraryClassLoader = { + // todo. this is more or less okay, but not completely correct + // see https://issues.scala-lang.org/browse/SI-5433 for more info val classpath = global.classPath.asURLs - ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) + + // an heuristic to detect REPL + if (global.settings.exposeEmptyPackage.value) { + import scala.tools.nsc.interpreter._ + val virtualDirectory = global.settings.outputDirs.getSingleOutput.get + loader = new AbstractFileClassLoader(virtualDirectory, loader) {} + } + + loader } override def defaultReflectiveClassLoader() = libraryClassLoader @@ -112,19 +123,42 @@ trait Macros { self: Analyzer => if (mmeth == NoSymbol) None else { - val receiverClass: mirror.Symbol = mirror.symbolForName(mmeth.owner.fullName) + trace("loading implementation class: ")(mmeth.owner.fullName) + trace("classloader is: ")("%s of type %s".format(mirror.libraryClassLoader, mirror.libraryClassLoader.getClass)) + def inferClasspath(cl: ClassLoader) = cl match { + case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" + case _ => "" + } + trace("classpath is: ")(inferClasspath(mirror.libraryClassLoader)) + + // @xeno.by: relies on the fact that macros can only be defined in static classes + 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 + } + + if (sym.isClass || sym.isModule) recur(sym) + else recur(sym.enclClass) + } + + // @xeno.by: this doesn't work for inner classes + // neither does mmeth.owner.javaClassName, so I had to roll my own implementation + //val receiverName = mmeth.owner.fullName + val receiverName = classfile(mmeth.owner) + val receiverClass: mirror.Symbol = mirror.symbolForName(receiverName) + if (debug) { println("receiverClass is: " + receiverClass.fullNameString) val jreceiverClass = mirror.classToJava(receiverClass) val jreceiverSource = jreceiverClass.getProtectionDomain.getCodeSource println("jreceiverClass is %s from %s".format(jreceiverClass, jreceiverSource)) - - val jreceiverClasspath = jreceiverClass.getClassLoader match { - case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" - case _ => "" - } - println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, jreceiverClasspath)) + println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, inferClasspath(jreceiverClass.getClassLoader))) } val receiverObj = receiverClass.companionModule @@ -132,7 +166,11 @@ trait Macros { self: Analyzer => if (receiverObj == mirror.NoSymbol) None else { - val receiver = mirror.companionInstance(receiverClass) + // @xeno.by: yet another reflection method that doesn't work for inner classes + //val receiver = mirror.companionInstance(receiverClass) + val clazz = java.lang.Class.forName(receiverName, true, mirror.libraryClassLoader) + val receiver = clazz getField "MODULE$" get null + val rmeth = receiverObj.info.member(mirror.newTermName(mmeth.name.toString)) if (debug) { println("rmeth is: " + rmeth.fullNameString) @@ -147,6 +185,7 @@ trait Macros { self: Analyzer => } } catch { case ex: ClassNotFoundException => + trace("implementation class failed to load: ")(ex.toString) None } } @@ -155,7 +194,7 @@ trait Macros { self: Analyzer => * Or, if that fails, and the macro overrides a method return * tree that calls this method instead of the macro. */ - def macroExpand(tree: Tree, context: Context): Option[Any] = { + def macroExpand(tree: Tree, typer: Typer): Option[Any] = { val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value trace("macroExpand: ")(tree) @@ -180,7 +219,19 @@ trait Macros { self: Analyzer => // 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 - Some(mirror.invoke(receiver, rmeth)(rawArgs: _*)) + val expanded = mirror.invoke(receiver, rmeth)(rawArgs: _*) + expanded match { + case expanded: Tree => + val expectedTpe = tree.tpe + val typed = typer.typed(expanded, EXPRmode, expectedTpe) + Some(typed) + case expanded if expanded.isInstanceOf[Tree] => + typer.context.unit.error(tree.pos, "macro must return a compiler-specific tree; returned value is Tree, but it doesn't belong to this compiler's universe") + None + case expanded => + typer.context.unit.error(tree.pos, "macro must return a compiler-specific tree; returned value is of class: " + expanded.getClass) + None + } } catch { case ex => val realex = ReflectionUtils.unwrapThrowable(ex) @@ -191,14 +242,14 @@ trait Macros { self: Analyzer => } else { realex.getMessage } - context.unit.error(tree.pos, "exception during macro expansion: " + msg) + typer.context.unit.error(tree.pos, "exception during macro expansion: " + msg) None } finally { nodePrinters.infolevel = savedInfolevel } case None => def notFound() = { - context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name) + typer.context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name) None } def fallBackToOverridden(tree: Tree): Option[Tree] = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ef69e1525e..9275156da7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -941,10 +941,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (tree.isType) adaptType() - else if (inExprModeButNot(mode, FUNmode) && tree.symbol != null && tree.symbol.isMacro && !tree.isDef) { - val tree1 = expandMacro(tree) - if (tree1.isErroneous) tree1 else typed(tree1, mode, pt) - } else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) + else if (inExprModeButNot(mode, FUNmode) && tree.symbol != null && tree.symbol.isMacro && !tree.isDef && !(tree exists (_.isErroneous))) + macroExpand(tree, this) match { + case Some(expanded: Tree) => + typed(expanded, mode, pt) + case None => + setError(tree) // error already reported + } + else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode)) adaptConstrPattern() else if (inAllModes(mode, EXPRmode | FUNmode) && !tree.tpe.isInstanceOf[MethodType] && @@ -4536,13 +4540,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } - def expandMacro(tree: Tree): Tree = - macroExpand(tree, context) match { - case Some(t: Tree) => t - case Some(t) => MacroExpandError(tree, t) - case None => setError(tree) // error already reported - } - def atOwner(owner: Symbol): Typer = newTyper(context.make(context.tree, owner)) -- cgit v1.2.3