Skip to content

Commit

Permalink
[backport] transformers no longer ignore UnApply.fun
Browse files Browse the repository at this point in the history
Backports 7122560 and 4133eb8 from the 2.11.x branch
  • Loading branch information
xeno-by committed Jul 3, 2014
1 parent 300db2a commit 36379cf
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 42 deletions.
104 changes: 63 additions & 41 deletions src/compiler/scala/tools/reflect/ToolBoxFactory.scala
Expand Up @@ -5,9 +5,10 @@ import scala.tools.nsc.reporters._
import scala.tools.nsc.CompilerCommand
import scala.tools.nsc.Global
import scala.tools.nsc.typechecker.Modes
import scala.tools.nsc.io.VirtualDirectory
import scala.tools.nsc.io.{AbstractFile, VirtualDirectory}
import scala.tools.nsc.interpreter.AbstractFileClassLoader
import scala.tools.nsc.util.FreshNameCreator
import scala.tools.nsc.util.CommandLineParser
import scala.tools.nsc.ast.parser.Tokens.EOF
import scala.reflect.internal.Flags._
import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile}
Expand All @@ -17,6 +18,7 @@ import scala.reflect.NameTransformer
import scala.reflect.api.JavaUniverse
import scala.reflect.io.NoAbstractFile
import scala.tools.nsc.interactive.RangePositions
import scala.reflect.internal.FatalError

abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>

Expand All @@ -32,6 +34,13 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
lazy val classLoader = new AbstractFileClassLoader(virtualDirectory, factorySelf.mirror.classLoader)
lazy val mirror: u.Mirror = u.runtimeMirror(classLoader)

lazy val arguments = CommandLineParser.tokenize(options)
lazy val virtualDirectory =
arguments.iterator.sliding(2).collectFirst{ case Seq("-d", dir) => dir } match {
case Some(outDir) => AbstractFile.getDirectory(outDir)
case None => new VirtualDirectory("(memory)", None)
}

class ToolBoxGlobal(settings: scala.tools.nsc.Settings, reporter: Reporter)
extends ReflectGlobal(settings, reporter, toolBoxSelf.classLoader) {
import definitions._
Expand All @@ -50,7 +59,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
}

// should be called after every use of ToolBoxGlobal in order to prevent leaks
// there's the `withCleanupCaches` method defined below, which provides a convenient interface for that
def cleanupCaches(): Unit = {
perRunCaches.clearAll()
undoLog.clear()
Expand All @@ -59,10 +67,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
lastSeenContext = null
}

def withCleanupCaches[T](body: => T): T =
try body
finally cleanupCaches()

def verify(expr: Tree): Tree = {
// Previously toolboxes used to typecheck their inputs before compiling.
// Actually, the initial demo by Martin first typechecked the reified tree,
Expand Down Expand Up @@ -312,42 +316,51 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
}
}

// todo. is not going to work with quoted arguments with embedded whitespaces
lazy val arguments = options.split(" ")
trait CompilerApi {
val compiler: ToolBoxGlobal
val importer: compiler.Importer { val from: u.type }
val exporter: u.Importer { val from: compiler.type }
}

lazy val virtualDirectory =
(arguments zip arguments.tail).collect{ case ("-d", dir) => dir }.lastOption match {
case Some(outDir) => scala.tools.nsc.io.AbstractFile.getDirectory(outDir)
case None => new VirtualDirectory("(memory)", None)
object withCompilerApi {
private object api extends CompilerApi {
lazy val compiler: ToolBoxGlobal = {
try {
val errorFn: String => Unit = msg => frontEnd.log(scala.reflect.internal.util.NoPosition, msg, frontEnd.ERROR)
val command = new CompilerCommand(arguments.toList, errorFn)
val settings = command.settings
settings.outputDirs setSingleOutput virtualDirectory
val reporter = frontEndToReporter(frontEnd, command.settings)
val instance =
if (settings.Yrangepos.value) new ToolBoxGlobal(settings, reporter) with RangePositions
else new ToolBoxGlobal(settings, reporter)
if (frontEnd.hasErrors) {
var msg = "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL
msg += frontEnd.infos map (_.msg) mkString EOL
throw ToolBoxError(msg)
}
instance
} catch {
case ex: Throwable =>
var msg = "reflective compilation has failed: cannot initialize the compiler due to %s".format(ex.toString)
throw ToolBoxError(msg, ex)
}
}

lazy val importer = compiler.mkImporter(u)
lazy val exporter = importer.reverse
}

lazy val compiler: ToolBoxGlobal = {
try {
val errorFn: String => Unit = msg => frontEnd.log(scala.reflect.internal.util.NoPosition, msg, frontEnd.ERROR)
val command = new CompilerCommand(arguments.toList, errorFn)
val settings = command.settings
settings.outputDirs setSingleOutput virtualDirectory
val reporter = frontEndToReporter(frontEnd, command.settings)
val instance =
if (settings.Yrangepos.value) new ToolBoxGlobal(settings, reporter) with RangePositions
else new ToolBoxGlobal(settings, reporter)
if (frontEnd.hasErrors) {
var msg = "reflective compilation has failed: cannot initialize the compiler: " + EOL + EOL
msg += frontEnd.infos map (_.msg) mkString EOL
throw ToolBoxError(msg)
}
instance
} catch {
case ex: Throwable =>
var msg = "reflective compilation has failed: cannot initialize the compiler due to %s".format(ex.toString)
throw ToolBoxError(msg, ex)
def apply[T](f: CompilerApi => T): T = {
object FatalError { def unapply(ex: Throwable) = ex match { case _: FatalError | _: AssertionError => Some(ex); case _ => None } }
try f(api) catch { case FatalError(ex) => throw ToolBoxError(s"fatal compiler error", ex) }
finally api.compiler.cleanupCaches()
}
}

lazy val importer = compiler.mkImporter(u)
lazy val exporter = importer.reverse
def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = withCompilerApi { compilerApi =>
import compilerApi._

def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = compiler.withCleanupCaches {
if (compiler.settings.verbose.value) println("importing "+tree+", expectedType = "+expectedType)
var ctree: compiler.Tree = importer.importTree(tree)
var cexpectedType: compiler.Type = importer.importType(expectedType)
Expand All @@ -367,7 +380,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
inferImplicit(tree, viewTpe, isView = true, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = pos)
}

private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = compiler.withCleanupCaches {
private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = withCompilerApi { compilerApi =>
import compilerApi._

if (compiler.settings.verbose.value) println("importing "+pt, ", tree = "+tree+", pos = "+pos)
var ctree: compiler.Tree = importer.importTree(tree)
var cpt: compiler.Type = importer.importType(pt)
Expand All @@ -379,31 +394,38 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
uitree
}

def resetAllAttrs(tree: u.Tree): u.Tree = {
def resetAllAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi =>
import compilerApi._
val ctree: compiler.Tree = importer.importTree(tree)
val ttree: compiler.Tree = compiler.resetAllAttrs(ctree)
val uttree = exporter.importTree(ttree)
uttree
}

def resetLocalAttrs(tree: u.Tree): u.Tree = {
def resetLocalAttrs(tree: u.Tree): u.Tree = withCompilerApi { compilerApi =>
import compilerApi._
val ctree: compiler.Tree = importer.importTree(tree)
val ttree: compiler.Tree = compiler.resetLocalAttrs(ctree)
val uttree = exporter.importTree(ttree)
uttree
}

def showAttributed(tree: u.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String =
def showAttributed(tree: u.Tree, printTypes: Boolean = true, printIds: Boolean = true, printKinds: Boolean = false): String = withCompilerApi { compilerApi =>
import compilerApi._
compiler.showAttributed(importer.importTree(tree), printTypes, printIds, printKinds)
}

def parse(code: String): u.Tree = {
def parse(code: String): u.Tree = withCompilerApi { compilerApi =>
import compilerApi._
if (compiler.settings.verbose.value) println("parsing "+code)
val ctree: compiler.Tree = compiler.parse(code)
val utree = exporter.importTree(ctree)
utree
}

def compile(tree: u.Tree): () => Any = {
def compile(tree: u.Tree): () => Any = withCompilerApi { compilerApi =>
import compilerApi._

if (compiler.settings.verbose.value) println("importing "+tree)
val ctree: compiler.Tree = importer.importTree(tree)

Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/Trees.scala
Expand Up @@ -1315,7 +1315,7 @@ trait Trees extends api.Trees { self: SymbolTable =>
case Star(elem) =>
treeCopy.Star(tree, transform(elem))
case UnApply(fun, args) =>
treeCopy.UnApply(tree, fun, transformTrees(args)) // bq: see test/.../unapplyContexts2.scala
treeCopy.UnApply(tree, transform(fun), transformTrees(args)) // bq: see test/.../unapplyContexts2.scala
case ArrayValue(elemtpt, trees) =>
treeCopy.ArrayValue(tree, transform(elemtpt), transformTrees(trees))
case ApplyDynamic(qual, args) =>
Expand Down
1 change: 1 addition & 0 deletions test/files/run/t7871.check
@@ -0,0 +1 @@
(SomeTree,SomeTree)
43 changes: 43 additions & 0 deletions test/files/run/t7871/Macros_1.scala
@@ -0,0 +1,43 @@
import scala.reflect.macros.Context
import scala.language.experimental.macros

trait Tree
case object SomeTree extends Tree

object NewQuasiquotes {
implicit class QuasiquoteInterpolation(c: StringContext) {
object nq {
def unapply(t: Tree): Any = macro QuasiquoteMacros.unapplyImpl
}
}
}

object QuasiquoteMacros {
def unapplyImpl(c: Context)(t: c.Expr[Tree]) = {
import c.universe._
import Flag._
// q"""
// new {
// def unapply(t: Tree) = t match {
// case SomeTree => Some((SomeTree, SomeTree))
// case _ => None
// }
// }.unapply($t)
// """
c.Expr[Any](Apply(
Select(
Block(List(
ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(),
Template(List(Select(Ident(newTermName("scala")), newTypeName("AnyRef"))), emptyValDef, List(
DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(),
Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))),
DefDef(Modifiers(), newTermName("unapply"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("t"), Ident(newTypeName("Tree")), EmptyTree))), TypeTree(),
Match(
Ident(newTermName("t")), List(
CaseDef(Ident(newTermName("SomeTree")), EmptyTree, Apply(Ident(newTermName("Some")), List(Apply(Select(Ident(newTermName("scala")), newTermName("Tuple2")), List(Ident(newTermName("SomeTree")), Ident(newTermName("SomeTree"))))))),
CaseDef(Ident(nme.WILDCARD), EmptyTree, Ident(newTermName("None")))))))))),
Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List())),
newTermName("unapply")),
List(t.tree)))
}
}
6 changes: 6 additions & 0 deletions test/files/run/t7871/Test_2.scala
@@ -0,0 +1,6 @@
object Test extends App {
import NewQuasiquotes._
SomeTree match {
case nq"$x + $y" => println((x, y))
}
}

0 comments on commit 36379cf

Please sign in to comment.