summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-04-12 01:59:46 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-04-12 02:04:14 +0200
commit814cf34fb00f9ccb001249f4b3445ebc4f9942c9 (patch)
tree24dd54da571d27f10b0c482a6e08932c318fd7b2 /src/compiler/scala/tools/nsc
parentdb3056f11730da19e4e56f09f12e300bda62f57c (diff)
downloadscala-814cf34fb00f9ccb001249f4b3445ebc4f9942c9.tar.gz
scala-814cf34fb00f9ccb001249f4b3445ebc4f9942c9.tar.bz2
scala-814cf34fb00f9ccb001249f4b3445ebc4f9942c9.zip
Next generation of macros
Implements SIP 16: Self-cleaning macros: http://bit.ly/wjjXTZ Features: * Macro defs * Reification * Type tags * Manifests aliased to type tags * Extended reflection API * Several hundred tests * 1111 changed files Not yet implemented: * Reification of refined types * Expr.value splicing * Named and default macro expansions * Intricacies of interaction between macros and implicits * Emission of debug information for macros (compliant with JSR-45) Dedicated to Yuri Alekseyevich Gagarin
Diffstat (limited to 'src/compiler/scala/tools/nsc')
-rw-r--r--src/compiler/scala/tools/nsc/ClassLoaders.scala64
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala46
-rw-r--r--src/compiler/scala/tools/nsc/MacroContext.scala10
-rw-r--r--src/compiler/scala/tools/nsc/ReflectGlobal.scala7
-rw-r--r--src/compiler/scala/tools/nsc/ReflectMain.scala11
-rw-r--r--src/compiler/scala/tools/nsc/ToolBoxes.scala85
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/DocComments.scala4
-rw-r--r--src/compiler/scala/tools/nsc/ast/FreeVars.scala26
-rw-r--r--src/compiler/scala/tools/nsc/ast/NodePrinters.scala32
-rw-r--r--src/compiler/scala/tools/nsc/ast/Positions.scala44
-rw-r--r--src/compiler/scala/tools/nsc/ast/Reifiers.scala761
-rw-r--r--src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala75
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala16
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala124
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala129
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala3
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Tokens.scala1
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala6
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Global.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interactive/RangePositions.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala12
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Power.scala1
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplVals.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/RichClass.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala9
-rw-r--r--src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala11
-rw-r--r--src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala1
-rw-r--r--src/compiler/scala/tools/nsc/scratchpad/Executor.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala149
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Positions.scala30
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/AddInterfaces.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/CleanUp.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala18
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala14
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala33
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala31
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala190
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala24
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala1361
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala14
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala31
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala20
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala180
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassPath.scala2
-rw-r--r--src/compiler/scala/tools/nsc/util/Position.scala126
52 files changed, 2070 insertions, 1669 deletions
diff --git a/src/compiler/scala/tools/nsc/ClassLoaders.scala b/src/compiler/scala/tools/nsc/ClassLoaders.scala
new file mode 100644
index 0000000000..4058ee9324
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ClassLoaders.scala
@@ -0,0 +1,64 @@
+package scala.tools.nsc
+
+import util.ScalaClassLoader
+
+trait ClassLoaders { self: Global =>
+
+ def staticClass(fullname: String) = {
+ if (self.forMSIL)
+ throw new UnsupportedOperationException("Scala reflection not available on this platform")
+
+ getClass(newTypeName(fullname))
+ }
+
+ def staticModule(fullname: String) = {
+ if (self.forMSIL)
+ throw new UnsupportedOperationException("Scala reflection not available on this platform")
+
+ getModule(newTermName(fullname))
+ }
+
+ private def getClass(fullname: Name): Symbol = {
+ var result = getModuleOrClass(fullname.toTypeName)
+ while (result.isAliasType) result = result.info.typeSymbol
+ result
+ }
+
+ private def getModule(fullname: Name): Symbol =
+ getModuleOrClass(fullname.toTermName)
+
+ private def getModuleOrClass(path: Name): Symbol =
+ getModuleOrClass(path, path.length)
+
+ private def getModuleOrClass(path: Name, len: Int): Symbol = {
+ val point = path lastPos('.', len - 1)
+ val owner =
+ if (point > 0) getModuleOrClass(path.toTermName, point)
+ else definitions.RootClass
+ val name = path subName (point + 1, len)
+ val sym = owner.info member name
+ val result = if (path.isTermName) sym.suchThat(_ hasFlag symtab.Flags.MODULE) else sym
+ if (result != NoSymbol) result
+ else {
+ if (settings.debug.value) { log(sym.info); log(sym.info.members) }//debug
+ if (owner.isRoot && isJavaClass(name.toString))
+ definitions.EmptyPackageClass.info decl name
+ else {
+ def info(msg: => String) = if (settings.verbose.value) println(msg)
+ info("*** missing: "+name+"/"+name.isTermName+"/"+owner+"/"+owner.hasPackageFlag+"/"+owner.info.decls.getClass)
+ MissingRequirementError.notFound((if (path.isTermName) "object " else "class ")+path)
+ }
+ }
+ }
+
+ private def isJavaClass(path: String): Boolean =
+ try {
+ val classpath = platform.classPath.asURLs
+ var classLoader = ScalaClassLoader.fromURLs(classpath)
+ Class.forName(path, true, classLoader)
+ true
+ } catch {
+ case (_: ClassNotFoundException) | (_: NoClassDefFoundError) | (_: IncompatibleClassChangeError) =>
+ false
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 5e0c24d304..b7d7f5d16f 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -12,7 +12,7 @@ import compat.Platform.currentTime
import scala.tools.util.{ Profiling, PathResolver }
import scala.collection.{ mutable, immutable }
import io.{ SourceReader, AbstractFile, Path }
-import reporters.{ Reporter, ConsoleReporter }
+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 }
@@ -32,24 +32,25 @@ import backend.jvm.GenJVM
import backend.opt.{ Inliners, InlineExceptionHandlers, ClosureElimination, DeadCodeElimination }
import backend.icode.analysis._
-class Global(var currentSettings: Settings, var reporter: Reporter) extends SymbolTable
- with CompilationUnits
- with Plugins
- with PhaseAssembly
- with Trees
- with Reifiers
- with TreePrinters
- with DocComments
- with MacroContext
- with symtab.Positions {
+class Global(var currentSettings: Settings, var reporter: NscReporter) extends SymbolTable
+ with ClassLoaders
+ with ToolBoxes
+ with CompilationUnits
+ with Plugins
+ with PhaseAssembly
+ with Trees
+ with FreeVars
+ with TreePrinters
+ with DocComments
+ with Positions {
override def settings = currentSettings
-
+
import definitions.{ findNamedMember, findMemberFromRoot }
// alternate constructors ------------------------------------------
- def this(reporter: Reporter) =
+ def this(reporter: NscReporter) =
this(new Settings(err => reporter.error(null, err)), reporter)
def this(settings: Settings) =
@@ -61,7 +62,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
type AbstractFileType = scala.tools.nsc.io.AbstractFile
def mkAttributedQualifier(tpe: Type, termSym: Symbol): Tree = gen.mkAttributedQualifier(tpe, termSym)
-
+
def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase
// platform specific elements
@@ -78,6 +79,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
// sub-components --------------------------------------------------
/** Generate ASTs */
+ type TreeGen = scala.tools.nsc.ast.TreeGen
+
object gen extends {
val global: Global.this.type = Global.this
} with TreeGen {
@@ -127,7 +130,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
/** Print tree in detailed form */
object nodePrinters extends {
val global: Global.this.type = Global.this
- } with NodePrinters with ReifyPrinters {
+ } with NodePrinters {
infolevel = InfoLevel.Verbose
}
@@ -137,7 +140,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
} with TreeBrowsers
val nodeToString = nodePrinters.nodeToString
- val reifiedNodeToString = nodePrinters.reifiedNodeToString
val treeBrowser = treeBrowsers.create()
// ------------ Hooks for interactive mode-------------------------
@@ -215,7 +217,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def logAfterEveryPhase[T](msg: String)(op: => T) {
log("Running operation '%s' after every phase.\n".format(msg) + describeAfterEveryPhase(op))
}
-
+
def shouldLogAtThisPhase = (
(settings.log.isSetByUser)
&& ((settings.log containsPhase globalPhase) || (settings.log containsPhase phase))
@@ -319,7 +321,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def showNames = List(showClass, showObject).flatten
def showPhase = isActive(settings.Yshow)
def showSymbols = settings.Yshowsyms.value
- def showTrees = settings.Xshowtrees.value
+ def showTrees = settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value
val showClass = optSetting[String](settings.Xshowcls) map (x => splitClassAndPhase(x, false))
val showObject = optSetting[String](settings.Xshowobj) map (x => splitClassAndPhase(x, true))
@@ -1108,7 +1110,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def phaseNamed(name: String): Phase =
findOrElse(firstPhase.iterator)(_.name == name)(NoPhase)
-
+
/** All phases as of 3/2012 here for handiness; the ones in
* active use uncommented.
*/
@@ -1581,7 +1583,7 @@ object Global {
* This allows the use of a custom Global subclass with the software which
* wraps Globals, such as scalac, fsc, and the repl.
*/
- def fromSettings(settings: Settings, reporter: Reporter): Global = {
+ def fromSettings(settings: Settings, reporter: NscReporter): Global = {
// !!! The classpath isn't known until the Global is created, which is too
// late, so we have to duplicate it here. Classpath is too tightly coupled,
// it is a construct external to the compiler and should be treated as such.
@@ -1589,7 +1591,7 @@ object Global {
val loader = ScalaClassLoader.fromURLs(new PathResolver(settings).result.asURLs, parentLoader)
val name = settings.globalClass.value
val clazz = Class.forName(name, true, loader)
- val cons = clazz.getConstructor(classOf[Settings], classOf[Reporter])
+ val cons = clazz.getConstructor(classOf[Settings], classOf[NscReporter])
cons.newInstance(settings, reporter).asInstanceOf[Global]
}
@@ -1597,7 +1599,7 @@ object Global {
/** A global instantiated this way honors -Yglobal-class setting, and
* falls back on calling the Global constructor directly.
*/
- def apply(settings: Settings, reporter: Reporter): Global = {
+ def apply(settings: Settings, reporter: NscReporter): Global = {
val g = (
if (settings.globalClass.isDefault) null
else try fromSettings(settings, reporter) catch { case x =>
diff --git a/src/compiler/scala/tools/nsc/MacroContext.scala b/src/compiler/scala/tools/nsc/MacroContext.scala
deleted file mode 100644
index 9ea1f87125..0000000000
--- a/src/compiler/scala/tools/nsc/MacroContext.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package scala.tools.nsc
-
-import symtab.Flags._
-
-trait MacroContext extends reflect.macro.Context { self: Global =>
-
- def captureVariable(vble: Symbol): Unit = vble setFlag CAPTURED
-
- def referenceCapturedVariable(id: Ident): Tree = ReferenceToBoxed(id)
-}
diff --git a/src/compiler/scala/tools/nsc/ReflectGlobal.scala b/src/compiler/scala/tools/nsc/ReflectGlobal.scala
index 3132a9987d..68a6a4d336 100644
--- a/src/compiler/scala/tools/nsc/ReflectGlobal.scala
+++ b/src/compiler/scala/tools/nsc/ReflectGlobal.scala
@@ -5,7 +5,7 @@ import reporters.Reporter
/** A version of Global that uses reflection to get class
* infos, instead of reading class or source files.
*/
-class ReflectGlobal(currentSettings: Settings, reporter: Reporter)
+class ReflectGlobal(currentSettings: Settings, reporter: Reporter, var classLoader: ClassLoader)
extends Global(currentSettings, reporter) with reflect.runtime.SymbolTable {
override def transformedType(sym: Symbol) =
@@ -13,4 +13,9 @@ class ReflectGlobal(currentSettings: Settings, reporter: Reporter)
uncurry.transformInfo(sym,
refChecks.transformInfo(sym, sym.info)))
+ override def staticClass(fullname: String) =
+ super[SymbolTable].staticClass(fullname)
+
+ override def staticModule(fullname: String) =
+ super[SymbolTable].staticModule(fullname)
}
diff --git a/src/compiler/scala/tools/nsc/ReflectMain.scala b/src/compiler/scala/tools/nsc/ReflectMain.scala
index 7167f5aa27..f9a18abc25 100644
--- a/src/compiler/scala/tools/nsc/ReflectMain.scala
+++ b/src/compiler/scala/tools/nsc/ReflectMain.scala
@@ -1,7 +1,16 @@
package scala.tools.nsc
+import util.ScalaClassLoader
+import tools.util.PathResolver
+import util.ClassPath.DefaultJavaContext
+
object ReflectMain extends Driver {
- override def newCompiler(): Global = new ReflectGlobal(settings, reporter)
+ private def reflectionClassloaderFromSettings(settings: Settings) = {
+ val classpath = new PathResolver(settings).result
+ ScalaClassLoader.fromURLs(classpath.asURLs, getClass.getClassLoader)
+ }
+
+ override def newCompiler(): Global = new ReflectGlobal(settings, reporter, reflectionClassloaderFromSettings(settings))
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/ToolBoxes.scala b/src/compiler/scala/tools/nsc/ToolBoxes.scala
new file mode 100644
index 0000000000..eb298833b8
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ToolBoxes.scala
@@ -0,0 +1,85 @@
+package scala.tools.nsc
+
+import util.ScalaClassLoader
+
+trait ToolBoxes { self: Global =>
+
+ import self.{Reporter => ApiReporter}
+
+ def mkToolBox(reporter: ApiReporter = mkSilentReporter(), options: String = "") = new ToolBox(reporter, options)
+
+ class ToolBox(val reporter: ApiReporter, val options: String) extends AbsToolBox {
+ def typeCheck(tree0: Tree, pt: Type = WildcardType, freeTypes: Map[FreeType, Type] = Map[FreeType, Type](), silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = {
+ val tree = substituteFreeTypes(tree0, freeTypes)
+ val currentTyper = typer
+ val wrapper1 = if (!withImplicitViewsDisabled) (currentTyper.context.withImplicitsEnabled[Tree] _) else (currentTyper.context.withImplicitsDisabled[Tree] _)
+ val wrapper2 = if (!withMacrosDisabled) (currentTyper.context.withMacrosEnabled[Tree] _) else (currentTyper.context.withMacrosDisabled[Tree] _)
+ def wrapper (tree: => Tree) = wrapper1(wrapper2(tree))
+ wrapper(currentTyper.silent(_.typed(tree, analyzer.EXPRmode, pt)) match {
+ case analyzer.SilentResultValue(result) =>
+ result
+ case error @ analyzer.SilentTypeError(_) =>
+ if (!silent) throw new ToolBoxError(this, "reflective typecheck has failed: %s".format(error.err.errMsg))
+ EmptyTree
+ })
+ }
+
+ def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false): Tree =
+ // todo. implement this
+ ???
+
+ def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, reportAmbiguous: Boolean = true): Tree =
+ // todo. implement this
+ ???
+
+ def resetAllAttrs[T <: Tree](tree: T): T =
+ self.resetAllAttrs(tree)
+
+ def resetLocalAttrs[T <: Tree](tree: T): T =
+ self.resetLocalAttrs(tree)
+
+ def runExpr(tree0: Tree, freeTypes: Map[FreeType, Type] = Map[FreeType, Type]()): Any = {
+ var tree = substituteFreeTypes(tree0, freeTypes)
+ // need to reset the tree, otherwise toolbox will refuse to work with it
+ tree = resetAllAttrs(tree0.duplicate)
+ val imported = importer.importTree(tree)
+ val toolBox = libraryClasspathMirror.mkToolBox(reporter.asInstanceOf[libraryClasspathMirror.Reporter], options)
+ try toolBox.runExpr(imported)
+ catch {
+ case ex: toolBox.ToolBoxError =>
+ throw new ToolBoxError(this, ex.message, ex.cause)
+ }
+ }
+
+ // [Eugene] how do I make this work without casts?
+ // private lazy val importer = libraryClasspathMirror.mkImporter(self)
+ private lazy val importer = libraryClasspathMirror.mkImporter(self).asInstanceOf[libraryClasspathMirror.Importer { val from: self.type }]
+
+ private lazy val libraryClasspathMirror = {
+ if (self.forMSIL)
+ throw new UnsupportedOperationException("Scala reflection not available on this platform")
+
+ val libraryClassLoader = {
+ val classpath = self.classPath.asURLs
+ var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
+
+ // [Eugene] a heuristic to detect REPL
+ if (self.settings.exposeEmptyPackage.value) {
+ import scala.tools.nsc.interpreter._
+ val virtualDirectory = self.settings.outputDirs.getSingleOutput.get
+ loader = new AbstractFileClassLoader(virtualDirectory, loader) {}
+ }
+
+ loader
+ }
+
+ new scala.reflect.runtime.Mirror(libraryClassLoader)
+ }
+
+ class ToolBoxError(val toolBox: ToolBox, val message: String, val cause: Throwable = null) extends Throwable(message, cause)
+
+ object ToolBoxError extends ToolBoxErrorExtractor {
+ def unapply(error: ToolBoxError): Option[(ToolBox, String)] = Some((error.toolBox, error.message))
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala
index 456e7eae9e..ff4e2f3fb5 100755
--- a/src/compiler/scala/tools/nsc/ast/DocComments.scala
+++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc
package ast
import symtab._
-import reporters.Reporter
+import reporters.{Reporter => NscReporter}
import util.{Position, NoPosition}
import util.DocStrings._
import scala.reflect.internal.Chars._
@@ -21,7 +21,7 @@ trait DocComments { self: Global =>
var cookedDocComments = Map[Symbol, String]()
- def reporter: Reporter
+ def reporter: NscReporter
/** The raw doc comment map */
val docComments = mutable.HashMap[Symbol, DocComment]()
diff --git a/src/compiler/scala/tools/nsc/ast/FreeVars.scala b/src/compiler/scala/tools/nsc/ast/FreeVars.scala
new file mode 100644
index 0000000000..1bf36e8bf2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ast/FreeVars.scala
@@ -0,0 +1,26 @@
+package scala.tools.nsc
+package ast
+
+trait FreeVars extends reflect.internal.FreeVars { self: Global =>
+
+ import self._
+ import definitions._
+ import treeInfo._
+
+ def logFreeVars(position: Position, reified: Tree): Unit = {
+ if (settings.logFreeTerms.value || settings.logFreeTypes.value) {
+ reified match {
+ case Reified(_, symbolTable, _) =>
+ // logging free vars only when they are untyped prevents avalanches of duplicate messages
+ symbolTable foreach {
+ case FreeTermDef(_, _, binding, origin) if settings.logFreeTerms.value && binding.tpe == null =>
+ reporter.echo(position, "free term: %s %s".format(showRaw(binding), origin))
+ case FreeTypeDef(_, _, binding, origin) if settings.logFreeTypes.value && binding.tpe == null =>
+ reporter.echo(position, "free type: %s %s".format(showRaw(binding), origin))
+ case _ =>
+ // do nothing
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala
index acbdcd501f..c79ca1206e 100644
--- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala
+++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala
@@ -27,24 +27,24 @@ abstract class NodePrinters {
def nodeToString: Tree => String =
if (sys.props contains "scala.colors") nodeToColorizedString
else nodeToRegularString
-
+
object nodeToRegularString extends DefaultPrintAST with (Tree => String) {
def apply(tree: Tree) = stringify(tree)
}
-
+
object nodeToColorizedString extends ColorPrintAST with (Tree => String) {
def apply(tree: Tree) = stringify(tree)
}
trait ColorPrintAST extends DefaultPrintAST {
import scala.tools.util.color._
-
+
def keywordColor = Cyan
def typeColor = Yellow
def termColor = Blue
def flagColor = Red
def literalColor = Green
-
+
override def showFlags(tree: MemberDef) =
super.showFlags(tree) in flagColor.bright
@@ -81,7 +81,7 @@ abstract class NodePrinters {
if (tpe == null || tpe == NoType) ""
else "tree.tpe=" + tpe
}
-
+
def showAttributes(tree: Tree): String = {
if (infolevel == InfoLevel.Quiet) ""
else {
@@ -90,7 +90,7 @@ abstract class NodePrinters {
}
}
}
-
+
trait PrintAST {
private val buf = new StringBuilder
private var level = 0
@@ -101,7 +101,7 @@ abstract class NodePrinters {
def showLiteral(lit: Literal): String
def showTypeTree(tt: TypeTree): String
def showAttributes(tree: Tree): String // symbol and type
-
+
def showRefTreeName(tree: Tree): String = tree match {
case SelectFromTypeTree(qual, name) => showRefTreeName(qual) + "#" + showName(name)
case Select(qual, name) => showRefTreeName(qual) + "." + showName(name)
@@ -122,8 +122,14 @@ abstract class NodePrinters {
def stringify(tree: Tree): String = {
buf.clear()
- level = 0
- traverse(tree)
+ if (settings.XshowtreesStringified.value) buf.append(tree.toString + EOL)
+ if (settings.XshowtreesCompact.value) {
+ // todo. colors for compact representation
+ buf.append(showRaw(tree))
+ } else {
+ level = 0
+ traverse(tree)
+ }
buf.toString
}
def traverseAny(x: Any) {
@@ -134,7 +140,7 @@ abstract class NodePrinters {
}
}
def println(s: String) = printLine(s, "")
-
+
def printLine(value: String, comment: String) {
buf append " " * level
buf append value
@@ -183,7 +189,7 @@ abstract class NodePrinters {
traverseList("Nil", "argument")(args)
}
}
-
+
def printMultiline(tree: Tree)(body: => Unit) {
printMultiline(tree.printingPrefix, showAttributes(tree))(body)
}
@@ -299,7 +305,7 @@ abstract class NodePrinters {
}
case Template(parents, self, body) =>
printMultiline(tree) {
- val ps0 = parents map { p =>
+ val ps0 = parents map { p =>
if (p.tpe eq null) p match {
case x: RefTree => showRefTree(x)
case x => "" + x
@@ -339,7 +345,7 @@ abstract class NodePrinters {
traverseList("[]", "type parameter")(tparams)
traverse(rhs)
}
-
+
case PackageDef(pid, stats) =>
printMultiline("PackageDef", "")(pid :: stats foreach traverse)
diff --git a/src/compiler/scala/tools/nsc/ast/Positions.scala b/src/compiler/scala/tools/nsc/ast/Positions.scala
new file mode 100644
index 0000000000..83a67cfbe3
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ast/Positions.scala
@@ -0,0 +1,44 @@
+package scala.tools.nsc
+package ast
+
+import scala.tools.nsc.util.{ SourceFile, Position, OffsetPosition, NoPosition }
+
+trait Positions extends scala.reflect.internal.Positions {
+ self: Global =>
+
+ def rangePos(source: SourceFile, start: Int, point: Int, end: Int) =
+ new OffsetPosition(source, point)
+
+ def validatePositions(tree: Tree) {}
+
+ // [Eugene] disabling this for now. imo it doesn't justify pollution of the public API
+ // override def _checkSetAnnotation(tree: Tree, annot: TreeAnnotation): Unit = {
+ // if (tree.pos != NoPosition && tree.pos != annot.pos) debugwarn("Overwriting annotation "+ tree.annotation +" of tree "+ tree +" with annotation "+ annot)
+ // // if ((tree.annotation.isInstanceOf[scala.tools.nsc.util.Position] || !annot.isInstanceOf[scala.tools.nsc.util.Position]) && tree.isInstanceOf[Block])
+ // // println("Updating block from "+ tree.annotation +" to "+ annot)
+ // }
+
+ class ValidatingPosAssigner extends PosAssigner {
+ var pos: Position = _
+ override def traverse(t: Tree) {
+ if (t eq EmptyTree) ()
+ else if (t.pos == NoPosition) super.traverse(t setPos pos)
+ else if (globalPhase.id <= currentRun.picklerPhase.id) {
+ // When we prune due to encountering a position, traverse the
+ // pruned children so we can warn about those lacking positions.
+ t.children foreach { c =>
+ if ((c eq EmptyTree) || (c eq emptyValDef)) ()
+ else if (c.pos == NoPosition) {
+ reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase)
+ inform("parent: " + treeSymStatus(t))
+ inform(" child: " + treeSymStatus(c) + "\n")
+ }
+ }
+ }
+ }
+ }
+
+ override protected[this] lazy val posAssigner: PosAssigner =
+ if (settings.Yrangepos.value && settings.debug.value || settings.Yposdebug.value) new ValidatingPosAssigner
+ else new DefaultPosAssigner
+}
diff --git a/src/compiler/scala/tools/nsc/ast/Reifiers.scala b/src/compiler/scala/tools/nsc/ast/Reifiers.scala
deleted file mode 100644
index 04468a096d..0000000000
--- a/src/compiler/scala/tools/nsc/ast/Reifiers.scala
+++ /dev/null
@@ -1,761 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2011 LAMP/EPFL
- * @author Gilles Dubochet
- */
-
-package scala.tools.nsc
-package ast
-
-import symtab._
-import Flags._
-import scala.reflect.api.Modifier._
-import scala.collection.{ mutable, immutable }
-import scala.collection.mutable.ListBuffer
-import scala.tools.nsc.util.FreshNameCreator
-import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple }
-
-/** Given a tree or type, generate a tree that when executed at runtime produces the original tree or type.
- * See more info in the comments to `reify' in scala.reflect.macro.Context.
- *
- * @author Martin Odersky
- * @version 2.10
- */
-trait Reifiers { self: Global =>
-
- def reify(tree: Tree): Tree = {
- class Reifier {
- import definitions._
- import Reifier._
-
- final val scalaPrefix = "scala."
- final val localPrefix = "$local"
- final val memoizerName = "$memo"
-
- val reifyDebug = settings.Yreifydebug.value
-
- private val reifiableSyms = mutable.ArrayBuffer[Symbol]() // the symbols that are reified with the tree
- private val symIndex = mutable.HashMap[Symbol, Int]() // the index of a reifiable symbol in `reifiableSyms`
- private var boundSyms = Set[Symbol]() // set of all symbols that are bound in tree to be reified
-
- private def definedInLiftedCode(tpe: Type) =
- tpe exists (tp => boundSyms contains tp.typeSymbol)
-
- private def definedInLiftedCode(sym: Symbol) =
- boundSyms contains sym
-
- /**
- * Generate tree of the form
- *
- * { val $mr = scala.reflect.runtime.Mirror
- * $local1 = new TypeSymbol(owner1, NoPosition, name1)
- * ...
- * $localN = new TermSymbol(ownerN, NoPositiion, nameN)
- * $local1.setInfo(tpe1)
- * ...
- * $localN.setInfo(tpeN)
- * $localN.setAnnotations(annotsN)
- * rtree
- * }
- *
- * where
- *
- * - `$localI` are free type symbols in the environment, as well as local symbols
- * of refinement types.
- * - `tpeI` are the info's of `symI`
- * - `rtree` is code that generates `data` at runtime, maintaining all attributes.
- * - `data` is typically a tree or a type.
- */
- def reifyTopLevel(data: Any): Tree = {
- val rtree = reify(data)
- Block(mirrorAlias :: reifySymbolTableSetup, rtree)
- }
-
- private def isLocatable(sym: Symbol) =
- sym.isPackageClass || sym.owner.isClass || sym.isTypeParameter && sym.paramPos >= 0
-
- private def registerReifiableSymbol(sym: Symbol): Unit =
- if (!(symIndex contains sym)) {
- sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol
- symIndex(sym) = reifiableSyms.length
- reifiableSyms += sym
- }
-
- // helper methods
-
- private def localName(sym: Symbol): TermName =
- newTermName(localPrefix + symIndex(sym))
-
- private def call(fname: String, args: Tree*): Tree =
- Apply(termPath(fname), args.toList)
-
- private def mirrorSelect(name: String): Tree =
- termPath(nme.MIRROR_PREFIX + name)
-
- private def mirrorCall(name: TermName, args: Tree*): Tree =
- call("" + (nme.MIRROR_PREFIX append name), args: _*)
-
- private def mirrorCall(name: String, args: Tree*): Tree =
- call(nme.MIRROR_PREFIX + name, args: _*)
-
- private def mirrorFactoryCall(value: Product, args: Tree*): Tree =
- mirrorFactoryCall(value.productPrefix, args: _*)
-
- private def mirrorFactoryCall(prefix: String, args: Tree*): Tree =
- mirrorCall(prefix, args: _*)
-
- private def scalaFactoryCall(name: String, args: Tree*): Tree =
- call(scalaPrefix + name + ".apply", args: _*)
-
- private def mkList(args: List[Tree]): Tree =
- scalaFactoryCall("collection.immutable.List", args: _*)
-
- private def reifyModifiers(m: Modifiers) =
- mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations))
-
- private def reifyAggregate(name: String, args: Any*) =
- scalaFactoryCall(name, (args map reify).toList: _*)
-
- /**
- * Reify a list
- */
- private def reifyList(xs: List[Any]): Tree =
- mkList(xs map reify)
-
- /**
- * Reify an array
- */
- private def reifyArray(xs: Array[_]): Tree =
- // @xeno.by: doesn't work for Array(LiteralAnnotArg(...))
- // because we cannot generate manifests for path-dependent types
- scalaFactoryCall(nme.Array, xs map reify: _*)
-
- /** Reify a name */
- private def reifyName(name: Name) =
- mirrorCall(if (name.isTypeName) "newTypeName" else "newTermName", Literal(Constant(name.toString)))
-
- private def isFree(sym: Symbol) =
- !(symIndex contains sym)
-
- /**
- * Reify a reference to a symbol
- */
- private def reifySymRef(sym: Symbol): Tree = {
- symIndex get sym match {
- case Some(idx) =>
- Ident(localName(sym))
- case None =>
- if (sym == NoSymbol)
- mirrorSelect("NoSymbol")
- else if (sym == RootPackage)
- mirrorSelect("definitions.RootPackage")
- else if (sym == RootClass)
- mirrorSelect("definitions.RootClass")
- else if (sym == EmptyPackage)
- mirrorSelect("definitions.EmptyPackage")
- else if (sym.isModuleClass)
- Select(reifySymRef(sym.sourceModule), "moduleClass")
- else if (sym.isStatic && sym.isClass)
- mirrorCall("staticClass", reify(sym.fullName))
- else if (sym.isStatic && sym.isModule)
- mirrorCall("staticModule", reify(sym.fullName))
- else if (isLocatable(sym))
- if (sym.isTypeParameter)
- mirrorCall("selectParam", reify(sym.owner), reify(sym.paramPos))
- else {
- if (reifyDebug) println("locatable: " + sym + " " + sym.isPackageClass + " " + sym.owner + " " + sym.isTypeParameter)
- val rowner = reify(sym.owner)
- val rname = reify(sym.name.toString)
- if (sym.isType)
- mirrorCall("selectType", rowner, rname)
- else if (sym.isMethod && sym.owner.isClass && sym.owner.info.decl(sym.name).isOverloaded) {
- val index = sym.owner.info.decl(sym.name).alternatives indexOf sym
- assert(index >= 0, sym)
- mirrorCall("selectOverloadedMethod", rowner, rname, reify(index))
- } else
- mirrorCall("selectTerm", rowner, rname)
- }
- else {
- if (sym.isTerm) {
- if (reifyDebug) println("Free: " + sym)
- val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false)
- def markIfCaptured(arg: Ident): Tree =
- if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg
- mirrorCall("newFreeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym)))
- } else {
- if (reifyDebug) println("Late local: " + sym)
- registerReifiableSymbol(sym)
- reifySymRef(sym)
- }
- }
- }
- }
-
- /**
- * reify the creation of a symbol
- */
- private def reifySymbolDef(sym: Symbol): Tree = {
- if (reifyDebug) println("reify sym def " + sym)
-
- ValDef(NoMods, localName(sym), TypeTree(),
- Apply(
- Select(reify(sym.owner), "newNestedSymbol"),
- List(reify(sym.name), reify(sym.pos), Literal(Constant(sym.flags)), Literal(Constant(sym.isClass)))
- )
- )
- }
-
- /**
- * Generate code to add type and annotation info to a reified symbol
- */
- private def fillInSymbol(sym: Symbol): Tree = {
- val rset = Apply(Select(reifySymRef(sym), nme.setTypeSignature), List(reifyType(sym.info)))
- if (sym.annotations.isEmpty) rset
- else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations)))
- }
-
- /** Reify a scope */
- private def reifyScope(scope: Scope): Tree = {
- scope foreach registerReifiableSymbol
- mirrorCall(nme.newScopeWith, scope.toList map reifySymRef: _*)
- }
-
- /** Reify a list of symbols that need to be created */
- private def reifySymbols(syms: List[Symbol]): Tree = {
- syms foreach registerReifiableSymbol
- mkList(syms map reifySymRef)
- }
-
- /** Reify a type that defines some symbols */
- private def reifyTypeBinder(value: Product, bound: List[Symbol], underlying: Type): Tree =
- mirrorFactoryCall(value, reifySymbols(bound), reify(underlying))
-
- /** Reify a type */
- private def reifyType(tpe0: Type): Tree = {
- val tpe = tpe0.normalize
-
- if (tpe.isErroneous)
- CannotReifyErroneousType(tpe)
- if (definedInLiftedCode(tpe))
- CannotReifyTypeInvolvingBoundType(tpe)
-
- val tsym = tpe.typeSymbol
- if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic)
- Select(reifySymRef(tpe.typeSymbol), nme.asTypeConstructor)
- else tpe match {
- case t @ NoType =>
- reifyMirrorObject(t)
- case t @ NoPrefix =>
- reifyMirrorObject(t)
- case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic =>
- mirrorCall(nme.thisModuleType, reify(clazz.fullName))
- case t @ RefinedType(parents, decls) =>
- registerReifiableSymbol(tpe.typeSymbol)
- mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
- case t @ ClassInfoType(parents, decls, clazz) =>
- registerReifiableSymbol(clazz)
- mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
- case t @ ExistentialType(tparams, underlying) =>
- reifyTypeBinder(t, tparams, underlying)
- case t @ PolyType(tparams, underlying) =>
- reifyTypeBinder(t, tparams, underlying)
- case t @ MethodType(params, restpe) =>
- reifyTypeBinder(t, params, restpe)
- case t @ AnnotatedType(anns, underlying, selfsym) =>
- val saved1 = reifySymbols
- val saved2 = reifyTypes
-
- try {
- // one more quirk of reifying annotations
- //
- // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs
- // that's because a lot of logic expects post-typer trees to have non-null tpes
- //
- // Q: reified trees are pre-typer, so there's shouldn't be a problem.
- // reflective typechecker will fill in missing symbols and types, right?
- // A: actually, no. annotation ASTs live inside AnnotatedTypes,
- // and insides of the types is the place where typechecker doesn't look.
- reifySymbols = true
- reifyTypes = true
- if (reifyDebug) println("reify AnnotatedType: " + tpe)
- reifyProductUnsafe(tpe)
- } finally {
- reifySymbols = saved1
- reifyTypes = saved2
- }
- case _ =>
- reifyProductUnsafe(tpe)
- }
- }
-
- var reifySymbols = false
- var reifyTypes = false
-
- /** Preprocess a tree before reification */
- private def trimTree(tree: Tree): Tree = {
- def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]) = {
- var stats1 = stats filterNot (stat => stat.isDef && {
- if (stat.symbol.isCaseAccessorMethod && reifyDebug) println("discarding case accessor method: " + stat)
- stat.symbol.isCaseAccessorMethod
- })
- stats1 = stats1 filterNot (memberDef => memberDef.isDef && {
- val isSynthetic = memberDef.symbol.isSynthetic
- // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
- // that's why I replace the check with an assumption that all synthetic members are, in fact, generated of case classes
-// val isCaseMember = deff.symbol.isCaseClass || deff.symbol.companionClass.isCaseClass
- val isCaseMember = true
- if (isSynthetic && isCaseMember && reifyDebug) println("discarding case class synthetic def: " + memberDef)
- isSynthetic && isCaseMember
- })
- stats1 = stats1 map {
- case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isCaseAccessor =>
- if (reifyDebug) println("resetting visibility of case accessor field: " + valdef)
- val Modifiers(flags, privateWithin, annotations) = mods
- val flags1 = flags & ~Flags.LOCAL & ~Flags.PRIVATE
- val mods1 = Modifiers(flags1, privateWithin, annotations)
- ValDef(mods1, name, tpt, rhs).copyAttrs(valdef)
- case stat =>
- stat
- }
- stats1
- }
-
- def trimSyntheticCaseClassCompanions(stats: List[Tree]) =
- stats diff (stats collect { case moddef: ModuleDef => moddef } filter (moddef => {
- val isSynthetic = moddef.symbol.isSynthetic
- // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
- // that's why I replace the check with an assumption that all synthetic modules are, in fact, companions of case classes
-// val isCaseCompanion = moddef.symbol.companionClass.isCaseClass
- val isCaseCompanion = true
- // @xeno.by: we also have to do this ugly hack for the very same reason described above
- // normally this sort of stuff is performed in reifyTree, which binds related symbols, however, local companions will be out of its reach
- if (reifyDebug) println("boundSym: "+ moddef.symbol)
- boundSyms += moddef.symbol
- if (isSynthetic && isCaseCompanion && reifyDebug) println("discarding synthetic case class companion: " + moddef)
- isSynthetic && isCaseCompanion
- }))
-
- tree match {
- case tree if tree.isErroneous =>
- tree
- case ta @ TypeApply(hk, ts) =>
- def isErased(tt: TypeTree) = tt.tpe != null && definedInLiftedCode(tt.tpe) && tt.original == null
- val discard = ts collect { case tt: TypeTree => tt } exists isErased
- if (reifyDebug && discard) println("discarding TypeApply: " + tree)
- if (discard) hk else ta
- case classDef @ ClassDef(mods, name, params, impl) =>
- val Template(parents, self, body) = impl
- val body1 = trimSyntheticCaseClassMembers(classDef, body)
- var impl1 = Template(parents, self, body1).copyAttrs(impl)
- ClassDef(mods, name, params, impl1).copyAttrs(classDef)
- case moduledef @ ModuleDef(mods, name, impl) =>
- val Template(parents, self, body) = impl
- val body1 = trimSyntheticCaseClassMembers(moduledef, body)
- var impl1 = Template(parents, self, body1).copyAttrs(impl)
- ModuleDef(mods, name, impl1).copyAttrs(moduledef)
- case template @ Template(parents, self, body) =>
- val body1 = trimSyntheticCaseClassCompanions(body)
- Template(parents, self, body1).copyAttrs(template)
- case block @ Block(stats, expr) =>
- val stats1 = trimSyntheticCaseClassCompanions(stats)
- Block(stats1, expr).copyAttrs(block)
- case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isLazy =>
- if (reifyDebug) println("dropping $lzy in lazy val's name: " + tree)
- val name1 = if (name endsWith nme.LAZY_LOCAL) name dropRight nme.LAZY_LOCAL.length else name
- ValDef(mods, name1, tpt, rhs).copyAttrs(valdef)
- case unapply @ UnApply(fun, args) =>
- def extractExtractor(tree: Tree): Tree = {
- val Apply(fun, args) = tree
- args match {
- case List(Ident(special)) if special == nme.SELECTOR_DUMMY =>
- val Select(extractor, flavor) = fun
- assert(flavor == nme.unapply || flavor == nme.unapplySeq)
- extractor
- case _ =>
- extractExtractor(fun)
- }
- }
-
- if (reifyDebug) println("unapplying unapply: " + tree)
- val fun1 = extractExtractor(fun)
- Apply(fun1, args).copyAttrs(unapply)
- case _ =>
- tree
- }
- }
-
- /** Reify a tree */
- private def reifyTree(tree0: Tree): Tree = {
- val tree = trimTree(tree0)
-
- var rtree = tree match {
- case tree if tree.isErroneous =>
- CannotReifyErroneousTree(tree)
- case self.EmptyTree =>
- reifyMirrorObject(EmptyTree)
- case self.emptyValDef =>
- mirrorSelect(nme.emptyValDef)
- case This(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
- reifyFree(tree)
- case Ident(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
- if (tree.symbol.isVariable && tree.symbol.owner.isTerm) {
- if (reifyDebug) println("captured variable: " + tree.symbol)
- captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reifyTree here.
- mirrorCall("Select", reifyFree(tree), reifyName(nme.elem))
- } else reifyFree(tree)
- case tt: TypeTree if (tt.tpe != null) =>
- reifyTypeTree(tt)
- case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) =>
- CannotReifyClassOfBoundType(tree, tpe)
- case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym =>
- CannotReifyClassOfBoundEnum(tree, constant.tpe)
- case tree if tree.isDef =>
- if (reifyDebug) println("boundSym: %s of type %s".format(tree.symbol, (tree.productIterator.toList collect { case tt: TypeTree => tt } headOption).getOrElse(TypeTree(tree.tpe))))
- boundSyms += tree.symbol
-
- bindRelatedSymbol(tree.symbol.sourceModule, "sourceModule")
- bindRelatedSymbol(tree.symbol.moduleClass, "moduleClass")
- bindRelatedSymbol(tree.symbol.companionClass, "companionClass")
- bindRelatedSymbol(tree.symbol.companionModule, "companionModule")
- Some(tree.symbol) collect { case termSymbol: TermSymbol => bindRelatedSymbol(termSymbol.referenced, "referenced") }
- def bindRelatedSymbol(related: Symbol, name: String): Unit =
- if (related != null && related != NoSymbol) {
- if (reifyDebug) println("boundSym (" + name + "): " + related)
- boundSyms += related
- }
-
- val prefix = tree.productPrefix
- val elements = (tree.productIterator map {
- // annotations exist in two flavors:
- // 1) pre-typer ones that populate: a) Modifiers, b) Annotated nodes (irrelevant in this context)
- // 2) post-typer ones that dwell inside: a) sym.annotations, b) AnnotatedTypes (irrelevant in this context)
- //
- // here we process Modifiers that are involved in deftrees
- // AnnotatedTypes get reified elsewhere (currently, in ``reifyTypeTree'')
- case Modifiers(flags, privateWithin, annotations) =>
- assert(annotations.isEmpty) // should've been eliminated by the typer
- val postTyper = tree.symbol.annotations filter (_.original != EmptyTree)
- if (reifyDebug && !postTyper.isEmpty) println("reify symbol annotations for %s: %s".format(tree.symbol, tree.symbol.annotations))
- val preTyper = postTyper map toPreTyperAnnotation
- Modifiers(flags, privateWithin, preTyper)
- case x =>
- x
- }).toList
- reifyProduct(prefix, elements)
- case _ =>
- reifyProduct(tree)
- }
-
- // usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation
- // however, reification of AnnotatedTypes is special. see ``reifyType'' to find out why.
- if (reifySymbols && tree.hasSymbol) {
- if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree))
- rtree = Apply(Select(rtree, nme.setSymbol), List(reifySymRef(tree.symbol)))
- }
- if (reifyTypes && tree.tpe != null) {
- if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree))
- rtree = Apply(Select(rtree, nme.setType), List(reifyType(tree.tpe)))
- }
-
- rtree
- }
-
- /** Reify pre-typer representation of a type.
- *
- * NB: This is the trickiest part of reification!
- *
- * In most cases, we're perfectly fine to reify a Type itself (see ``reifyType'').
- * However if the type involves a symbol declared inside the quasiquote (i.e. registered in ``boundSyms''),
- * then we cannot reify it, or otherwise subsequent reflective compilation will fail.
- *
- * Why will it fail? Because reified deftrees (e.g. ClassDef(...)) will generate fresh symbols during that compilation,
- * so naively reified symbols will become out of sync, which brings really funny compilation errors and/or crashes, e.g.:
- * https://issues.scala-lang.org/browse/SI-5230
- *
- * To deal with this unpleasant fact, we need to fall back from types to equivalent trees (after all, parser trees don't contain any types, just trees, so it should be possible).
- * Luckily, these original trees get preserved for us in the ``original'' field when Trees get transformed into TypeTrees.
- * And if an original of a type tree is empty, we can safely assume that this type is non-essential (e.g. was inferred/generated by the compiler).
- * In that case the type can be omitted (e.g. reified as an empty TypeTree), since it will be inferred again later on.
- *
- * An important property of the original is that it isn't just a pre-typer tree.
- * It's actually kind of a post-typer tree with symbols assigned to its Idents (e.g. Ident("List") will contain a symbol that points to immutable.this.List).
- * This is very important, since subsequent reflective compilation won't have to resolve these symbols.
- * In general case, such resolution cannot be performed, since reification doesn't preserve lexical context,
- * which means that reflective compilation won't be aware of, say, imports that were provided when the reifee has been compiled.
- *
- * This workaround worked surprisingly well and allowed me to fix several important reification bugs, until the abstraction has leaked.
- * Suddenly I found out that in certain contexts original trees do not contain symbols, but are just parser trees.
- * To the moment I know only one such situation: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless.
- * This is laboriously worked around in the code below. I hope this will be the only workaround in this department.
- */
- private def reifyTypeTree(tt: TypeTree): Tree = {
- if (definedInLiftedCode(tt.tpe)) {
- if (reifyDebug) println("reifyTypeTree, defined in lifted code: " + tt.tpe)
- if (tt.original != null) {
- val annotations = tt.tpe filter { _.isInstanceOf[AnnotatedType] } collect { case atp: AnnotatedType => atp.annotations } flatten
- val annmap = annotations map { ann => (ann.original, ann) } toMap
-
- // annotations exist in two flavors:
- // 1) pre-typer ones that populate: a) Modifiers (irrelevant in this context), b) Annotated nodes
- // 2) post-typer ones that dwell inside: a) sym.annotations (irrelevant in this context), b) AnnotatedTypes
- //
- // here we process AnnotatedTypes, since only they can be involved in TypeTrees
- // Modifiers get reified elsewhere (currently, in the "isDef" case of ``reifyTree'')
- //
- // the problem with annotations is that their originals don't preserve any symbols at all
- // read the comment to this method to find out why it's bad
- // that's why we transplant typechecked, i.e. symful, annotations onto original trees
- class AnnotationFixup extends self.Transformer {
- override def transform(tree: Tree) = tree match {
- case Annotated(ann0, args) =>
- assert(annmap contains ann0)
- val ann1 = annmap(ann0)
- val ann = toPreTyperAnnotation(ann1)
- Annotated(ann, transform(args))
- case _ =>
- tree
- }
- }
-
- if (reifyDebug) println("verdict: essential, reify as original")
- val patchedOriginal = new AnnotationFixup().transform(tt.original)
- reifyTree(patchedOriginal)
- } else {
- // type is deemed to be non-essential
- // erase it and hope that subsequent reflective compilation will be able to recreate it again
- if (reifyDebug) println("verdict: non-essential, discard")
- mirrorCall("TypeTree")
- }
- } else {
- var rtt = mirrorCall(nme.TypeTree, reifyType(tt.tpe))
- // @xeno.by: temporarily disabling reification of originals
- // subsequent reflective compilation will try to typecheck them
- // and this means that the reifier has to do additional efforts to ensure that this will succeed
- // additional efforts + no clear benefit = will be implemented later
-// if (tt.original != null) {
-// val setOriginal = Select(rtt, newTermName("setOriginal"))
-// val reifiedOriginal = reify(tt.original)
-// rtt = Apply(setOriginal, List(reifiedOriginal))
-// }
- rtt
- }
- }
-
- /** Reify post-typer representation of an annotation */
- private def reifyAnnotation(ann: AnnotationInfo): Tree =
- // @xeno.by: if you reify originals, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important
- mirrorFactoryCall("AnnotationInfo", reifyType(ann.atp), reifyList(ann.args), reify(ann.assocs))
-
- /** Reify pre-typer representation of an annotation.
- * The trick here is to retain the symbols that have been populated during typechecking of the annotation.
- * If we do not do that, subsequent reflective compilation will fail.
- */
- private def toPreTyperAnnotation(ann: AnnotationInfo): Tree = {
- if (definedInLiftedCode(ann.atp)) {
- // todo. deconstruct reifiable tree from ann.original and ann.args+ann.assocs
- //
- // keep in mind that we can't simply use ann.original, because its args are symless
- // which means that any imported symbol (e.g. List) will crash subsequent reflective compilation
- // hint: if I had enough time, I'd try to extract reifiable annotation type from ann.original
- // and to apply its constructor to ann.args (that are symful, i.e. suitable for reification)
- //
- // also, if we pursue the route of reifying annotations defined in lifted code
- // we should think about how to provide types for all nodes of the return value
- // this will be necessary for reifying AnnotatedTypes, since ASTs inside ATs must all have non-null tpes
- // an alternative would be downgrading ATs to Annotated nodes, but this needs careful thinking
- // for now I just leave this as an implementation restriction
- CannotReifyAnnotationInvolvingBoundType(ann)
- } else {
- val args = if (ann.assocs.isEmpty) {
- ann.args
- } else {
- def toScalaAnnotation(jann: ClassfileAnnotArg): Tree = jann match {
- case LiteralAnnotArg(const) =>
- Literal(const)
- case ArrayAnnotArg(arr) =>
- Apply(Ident(definitions.ArrayModule), arr.toList map toScalaAnnotation)
- case NestedAnnotArg(ann) =>
- toPreTyperAnnotation(ann)
- }
-
- ann.assocs map { case (nme, arg) => AssignOrNamedArg(Ident(nme), toScalaAnnotation(arg)) }
- }
-
- New(ann.atp, args: _*)
- }
- }
-
- /**
- * Reify a free reference. The result will be either a mirror reference
- * to a global value, or else a mirror Literal.
- */
- private def reifyFree(tree: Tree): Tree = tree match {
- case This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass =>
- val sym = tree.symbol
- if (reifyDebug) println("This for %s, reified as freeVar".format(sym))
- if (reifyDebug) println("Free: " + sym)
- val freeVar = mirrorCall("newFreeVar", reify(sym.name.toString), reify(sym.tpe), This(sym))
- mirrorCall(nme.Ident, freeVar)
- case This(_) =>
- if (reifyDebug) println("This for %s, reified as This".format(tree.symbol))
- mirrorCall(nme.This, reifySymRef(tree.symbol))
- case _ =>
- mirrorCall(nme.Ident, reifySymRef(tree.symbol))
- }
-
- // todo: consider whether we should also reify positions
- private def reifyPosition(pos: Position): Tree =
- reifyMirrorObject(NoPosition)
-
- // !!! we must eliminate these casts.
- private def reifyProductUnsafe(x: Any): Tree =
- if (x.isInstanceOf[Product]) reifyProduct(x.asInstanceOf[Product])
- else throw new Exception("%s of type %s cannot be cast to Product".format(x, x.getClass))
- private def reifyProduct(x: Product): Tree =
- reifyProduct(x.productPrefix, x.productIterator.toList)
- private def reifyProduct(prefix: String, elements: List[Any]): Tree = {
- // @xeno.by: reflection would be more robust, but, hey, this is a hot path
- if (prefix.startsWith("Tuple")) reifyAggregate(prefix, elements: _*)
- else mirrorCall(prefix, (elements map reify): _*)
- }
-
- /**
- * Reify a case object defined in Mirror
- */
- private def reifyMirrorObject(name: String): Tree = mirrorSelect(name)
- private def reifyMirrorObject(x: Product): Tree = reifyMirrorObject(x.productPrefix)
-
- private def isReifiableConstant(value: Any) = value match {
- case null => true // seems pretty reifable to me?
- case _: String => true
- case _ => isAnyVal(value)
- }
-
- /** Reify an arbitary value */
- private def reify(value: Any): Tree = value match {
- case tree: Tree => reifyTree(tree)
- case sym: Symbol => reifySymRef(sym)
- case tpe: Type => reifyType(tpe)
- case xs: List[_] => reifyList(xs)
- case xs: Array[_] => reifyArray(xs)
- case scope: Scope => reifyScope(scope)
- case x: Name => reifyName(x)
- case x: Position => reifyPosition(x)
- case x: Modifiers => reifyModifiers(x)
- case x: AnnotationInfo => reifyAnnotation(x)
- case _ =>
- if (isReifiableConstant(value)) Literal(Constant(value))
- else reifyProductUnsafe(value)
- }
-
- /**
- * An (unreified) path that refers to definition with given fully qualified name
- * @param mkName Creator for last portion of name (either TermName or TypeName)
- */
- private def path(fullname: String, mkName: String => Name): Tree = {
- val parts = fullname split "\\."
- val prefixParts = parts.init
- val lastName = mkName(parts.last)
- if (prefixParts.isEmpty) Ident(lastName)
- else {
- val prefixTree = ((Ident(prefixParts.head): Tree) /: prefixParts.tail)(Select(_, _))
- Select(prefixTree, lastName)
- }
- }
-
- /** An (unreified) path that refers to term definition with given fully qualified name */
- private def termPath(fullname: String): Tree = path(fullname, newTermName)
-
- /** An (unreified) path that refers to type definition with given fully qualified name */
- private def typePath(fullname: String): Tree = path(fullname, newTypeName)
-
- private def mirrorAlias =
- ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(termPath(fullnme.MirrorPackage)), termPath(fullnme.MirrorPackage))
-
- /**
- * Generate code that generates a symbol table of all symbols registered in `reifiableSyms`
- */
- private def reifySymbolTableSetup: List[Tree] = {
- val symDefs, fillIns = new mutable.ArrayBuffer[Tree]
- var i = 0
- while (i < reifiableSyms.length) {
- // fillInSymbol might create new reifiableSyms, that's why this is done iteratively
- symDefs += reifySymbolDef(reifiableSyms(i))
- fillIns += fillInSymbol(reifiableSyms(i))
- i += 1
- }
-
- symDefs.toList ++ fillIns.toList
- }
- } // end of Reifier
-
- object Reifier {
- def CannotReifyPreTyperTree(tree: Tree) = {
- val msg = "pre-typer trees are not supported, consider typechecking the tree before passing it to the reifier"
- throw new ReifierError(tree.pos, msg)
- }
-
- def CannotReifyErroneousTree(tree: Tree) = {
- val msg = "erroneous trees are not supported, make sure that your tree typechecks successfully before passing it to the reifier"
- throw new ReifierError(tree.pos, msg)
- }
-
- def CannotReifyErroneousType(tpe: Type) = {
- val msg = "erroneous types are not supported, make sure that your tree typechecks successfully before passing it to the reifier"
- throw new ReifierError(NoPosition, msg)
- }
-
- def CannotReifyClassOfBoundType(tree: Tree, tpe: Type) = {
- val msg = "implementation restriction: cannot reify classOf[%s] which refers to a type declared inside the block being reified".format(tpe)
- throw new ReifierError(tree.pos, msg)
- }
-
- def CannotReifyClassOfBoundEnum(tree: Tree, tpe: Type) = {
- val msg = "implementation restriction: cannot reify classOf[%s] which refers to an enum declared inside the block being reified".format(tpe)
- throw new ReifierError(tree.pos, msg)
- }
-
- def CannotReifyTypeInvolvingBoundType(tpe: Type) = {
- val msg = "implementation restriction: cannot reify type %s which involves a symbol declared inside the block being reified".format(tpe)
- throw new ReifierError(NoPosition, msg)
- }
-
- def CannotReifyAnnotationInvolvingBoundType(ann: AnnotationInfo) = {
- val msg = "implementation restriction: cannot reify annotation @%s which involves a symbol declared inside the block being reified".format(ann)
- throw new ReifierError(ann.original.pos, msg)
- }
- } // end of Reifier
-
- // begin reify
- import Reifier._
- if (tree.tpe != null) {
- val saved = printTypings
- try {
- val reifyDebug = settings.Yreifydebug.value
- val debugTrace = util.trace when reifyDebug
- debugTrace("transforming = ")(if (settings.Xshowtrees.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
- debugTrace("transformed = ") {
- val reifier = new Reifier()
- val untyped = reifier.reifyTopLevel(tree)
-
- val reifyCopypaste = settings.Yreifycopypaste.value
- if (reifyCopypaste) {
- if (reifyDebug) println("=======================")
- println(reifiedNodeToString(untyped))
- if (reifyDebug) println("=======================")
- }
-
- untyped
- }
- } finally {
- printTypings = saved
- }
- } else {
- CannotReifyPreTyperTree(tree)
- }
- }
-
- /** A throwable signalling a reification error */
- class ReifierError(var pos: Position, val msg: String) extends Throwable(msg) {
- def this(msg: String) = this(NoPosition, msg)
- }
-}
diff --git a/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala
deleted file mode 100644
index fce59bb099..0000000000
--- a/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala
+++ /dev/null
@@ -1,75 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2011 LAMP/EPFL
- * @author Martin Odersky
- */
-
-package scala.tools.nsc
-package ast
-
-import compat.Platform.EOL
-import symtab._
-import Flags._
-
-trait ReifyPrinters { self: NodePrinters =>
-
- val global: Global
- import global._
-
- object reifiedNodeToString extends Function1[Tree, String] {
- def apply(tree: Tree): String = {
- import scala.reflect.api.Modifier
-
- // @PP: I fervently hope this is a test case or something, not anything being
- // depended upon. Of more fragile code I cannot conceive.
- // @eb: This stuff is only needed to debug-print out reifications in human-readable format
- // Rolling a full-fledged, robust TreePrinter would be several times more code.
- (for (line <- (tree.toString.split(EOL) drop 2 dropRight 1)) yield {
- var s = line.trim
- s = s.replace("$mr.", "")
- s = s.replace(".apply", "")
- s = s.replace("scala.collection.immutable.", "")
- s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List")
- s = "List\\[.*?\\]".r.replaceAllIn(s, "List")
- s = s.replace("immutable.this.Nil", "List()")
- s = s.replace("modifiersFromInternalFlags", "Modifiers")
- s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()")
- s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => {
- val buf = new collection.mutable.ListBuffer[String]
-
- val annotations = m.group(3)
- if (buf.nonEmpty || annotations.nonEmpty)
- buf.append("List(" + annotations + ")")
-
- val privateWithin = "" + m.group(2)
- if (buf.nonEmpty || privateWithin != "")
- buf.append("newTypeName(\"" + privateWithin + "\")")
-
- val flags = m.group(1).toLong
- val s_flags = Flags.modifiersOfFlags(flags) map (_.sourceString) mkString ", "
- if (buf.nonEmpty || s_flags != "")
- buf.append("Set(" + s_flags + ")")
-
- "Modifiers(" + buf.reverse.mkString(", ") + ")"
- })
- s = """setInternalFlags\((\d+)L\)""".r.replaceAllIn(s, m => {
- val flags = m.group(1).toLong
- val mods = Flags.modifiersOfFlags(flags) map (_.sourceString)
- "setInternalFlags(flagsOfModifiers(List(" + mods.mkString(", ") + ")))"
- })
-
- s
- }) mkString EOL
- }
- }
-
-
- def printReifyCopypaste(tree: Tree) {
- val reifyDebug = settings.Yreifydebug.value
- if (reifyDebug) println("=======================")
- printReifyCopypaste1(tree)
- if (reifyDebug) println("=======================")
- }
-
- def printReifyCopypaste1(tree: Tree) {
- }
-} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index ad26ccad5e..19d1e0a51a 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -207,22 +207,6 @@ abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL {
def mkSysErrorCall(message: String): Tree =
mkMethodCall(Sys_error, List(Literal(Constant(message))))
- /** A creator for a call to a scala.reflect.Manifest or ClassManifest factory method.
- *
- * @param full full or partial manifest (target will be Manifest or ClassManifest)
- * @param constructor name of the factory method (e.g. "classType")
- * @param tparg the type argument
- * @param args value arguments
- * @return the tree
- */
- def mkManifestFactoryCall(full: Boolean, constructor: String, tparg: Type, args: List[Tree]): Tree =
- mkMethodCall(
- if (full) FullManifestModule else PartialManifestModule,
- newTermName(constructor),
- List(tparg),
- args
- )
-
/** Make a synchronized block on 'monitor'. */
def mkSynchronized(monitor: Tree, body: Tree): Tree =
Apply(Select(monitor, Object_synchronized), List(body))
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 43c231cf2d..66704680ae 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -13,6 +13,7 @@ import scala.reflect.internal.Flags.PARAM
import scala.reflect.internal.Flags.PARAMACCESSOR
import scala.reflect.internal.Flags.PRESUPER
import scala.reflect.internal.Flags.TRAIT
+import scala.compat.Platform.EOL
trait Trees extends reflect.internal.Trees { self: Global =>
@@ -33,30 +34,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
)
}
- class ValidatingPosAssigner extends PosAssigner {
- var pos: Position = _
- override def traverse(t: Tree) {
- if (t eq EmptyTree) ()
- else if (t.pos == NoPosition) super.traverse(t setPos pos)
- else if (globalPhase.id <= currentRun.picklerPhase.id) {
- // When we prune due to encountering a position, traverse the
- // pruned children so we can warn about those lacking positions.
- t.children foreach { c =>
- if ((c eq EmptyTree) || (c eq emptyValDef)) ()
- else if (c.pos == NoPosition) {
- reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase)
- inform("parent: " + treeSymStatus(t))
- inform(" child: " + treeSymStatus(c) + "\n")
- }
- }
- }
- }
- }
-
- override protected[this] lazy val posAssigner: PosAssigner =
- if (settings.Yrangepos.value && settings.debug.value || settings.Yposdebug.value) new ValidatingPosAssigner
- else new DefaultPosAssigner
-
// --- additional cases --------------------------------------------------------
/** Only used during parsing */
case class Parens(args: List[Tree]) extends Tree
@@ -84,15 +61,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
/** emitted by typer, eliminated by refchecks */
case class TypeTreeWithDeferredRefCheck()(val check: () => TypeTree) extends TypTree
- /** Marks underlying reference to id as boxed.
- * @pre: id must refer to a captured variable
- * A reference such marked will refer to the boxed entity, no dereferencing
- * with `.elem` is done on it.
- * This tree node can be emitted by macros such as reify that call markBoxedReference.
- * It is eliminated in LambdaLift, where the boxing conversion takes place.
- */
- case class ReferenceToBoxed(idt: Ident) extends TermTree
-
// --- factory methods ----------------------------------------------------------
/** Generates a template with constructor corresponding to
@@ -118,7 +86,7 @@ trait Trees extends reflect.internal.Trees { self: Global =>
// create parameters for <init> as synthetic trees.
var vparamss1 =
vparamss map (vps => vps.map { vd =>
- atPos(focusPos(vd.pos)) {
+ atPos(vd.pos.focus) {
ValDef(
Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM | BYNAMEPARAM) | PARAM | PARAMACCESSOR) withAnnotations vd.mods.annotations,
vd.name, vd.tpt.duplicate, vd.rhs.duplicate)
@@ -130,7 +98,7 @@ trait Trees extends reflect.internal.Trees { self: Global =>
// !!! I know "atPos in case" wasn't intentionally planted to
// add an air of mystery to this file, but it is the sort of
// comment which only its author could love.
- tpt = atPos(focusPos(vdef.pos))(TypeTree() setOriginal tpt setPos focusPos(tpt.pos)), // atPos in case
+ tpt = atPos(vdef.pos.focus)(TypeTree() setOriginal tpt setPos tpt.pos.focus), // atPos in case
rhs = EmptyTree
)
}
@@ -198,8 +166,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
traverser.traverse(qualifier)
case InjectDerivedValue(arg) =>
traverser.traverse(arg)
- case ReferenceToBoxed(idt) =>
- traverser.traverse(idt)
case TypeTreeWithDeferredRefCheck() =>
// (and rewrap the result? how to update the deferred check? would need to store wrapped tree instead of returning it from check)
case _ => super.xtraverse(traverser, tree)
@@ -209,7 +175,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef
def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray
def InjectDerivedValue(tree: Tree, arg: Tree): InjectDerivedValue
- def ReferenceToBoxed(tree: Tree, idt: Ident): ReferenceToBoxed
def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck
}
@@ -223,8 +188,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree)
def InjectDerivedValue(tree: Tree, arg: Tree) =
new InjectDerivedValue(arg)
- def ReferenceToBoxed(tree: Tree, idt: Ident) =
- new ReferenceToBoxed(idt).copyAttrs(tree)
def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match {
case dc@TypeTreeWithDeferredRefCheck() => new TypeTreeWithDeferredRefCheck()(dc.check).copyAttrs(tree)
}
@@ -246,11 +209,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
if (arg0 == arg) => t
case _ => this.treeCopy.InjectDerivedValue(tree, arg)
}
- def ReferenceToBoxed(tree: Tree, idt: Ident) = tree match {
- case t @ ReferenceToBoxed(idt0)
- if (idt0 == idt) => t
- case _ => this.treeCopy.ReferenceToBoxed(tree, idt)
- }
def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match {
case t @ TypeTreeWithDeferredRefCheck() => t
case _ => this.treeCopy.TypeTreeWithDeferredRefCheck(tree)
@@ -277,9 +235,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
case InjectDerivedValue(arg) =>
transformer.treeCopy.InjectDerivedValue(
tree, transformer.transform(arg))
- case ReferenceToBoxed(idt) =>
- transformer.treeCopy.ReferenceToBoxed(
- tree, transformer.transform(idt) match { case idt1: Ident => idt1 })
case TypeTreeWithDeferredRefCheck() =>
transformer.treeCopy.TypeTreeWithDeferredRefCheck(tree)
}
@@ -296,8 +251,8 @@ trait Trees extends reflect.internal.Trees { self: Global =>
// def resetAllAttrs[A<:Tree](x:A): A = { new ResetAttrsTraverser().traverse(x); x }
// def resetLocalAttrs[A<:Tree](x:A): A = { new ResetLocalAttrsTraverser().traverse(x); x }
- def resetAllAttrs[A<:Tree](x:A): A = new ResetAttrs(false).transform(x)
- def resetLocalAttrs[A<:Tree](x:A): A = new ResetAttrs(true).transform(x)
+ def resetAllAttrs[A <: Tree](x: A, leaveAlone: Tree => Boolean = null): A = new ResetAttrs(false, leaveAlone).transform(x)
+ def resetLocalAttrs[A <: Tree](x: A, leaveAlone: Tree => Boolean = null): A = new ResetAttrs(true, leaveAlone).transform(x)
/** A transformer which resets symbol and tpe fields of all nodes in a given tree,
* with special treatment of:
@@ -308,7 +263,7 @@ trait Trees extends reflect.internal.Trees { self: Global =>
*
* (bq:) This transformer has mutable state and should be discarded after use
*/
- private class ResetAttrs(localOnly: Boolean) {
+ private class ResetAttrs(localOnly: Boolean, leaveAlone: Tree => Boolean = null) {
val debug = settings.debug.value
val trace = scala.tools.nsc.util.trace when debug
@@ -328,6 +283,12 @@ trait Trees extends reflect.internal.Trees { self: Global =>
registerLocal(sym)
registerLocal(sym.sourceModule)
registerLocal(sym.moduleClass)
+ registerLocal(sym.companionClass)
+ registerLocal(sym.companionModule)
+ sym match {
+ case sym: TermSymbol => registerLocal(sym.referenced)
+ case _ => ;
+ }
}
}
@@ -335,10 +296,8 @@ trait Trees extends reflect.internal.Trees { self: Global =>
tree match {
case _: DefTree | Function(_, _) | Template(_, _, _) =>
markLocal(tree)
- case _ if tree.symbol.isInstanceOf[FreeVar] =>
- markLocal(tree)
case _ =>
- ;
+ tree
}
super.traverse(tree)
@@ -346,43 +305,48 @@ trait Trees extends reflect.internal.Trees { self: Global =>
}
class Transformer extends self.Transformer {
- override def transform(tree: Tree): Tree = super.transform {
- tree match {
- case tpt: TypeTree =>
- if (tpt.original != null) {
- transform(tpt.original)
- } else {
- if (tpt.tpe != null && (tpt.wasEmpty || (tpt.tpe exists (tp => locals contains tp.typeSymbol))))
- tpt.tpe = null
- tree
+ override def transform(tree: Tree): Tree = {
+ if (leaveAlone != null && leaveAlone(tree))
+ tree
+ else
+ super.transform {
+ tree match {
+ case tpt: TypeTree =>
+ if (tpt.original != null) {
+ transform(tpt.original)
+ } else {
+ if (tpt.tpe != null && (tpt.wasEmpty || (tpt.tpe exists (tp => locals contains tp.typeSymbol))))
+ tpt.tpe = null
+ tree
+ }
+ case TypeApply(fn, args) if args map transform exists (_.isEmpty) =>
+ transform(fn)
+ case This(_) if tree.symbol != null && tree.symbol.isPackageClass =>
+ tree
+ case EmptyTree =>
+ tree
+ case _ =>
+ if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)))
+ tree.symbol = NoSymbol
+ tree.tpe = null
+ tree
}
- case TypeApply(fn, args) if args map transform exists (_.isEmpty) =>
- transform(fn)
- case This(_) if tree.symbol != null && tree.symbol.isPackageClass =>
- tree
- case EmptyTree =>
- tree
- case _ =>
- if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)))
- tree.symbol = NoSymbol
- tree.tpe = null
- tree
+ }
}
- }
}
def transform[T <: Tree](x: T): T = {
- new MarkLocals().traverse(x)
+ if (localOnly)
+ new MarkLocals().traverse(x)
- if (debug) {
+ if (localOnly && debug) {
assert(locals.size == orderedLocals.size)
- val eoln = System.getProperty("line.separator")
- val msg = orderedLocals.toList filter {_ != NoSymbol} map {" " + _} mkString eoln
+ val msg = orderedLocals.toList filter {_ != NoSymbol} map {" " + _} mkString EOL
trace("locals (%d total): %n".format(orderedLocals.size))(msg)
}
val x1 = new Transformer().transform(x)
- assert(x.getClass isInstance x1)
+ assert(x.getClass isInstance x1, x1.getClass)
x1.asInstanceOf[T]
}
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index e7e3eaabf5..daabfae6b3 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -1771,7 +1771,23 @@ self =>
* }}}
*/
def pattern2(): Tree = {
+ val nameOffset = in.offset
+ def warnIfMacro(tree: Tree): Unit = {
+ def check(name: Name): Unit = if (name.toString == nme.MACROkw.toString)
+ warning(nameOffset, "in future versions of Scala \"macro\" will be a keyword. consider using a different name.")
+ tree match {
+ case _: BackQuotedIdent =>
+ ;
+ case Ident(name) =>
+ check(name)
+ case _ =>
+ ;
+ }
+ }
+
val p = pattern3()
+ warnIfMacro(p)
+
if (in.token != AT) p
else p match {
case Ident(nme.WILDCARD) =>
@@ -2421,10 +2437,10 @@ self =>
*/
/** {{{
- * FunDef ::= FunSig `:' Type `=' Expr
- * | FunSig [nl] `{' Block `}'
- * | this ParamClause ParamClauses (`=' ConstrExpr | [nl] ConstrBlock)
- * | `macro' FunSig [`:' Type] `=' Expr
+ * FunDef ::= FunSig [`:' Type] `=' [`macro'] Expr
+ * | FunSig [nl] `{' Block `}'
+ * | `this' ParamClause ParamClauses
+ * (`=' ConstrExpr | [nl] ConstrBlock)
* FunDcl ::= FunSig [`:' Type]
* FunSig ::= id [FunTypeParamClause] ParamClauses
* }}}
@@ -2444,18 +2460,16 @@ self =>
}
else {
val nameOffset = in.offset
+ val isBackquoted = in.token == BACKQUOTED_IDENT
val name = ident()
- if (name == nme.macro_ && isIdent && settings.Xmacros.value)
- funDefRest(start, in.offset, mods | Flags.MACRO, ident())
- else
- funDefRest(start, nameOffset, mods, name)
+ if (name.toString == nme.MACROkw.toString && !isBackquoted)
+ warning(nameOffset, "in future versions of Scala \"macro\" will be a keyword. consider using a different name.")
+ funDefRest(start, nameOffset, mods, name)
}
}
def funDefRest(start: Int, nameOffset: Int, mods: Modifiers, name: Name): Tree = {
val result = atPos(start, if (name.toTermName == nme.ERROR) start else nameOffset) {
- val isMacro = mods hasFlag Flags.MACRO
- val isTypeMacro = isMacro && name.isTypeName
var newmods = mods
// contextBoundBuf is for context bounded type parameters of the form
// [T : B] or [T : => B]; it contains the equivalent implicit parameter type,
@@ -2463,12 +2477,10 @@ self =>
val contextBoundBuf = new ListBuffer[Tree]
val tparams = typeParamClauseOpt(name, contextBoundBuf)
val vparamss = paramClauses(name, contextBoundBuf.toList, false)
- if (!isMacro) newLineOptWhenFollowedBy(LBRACE)
- var restype = if (isTypeMacro) TypeTree() else fromWithinReturnType(typedOpt())
- val rhs =
- if (isMacro)
- equalsExpr()
- else if (isStatSep || in.token == RBRACE) {
+ newLineOptWhenFollowedBy(LBRACE)
+ var restype = fromWithinReturnType(typedOpt())
+ val rhs =
+ if (isStatSep || in.token == RBRACE) {
if (restype.isEmpty) restype = scalaUnitConstr
newmods |= Flags.DEFERRED
EmptyTree
@@ -2476,10 +2488,12 @@ self =>
restype = scalaUnitConstr
blockExpr()
} else {
- if (name == nme.macro_ && isIdent && in.token != EQUALS) {
- warning("this syntactically invalid code resembles a macro definition. have you forgotten to enable -Xmacros?")
+ accept(EQUALS)
+ if (settings.Xmacros.value && in.token == MACRO) {
+ in.nextToken()
+ newmods |= Flags.MACRO
}
- equalsExpr()
+ expr()
}
DefDef(newmods, name, tparams, vparamss, restype, rhs)
}
@@ -2529,7 +2543,7 @@ self =>
/** {{{
* TypeDef ::= type Id [TypeParamClause] `=' Type
- * | `macro' FunSig `=' Expr
+ * | FunSig `=' Expr
* TypeDcl ::= type Id [TypeParamClause] TypeBounds
* }}}
*/
@@ -2537,22 +2551,22 @@ self =>
in.nextToken()
newLinesOpt()
atPos(start, in.offset) {
+ val nameOffset = in.offset
+ val isBackquoted = in.token == BACKQUOTED_IDENT
val name = identForType()
- if (name == nme.macro_.toTypeName && isIdent && settings.Xmacros.value) {
- funDefRest(start, in.offset, mods | Flags.MACRO, identForType())
- } else {
- // @M! a type alias as well as an abstract type may declare type parameters
- val tparams = typeParamClauseOpt(name, null)
- in.token match {
- case EQUALS =>
- in.nextToken()
- TypeDef(mods, name, tparams, typ())
- case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE =>
- TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds())
- case _ =>
- syntaxErrorOrIncomplete("`=', `>:', or `<:' expected", true)
- EmptyTree
- }
+ if (name.toString == nme.MACROkw.toString && !isBackquoted)
+ warning(nameOffset, "in future versions of Scala \"macro\" will be a keyword. consider using a different name.")
+ // @M! a type alias as well as an abstract type may declare type parameters
+ val tparams = typeParamClauseOpt(name, null)
+ in.token match {
+ case EQUALS =>
+ in.nextToken()
+ TypeDef(mods, name, tparams, typ())
+ case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE =>
+ TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds())
+ case _ =>
+ syntaxErrorOrIncomplete("`=', `>:', or `<:' expected", true)
+ EmptyTree
}
}
}
@@ -2599,7 +2613,10 @@ self =>
def classDef(start: Int, mods: Modifiers): ClassDef = {
in.nextToken
val nameOffset = in.offset
+ val isBackquoted = in.token == BACKQUOTED_IDENT
val name = identForType()
+ if (name.toString == nme.MACROkw.toString && !isBackquoted)
+ warning(nameOffset, "in future versions of Scala \"macro\" will be a keyword. consider using a different name.")
atPos(start, if (name == tpnme.ERROR) start else nameOffset) {
savingClassContextBounds {
@@ -2640,7 +2657,10 @@ self =>
def objectDef(start: Int, mods: Modifiers): ModuleDef = {
in.nextToken
val nameOffset = in.offset
+ val isBackquoted = in.token == BACKQUOTED_IDENT
val name = ident()
+ if (name.toString == nme.MACROkw.toString && !isBackquoted)
+ warning(nameOffset, "in future versions of Scala \"macro\" will be a keyword. consider using a different name.")
val tstart = in.offset
atPos(start, if (name == nme.ERROR) start else nameOffset) {
val mods1 = if (in.token == SUBTYPE) mods | Flags.DEFERRED else mods
@@ -2818,7 +2838,25 @@ self =>
* }}}
*/
def packaging(start: Int): Tree = {
+ val nameOffset = in.offset
+ def warnIfMacro(tree: Tree): Unit = {
+ def check(name: Name): Unit = if (name.toString == nme.MACROkw.toString)
+ warning(nameOffset, "in future versions of Scala \"macro\" will be a keyword. consider using a different name.")
+ tree match {
+ case _: BackQuotedIdent =>
+ ;
+ case Ident(name) =>
+ check(name)
+ case Select(qual, name) =>
+ warnIfMacro(qual)
+ check(name)
+ case _ =>
+ ;
+ }
+ }
+
val pkg = pkgQualId()
+ warnIfMacro(pkg)
val stats = inBracesOrNil(topStatSeq())
makePackaging(start, pkg, stats)
}
@@ -3020,8 +3058,29 @@ self =>
ts ++= topStatSeq()
}
} else {
+ val nameOffset = in.offset
+ def warnIfMacro(tree: Tree): Unit = {
+ def check(name: Name): Unit = if (name.toString == nme.MACROkw.toString)
+ warning(nameOffset, "in future versions of Scala \"macro\" will be a keyword. consider using a different name.")
+ tree match {
+ // [Eugene] pkgQualId never returns BackQuotedIdents
+ // this means that we'll get spurious warnings even if we wrap macro package name in backquotes
+ case _: BackQuotedIdent =>
+ ;
+ case Ident(name) =>
+ check(name)
+ case Select(qual, name) =>
+ warnIfMacro(qual)
+ check(name)
+ case _ =>
+ ;
+ }
+ }
+
in.flushDoc
val pkg = pkgQualId()
+ warnIfMacro(pkg)
+
if (in.token == EOF) {
ts += makePackaging(start, pkg, List())
} else if (isStatSep) {
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 2895d02dfe..81d81a4fb7 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -1125,7 +1125,8 @@ trait Scanners extends ScannersCommon {
nme.SUPERTYPEkw -> SUPERTYPE,
nme.HASHkw -> HASH,
nme.ATkw -> AT
- )
+ ) ++
+ (if (settings.Xmacros.value) List(nme.MACROkw -> MACRO) else List())
private var kwOffset: Int = -1
private val kwArray: Array[Int] = {
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
index fb4daefd57..e17bbf5e46 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
@@ -110,6 +110,7 @@ object Tokens extends Tokens {
final val MATCH = 58
final val FORSOME = 59
final val LAZY = 61
+ final val MACRO = 62
def isKeyword(code: Int) =
code >= IF && code <= LAZY
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index be1e466f4e..c04be1721e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -651,7 +651,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
case StringTag =>
buf put 's'.toByte
buf putShort cpool.addUtf8(const.stringValue).toShort
- case ClassTag =>
+ case ClazzTag =>
buf put 'c'.toByte
buf putShort cpool.addUtf8(javaType(const.typeValue).getSignature()).toShort
case EnumTag =>
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala
index b74981b999..807a3dd0bb 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala
@@ -121,7 +121,7 @@ trait GenJVMUtil {
case DoubleTag => jcode emitPUSH const.doubleValue
case StringTag => jcode emitPUSH const.stringValue
case NullTag => jcode.emitACONST_NULL()
- case ClassTag =>
+ case ClazzTag =>
val kind = toTypeKind(const.typeValue)
val toPush =
if (kind.isValueType) classLiteral(kind)
diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
index 2fb615f893..98c1fc2f63 100644
--- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
+++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
@@ -365,7 +365,7 @@ abstract class GenMSIL extends SubComponent {
arr.foreach(emitConst)
}
- // TODO: other Tags: NoTag, UnitTag, ClassTag, EnumTag, ArrayTag ???
+ // TODO: other Tags: NoTag, UnitTag, ClazzTag, EnumTag, ArrayTag ???
case _ => abort("could not handle attribute argument: " + const)
}
@@ -388,7 +388,7 @@ abstract class GenMSIL extends SubComponent {
case DoubleTag => buf.put(0x0d.toByte)
case StringTag => buf.put(0x0e.toByte)
- // TODO: other Tags: NoTag, UnitTag, ClassTag, EnumTag ???
+ // TODO: other Tags: NoTag, UnitTag, ClazzTag, EnumTag ???
// ArrayTag falls in here
case _ => abort("could not handle attribute argument: " + c)
@@ -968,7 +968,7 @@ abstract class GenMSIL extends SubComponent {
case DoubleTag => mcode.Emit(OpCodes.Ldc_R8, const.doubleValue)
case StringTag => mcode.Emit(OpCodes.Ldstr, const.stringValue)
case NullTag => mcode.Emit(OpCodes.Ldnull)
- case ClassTag =>
+ case ClazzTag =>
mcode.Emit(OpCodes.Ldtoken, msilType(const.typeValue))
mcode.Emit(OpCodes.Call, TYPE_FROM_HANDLE)
case _ => abort("Unknown constant value: " + const)
diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala
index 5b298b3761..12a3c4b3c6 100644
--- a/src/compiler/scala/tools/nsc/interactive/Global.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Global.scala
@@ -957,7 +957,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
if (ownerTpe.isErroneous) List()
else new ImplicitSearch(
tree, functionType(List(ownerTpe), AnyClass.tpe), isView = true,
- context.makeImplicit(reportAmbiguousErrors = false)).allImplicits
+ context0 = context.makeImplicit(reportAmbiguousErrors = false)).allImplicits
for (view <- applicableViews) {
val vtree = viewApply(view)
val vpre = stabilizedType(vtree)
diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala
index 88e3827403..72e5ee42ed 100644
--- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala
+++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala
@@ -6,7 +6,7 @@ package scala.tools.nsc
package interactive
import ast.Trees
-import symtab.Positions
+import ast.Positions
import scala.tools.nsc.util.{SourceFile, Position, RangePosition, NoPosition, WorkScheduler}
import scala.collection.mutable.ListBuffer
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 0c64bb2901..c0f7d8412a 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -13,6 +13,7 @@ import scala.sys.BooleanProp
import io.VirtualDirectory
import scala.tools.nsc.io.AbstractFile
import reporters._
+import reporters.{Reporter => NscReporter}
import symtab.Flags
import scala.reflect.internal.Names
import scala.tools.util.PathResolver
@@ -274,7 +275,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
protected def createLineManager(classLoader: ClassLoader): Line.Manager = new Line.Manager(classLoader)
/** Instantiate a compiler. Overridable. */
- protected def newCompiler(settings: Settings, reporter: Reporter) = {
+ protected def newCompiler(settings: Settings, reporter: NscReporter) = {
settings.outputDirs setSingleOutput virtualDirectory
settings.exposeEmptyPackage.value = true
@@ -340,7 +341,14 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
def getInterpreterClassLoader() = classLoader
// Set the current Java "context" class loader to this interpreter's class loader
- def setContextClassLoader() = classLoader.setAsContext()
+ def setContextClassLoader() = {
+ classLoader.setAsContext()
+
+ // this is risky, but it's our only possibility to make default reflexive mirror to work with REPL
+ // so far we have only used the default mirror to create a few manifests for the compiler
+ // so it shouldn't be in conflict with our classloader, especially since it respects its parent
+ scala.reflect.mirror.classLoader = classLoader
+ }
/** Given a simple repl-defined name, returns the real name of
* the class representing it, e.g. for "Bippy" it may return
diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala
index 14876425f4..cc06100f5f 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Power.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala
@@ -6,7 +6,6 @@
package scala.tools.nsc
package interpreter
-import scala.reflect.AnyValManifest
import scala.collection.{ mutable, immutable }
import scala.util.matching.Regex
import scala.tools.nsc.util.{ BatchSourceFile }
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala
index ad6e8dc48d..e293c0fed9 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala
@@ -58,7 +58,7 @@ object ReplVals {
* I have this forwarder which widens the type and then cast the result back
* to the dependent type.
*/
- def manifestToType(m: OptManifest[_]): Global#Type =
+ def manifestToType(m: Manifest[_]): Global#Type =
definitions.manifestToType(m)
class AppliedTypeFromManifests(sym: Symbol) {
diff --git a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala
index 5edc8fd202..59a7b9b5d2 100644
--- a/src/compiler/scala/tools/nsc/interpreter/RichClass.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/RichClass.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc
package interpreter
class RichClass[T](val clazz: Class[T]) {
- def toManifest: Manifest[T] = Manifest.classType(clazz)
+ def toManifest: Manifest[T] = Manifest[T](ClassManifest[T](clazz).tpe)
def toTypeString: String = TypeStrings.fromClazz(clazz)
// Sadly isAnonymousClass does not return true for scala anonymous
diff --git a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala
index 6b56d881fc..872ac00bfd 100644
--- a/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/TypeStrings.scala
@@ -10,6 +10,7 @@ import java.lang.{ reflect => r }
import r.TypeVariable
import scala.reflect.NameTransformer
import NameTransformer._
+import scala.reflect.{mirror => rm}
/** Logic for turning a type into a String. The goal is to be
* able to take some arbitrary object 'x' and obtain the most precise
@@ -72,8 +73,12 @@ trait TypeStrings {
brackets(clazz.getTypeParameters map tvarString: _*)
}
- private def tparamString[T: Manifest] : String =
- brackets(manifest[T].typeArguments map (m => tvarString(List(m.erasure))): _*)
+ private def tparamString[T: Manifest] : String = {
+ // [Eugene to Paul] needs review!!
+ def typeArguments: List[rm.Type] = manifest[T].tpe.typeArguments
+ def typeVariables: List[java.lang.Class[_]] = typeArguments map (targ => rm.typeToClass(targ))
+ brackets(typeArguments map (jc => tvarString(List(jc))): _*)
+ }
/** Going for an overabundance of caution right now. Later these types
* can be a lot more precise, but right now the manifests have a habit of
diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
index 2ba8c8eb6b..ab8fe23909 100644
--- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
@@ -35,17 +35,22 @@ abstract class AbstractReporter extends Reporter {
else _severity
if (severity == INFO) {
- if (isVerbose || force)
+ if (isVerbose || force) {
+ severity.count += 1
display(pos, msg, severity)
+ }
}
else {
val hidden = testAndLog(pos, severity)
if (severity == WARNING && noWarnings) ()
else {
- if (!hidden || isPromptSet)
+ if (!hidden || isPromptSet) {
+ severity.count += 1
display(pos, msg, severity)
- else if (settings.debug.value)
+ } else if (settings.debug.value) {
+ severity.count += 1
display(pos, "[ suppressed ] " + msg, severity)
+ }
if (isPromptSet)
displayPrompt
diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
index f5335fb0f5..956c43c35a 100644
--- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
@@ -75,7 +75,6 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr
}
def display(pos: Position, msg: String, severity: Severity) {
- severity.count += 1
if (severity != ERROR || severity.count <= ERROR_LIMIT)
print(pos, msg, severity)
}
diff --git a/src/compiler/scala/tools/nsc/scratchpad/Executor.scala b/src/compiler/scala/tools/nsc/scratchpad/Executor.scala
index ff0f94d897..8a918a829c 100644
--- a/src/compiler/scala/tools/nsc/scratchpad/Executor.scala
+++ b/src/compiler/scala/tools/nsc/scratchpad/Executor.scala
@@ -28,7 +28,7 @@ object Executor {
Console.setOut(newOut)
Console.setErr(newOut)
try {
- singletonInstance(name, classLoader)
+ singletonInstance(classLoader, name)
} catch {
case ex: Throwable =>
unwrapThrowable(ex) match {
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index e7959f36b2..ea12300785 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -28,7 +28,7 @@ class MutableSettings(val errorFn: String => Unit)
settings
}
- protected def copyInto(settings: MutableSettings) {
+ def copyInto(settings: MutableSettings) {
allSettings foreach { thisSetting =>
val otherSetting = settings.allSettings find { _.name == thisSetting.name }
otherSetting foreach { otherSetting =>
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 14b3bcc8ce..e9a7e3dab4 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -80,6 +80,9 @@ trait ScalaSettings extends AbsScalaSettings
val XlogImplicits = BooleanSetting ("-Xlog-implicits", "Show more detail on why some implicits are not applicable.")
val logImplicitConv = BooleanSetting ("-Xlog-implicit-conversions", "Print a message whenever an implicit conversion is inserted.")
val logReflectiveCalls = BooleanSetting("-Xlog-reflective-calls", "Print a message when a reflective method call is generated")
+ val logRuntimeSplices = BooleanSetting("-Xlog-runtime-splices", "Print a message when Expr.eval or Expr.value cannot be resolved statically.")
+ val logFreeTerms = BooleanSetting ("-Xlog-free-terms", "Print a message when reification creates a free term.")
+ val logFreeTypes = BooleanSetting ("-Xlog-free-types", "Print a message when reification resorts to generating a free type.")
val maxClassfileName = IntSetting ("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, Some((72, 255)), _ => None)
val Xmigration28 = BooleanSetting ("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8.")
val nouescape = BooleanSetting ("-Xno-uescape", "Disable handling of \\u unicode escapes.")
@@ -116,62 +119,62 @@ trait ScalaSettings extends AbsScalaSettings
/**
* -Y "Private" settings
*/
- val overrideObjects = BooleanSetting ("-Yoverride-objects", "Allow member objects to be overridden.")
- val overrideVars = BooleanSetting ("-Yoverride-vars", "Allow vars to be overridden.")
- val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options.")
- val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after")
- val check = PhasesSetting ("-Ycheck", "Check the tree at the end of")
- val Yshow = PhasesSetting ("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after")
- val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination.")
- val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees.")
- val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL.")
- val Xdce = BooleanSetting ("-Ydead-code", "Perform dead code elimination.")
- val debug = BooleanSetting ("-Ydebug", "Increase the quantity of debugging output.")
- // val doc = BooleanSetting ("-Ydoc", "Generate documentation")
- 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 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.")
- val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.")
- val noimports = BooleanSetting ("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.")
- val nopredef = BooleanSetting ("-Yno-predef", "Compile without importing Predef.")
- val noAdaptedArgs = BooleanSetting ("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.")
- val Yprofile = PhasesSetting ("-Yprofile", "(Requires jvm -agentpath to contain yjgpagent) Profile CPU usage of")
- val YprofileMem = BooleanSetting ("-Yprofile-memory", "Profile memory, get heap snapshot after each compiler run (requires yjpagent, see above).")
- val YprofileClass = StringSetting ("-Yprofile-class", "class", "Name of profiler class.", "scala.tools.util.YourkitProfiling")
- val Yrecursion = IntSetting ("-Yrecursion", "Set recursion depth used when locking symbols.", 0, Some((0, Int.MaxValue)), (_: String) => None)
- val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.")
- val Xshowtrees = BooleanSetting ("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs.")
- val Yshowsyms = BooleanSetting ("-Yshow-syms", "Print the AST symbol hierarchy after each phase.")
- val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.")
- val skip = PhasesSetting ("-Yskip", "Skip")
- val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "")
- val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "")
- val Ynosqueeze = BooleanSetting ("-Yno-squeeze", "Disable creation of compact code in matching.")
- val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (util.Statistics.enabled = _)
- val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat
- val stopBefore = PhasesSetting ("-Ystop-before", "Stop before")
- val refinementMethodDispatch =
- ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy",
- List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache")
- val globalClass = StringSetting ("-Yglobal-class", "class", "subclass of scala.tools.nsc.Global to use for compiler", "")
- val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.")
- 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 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.")
- val etaExpandKeepsStar = BooleanSetting("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.")
- val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.")
- val YvirtPatmat = BooleanSetting ("-Yvirtpatmat", "Translate pattern matches into flatMap/orElse calls. See scala.MatchingStrategy.")
- val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes")
+ val overrideObjects = BooleanSetting ("-Yoverride-objects", "Allow member objects to be overridden.")
+ val overrideVars = BooleanSetting ("-Yoverride-vars", "Allow vars to be overridden.")
+ val Yhelp = BooleanSetting ("-Y", "Print a synopsis of private options.")
+ val browse = PhasesSetting ("-Ybrowse", "Browse the abstract syntax tree after")
+ val check = PhasesSetting ("-Ycheck", "Check the tree at the end of")
+ val Yshow = PhasesSetting ("-Yshow", "(Requires -Xshow-class or -Xshow-object) Show after")
+ val Xcloselim = BooleanSetting ("-Yclosure-elim", "Perform closure elimination.")
+ val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees.")
+ val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL.")
+ val Xdce = BooleanSetting ("-Ydead-code", "Perform dead code elimination.")
+ val debug = BooleanSetting ("-Ydebug", "Increase the quantity of debugging output.")
+ //val doc = BooleanSetting ("-Ydoc", "Generate documentation")
+ 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 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.")
+ val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.")
+ val noimports = BooleanSetting ("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.")
+ val nopredef = BooleanSetting ("-Yno-predef", "Compile without importing Predef.")
+ val noAdaptedArgs = BooleanSetting ("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.")
+ val Yprofile = PhasesSetting ("-Yprofile", "(Requires jvm -agentpath to contain yjgpagent) Profile CPU usage of")
+ val YprofileMem = BooleanSetting ("-Yprofile-memory", "Profile memory, get heap snapshot after each compiler run (requires yjpagent, see above).")
+ val YprofileClass = StringSetting ("-Yprofile-class", "class", "Name of profiler class.", "scala.tools.util.YourkitProfiling")
+ val Yrecursion = IntSetting ("-Yrecursion", "Set recursion depth used when locking symbols.", 0, Some((0, Int.MaxValue)), (_: String) => None)
+ val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations.")
+ val Xshowtrees = BooleanSetting ("-Yshow-trees", "(Requires -Xprint:) Print detailed ASTs in formatted form.")
+ val XshowtreesCompact
+ = BooleanSetting ("-Yshow-trees-compact", "(Requires -Xprint:) Print detailed ASTs in compact form.")
+ val XshowtreesStringified
+ = BooleanSetting ("-Yshow-trees-stringified", "(Requires -Xprint:) Print stringifications along with detailed ASTs.")
+ val Yshowsyms = BooleanSetting ("-Yshow-syms", "Print the AST symbol hierarchy after each phase.")
+ val Yshowsymkinds = BooleanSetting ("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.")
+ val skip = PhasesSetting ("-Yskip", "Skip")
+ val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "")
+ val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "")
+ val Ynosqueeze = BooleanSetting ("-Yno-squeeze", "Disable creation of compact code in matching.")
+ val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (util.Statistics.enabled = _)
+ val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat
+ val stopBefore = PhasesSetting ("-Ystop-before", "Stop before")
+ val refinementMethodDispatch
+ = ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache")
+ val globalClass = StringSetting ("-Yglobal-class", "class", "subclass of scala.tools.nsc.Global to use for compiler", "")
+ val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.")
+ 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.")
+ val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.")
+ val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.")
+ val YvirtPatmat = BooleanSetting ("-Yvirtpatmat", "Translate pattern matches into flatMap/orElse calls. See scala.MatchingStrategy.")
+ val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes")
val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
val YnoProductN = BooleanSetting ("-Yno-productN", "Do not add ProductN to case classes")
@@ -180,26 +183,30 @@ trait ScalaSettings extends AbsScalaSettings
/** Area-specific debug output.
*/
- val Ybuildmanagerdebug = BooleanSetting("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.")
- val Ycompletion = BooleanSetting("-Ycompletion-debug", "Trace all tab completion activity.")
- val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.")
- 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 Ymacrodebug = BooleanSetting("-Ymacro-debug", "Trace macro-related activities: generation of synthetics, 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.")
- val Yrepldebug = BooleanSetting("-Yrepl-debug", "Trace all repl activity.") andThen (interpreter.replProps.debug setValue _)
- val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.")
+ val Ybuildmanagerdebug = BooleanSetting("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.")
+ val Ycompletion = BooleanSetting("-Ycompletion-debug", "Trace all tab completion activity.")
+ val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.")
+ 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 Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.")
+ val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.")
+ val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.")
+ val Yrepldebug = BooleanSetting("-Yrepl-debug", "Trace all repl activity.") andThen (interpreter.replProps.debug setValue _)
+ val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.")
/** Groups of Settings.
*/
- val future = BooleanSetting ("-Xfuture", "Turn on future language features.") enabling futureSettings
- val optimise = BooleanSetting ("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize" enabling optimiseSettings
- val Xexperimental = BooleanSetting ("-Xexperimental", "Enable experimental extensions.") enabling experimentalSettings
+ val future = BooleanSetting("-Xfuture", "Turn on future language features.") enabling futureSettings
+ val optimise = BooleanSetting("-optimise", "Generates faster bytecode by applying optimisations to the program") withAbbreviation "-optimize" enabling optimiseSettings
+ val Xexperimental = BooleanSetting("-Xexperimental", "Enable experimental extensions.") enabling experimentalSettings
// Feature extensions
- val Xmacros = BooleanSetting ("-Xmacros", "Enable macros.")
+ val Xmacros = BooleanSetting("-Xmacros", "Enable macros.")
+ val XmacroSettings = MultiStringSetting("-Xmacro-settings", "option", "Custom settings for macros.")
+ val XmacroPrimaryClasspath = PathSetting("-Xmacro-primary-classpath", "Classpath to load macros implementations from, defaults to compilation classpath (aka \"library classpath\".", "")
+ val XmacroFallbackClasspath = PathSetting("-Xmacro-fallback-classpath", "Classpath to load macros implementations from if they cannot be loaded from library classpath.", "")
/**
* IDE-specific settings
diff --git a/src/compiler/scala/tools/nsc/symtab/Positions.scala b/src/compiler/scala/tools/nsc/symtab/Positions.scala
deleted file mode 100644
index 94b619de90..0000000000
--- a/src/compiler/scala/tools/nsc/symtab/Positions.scala
+++ /dev/null
@@ -1,30 +0,0 @@
-package scala.tools.nsc
-package symtab
-
-import scala.tools.nsc.util.{ SourceFile, Position, OffsetPosition, NoPosition }
-
-trait Positions extends scala.reflect.internal.Positions {
-self: scala.tools.nsc.symtab.SymbolTable =>
-
- def rangePos(source: SourceFile, start: Int, point: Int, end: Int) =
- new OffsetPosition(source, point)
-
- def validatePositions(tree: Tree) {}
-
- type Position = scala.tools.nsc.util.Position
- val NoPosition = scala.tools.nsc.util.NoPosition
-
- type TreeAnnotation = scala.tools.nsc.util.TreeAnnotation
- def NoTreeAnnotation: TreeAnnotation = NoPosition
- def positionToAnnotation(pos: Position): TreeAnnotation = pos
- def annotationToPosition(annot: TreeAnnotation): Position = annot.pos
- override def _checkSetAnnotation(tree: Tree, annot: TreeAnnotation): Unit = {
- if (tree.pos != NoPosition && tree.pos != annot.pos) debugwarn("Overwriting annotation "+ tree.annotation +" of tree "+ tree +" with annotation "+ annot)
- // if ((tree.annotation.isInstanceOf[scala.tools.nsc.util.Position] || !annot.isInstanceOf[scala.tools.nsc.util.Position]) && tree.isInstanceOf[Block])
- // println("Updating block from "+ tree.annotation +" to "+ annot)
- }
- def focusPos(pos: Position): Position = pos.focus
- def isRangePos(pos: Position): Boolean = pos.isRange
- def showPos(pos: Position): String = pos.show
-
-}
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
index 758f870d6b..edbe6df472 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
@@ -425,7 +425,7 @@ abstract class Pickler extends SubComponent {
private def putConstant(c: Constant) {
if (putEntry(c)) {
if (c.tag == StringTag) putEntry(newTermName(c.stringValue))
- else if (c.tag == ClassTag) putType(c.typeValue)
+ else if (c.tag == ClazzTag) putType(c.typeValue)
else if (c.tag == EnumTag) putSymbol(c.symbolValue)
}
}
@@ -606,7 +606,7 @@ abstract class Pickler extends SubComponent {
else if (c.tag == FloatTag) writeLong(floatToIntBits(c.floatValue))
else if (c.tag == DoubleTag) writeLong(doubleToLongBits(c.doubleValue))
else if (c.tag == StringTag) writeRef(newTermName(c.stringValue))
- else if (c.tag == ClassTag) writeRef(c.typeValue)
+ else if (c.tag == ClazzTag) writeRef(c.typeValue)
else if (c.tag == EnumTag) writeRef(c.symbolValue)
LITERAL + c.tag // also treats UnitTag, NullTag; no value required
case AnnotatedType(annotations, tp, selfsym) =>
@@ -1059,7 +1059,7 @@ abstract class Pickler extends SubComponent {
else if (c.tag == FloatTag) print("Float "+c.floatValue)
else if (c.tag == DoubleTag) print("Double "+c.doubleValue)
else if (c.tag == StringTag) { print("String "); printRef(newTermName(c.stringValue)) }
- else if (c.tag == ClassTag) { print("Class "); printRef(c.typeValue) }
+ else if (c.tag == ClazzTag) { print("Class "); printRef(c.typeValue) }
else if (c.tag == EnumTag) { print("Enum "); printRef(c.symbolValue) }
case AnnotatedType(annots, tp, selfsym) =>
if (settings.selfInAnnots.value) {
diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
index 97e844f6d8..5a11926048 100644
--- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
+++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
@@ -179,7 +179,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
/** If `tp` refers to a non-interface trait, return a
* reference to its implementation class. Otherwise return `tp`.
*/
- def mixinToImplClass(tp: Type): Type = erasure(implSym) {
+ def mixinToImplClass(tp: Type): Type = AddInterfaces.this.erasure(implSym) {
tp match { //@MATN: no normalize needed (comes after erasure)
case TypeRef(pre, sym, _) if sym.needsImplClass =>
typeRef(pre, implClass(sym), Nil)
diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
index e6f5dc5b5f..eea87c8ba6 100644
--- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala
+++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -542,7 +542,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
if (forMSIL) savingStatics( transformTemplate(tree) )
else transformTemplate(tree)
- case Literal(c) if (c.tag == ClassTag) && !forMSIL=>
+ case Literal(c) if (c.tag == ClazzTag) && !forMSIL=>
val tpe = c.typeValue
typedWithPos(tree.pos) {
if (isPrimitiveValueClass(tpe.typeSymbol)) {
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index ecfc2b6084..e2ce3b62b4 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -734,7 +734,7 @@ abstract class Erasure extends AddInterfaces
/** A replacement for the standard typer's `typed1` method.
*/
- override protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
+ override def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
val tree1 = try {
tree match {
case InjectDerivedValue(arg) =>
@@ -1090,7 +1090,7 @@ abstract class Erasure extends AddInterfaces
case Match(selector, cases) =>
Match(Typed(selector, TypeTree(selector.tpe)), cases)
- case Literal(ct) if ct.tag == ClassTag
+ case Literal(ct) if ct.tag == ClazzTag
&& ct.typeValue.typeSymbol != definitions.UnitClass =>
val erased = ct.typeValue match {
case TypeRef(pre, clazz, args) if clazz.isDerivedValueClass => scalaErasure.eraseNormalClassRef(pre, clazz)
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index f6dc8fbfb0..6bddfe8d57 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -19,19 +19,6 @@ abstract class LambdaLift extends InfoTransform {
/** the following two members override abstract members in Transform */
val phaseName: String = "lambdalift"
- /** Converts types of captured variables to *Ref types.
- */
- def boxIfCaptured(sym: Symbol, tpe: Type, erasedTypes: Boolean) =
- if (sym.isCapturedVariable) {
- val symClass = tpe.typeSymbol
- def refType(valueRef: Map[Symbol, Symbol], objectRefClass: Symbol) =
- if (isPrimitiveValueClass(symClass) && symClass != UnitClass) valueRef(symClass).tpe
- else if (erasedTypes) objectRefClass.tpe
- else appliedType(objectRefClass, tpe)
- if (sym.hasAnnotation(VolatileAttr)) refType(volatileRefClass, VolatileObjectRefClass)
- else refType(refClass, ObjectRefClass)
- } else tpe
-
private val lifted = new TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(NoPrefix, sym, Nil) if sym.isClass && !sym.isPackageClass =>
@@ -46,7 +33,8 @@ abstract class LambdaLift extends InfoTransform {
}
def transformInfo(sym: Symbol, tp: Type): Type =
- boxIfCaptured(sym, lifted(tp), erasedTypes = true)
+ if (sym.isCapturedVariable) capturedVariableType(sym, tpe = lifted(tp), erasedTypes = true)
+ else lifted(tp)
protected def newTransformer(unit: CompilationUnit): Transformer =
new LambdaLifter(unit)
@@ -471,6 +459,8 @@ abstract class LambdaLift extends InfoTransform {
private def preTransform(tree: Tree) = super.transform(tree) setType lifted(tree.tpe)
override def transform(tree: Tree): Tree = tree match {
+ case Select(ReferenceToBoxed(idt), elem) if elem == nme.elem =>
+ postTransform(preTransform(idt), isBoxedRef = false)
case ReferenceToBoxed(idt) =>
postTransform(preTransform(idt), isBoxedRef = true)
case _ =>
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index 6d4dab57a3..0e4975c04c 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -1085,7 +1085,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
// add forwarders
assert(sym.alias != NoSymbol, sym)
// debuglog("New forwarder: " + sym.defString + " => " + sym.alias.defString)
- addDefDef(sym, Apply(staticRef(sym.alias), gen.mkAttributedThis(clazz) :: sym.paramss.head.map(Ident)))
+ if (!sym.isTermMacro) addDefDef(sym, Apply(staticRef(sym.alias), gen.mkAttributedThis(clazz) :: sym.paramss.head.map(Ident)))
}
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index f90d3d45fe..11f06a0541 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -358,18 +358,18 @@ abstract class UnCurry extends InfoTransform
def sequenceToArray(tree: Tree) = {
val toArraySym = tree.tpe member nme.toArray
assert(toArraySym != NoSymbol)
- def getManifest(tp: Type): Tree = {
- val manifestOpt = localTyper.findManifest(tp, false)
+ def getClassTag(tp: Type): Tree = {
+ val tag = localTyper.resolveClassTag(tree, tp)
// Don't want bottom types getting any further than this (SI-4024)
- if (tp.typeSymbol.isBottomClass) getManifest(AnyClass.tpe)
- else if (!manifestOpt.tree.isEmpty) manifestOpt.tree
- else if (tp.bounds.hi ne tp) getManifest(tp.bounds.hi)
- else localTyper.getManifestTree(tree, tp, false)
+ if (tp.typeSymbol.isBottomClass) getClassTag(AnyClass.tpe)
+ else if (!tag.isEmpty) tag
+ else if (tp.bounds.hi ne tp) getClassTag(tp.bounds.hi)
+ else localTyper.TyperErrorGen.MissingClassTagError(tree, tp)
}
afterUncurry {
localTyper.typedPos(pos) {
Apply(gen.mkAttributedSelect(tree, toArraySym),
- List(getManifest(tree.tpe.baseType(TraversableClass).typeArgs.head)))
+ List(getClassTag(tree.tpe.baseType(TraversableClass).typeArgs.head)))
}
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index ff0bdf7580..b400743469 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -277,11 +277,6 @@ trait ContextErrors {
setError(tree)
}
- def MultiDimensionalArrayError(tree: Tree) = {
- issueNormalTypeError(tree, "cannot create a generic multi-dimensional array of more than "+ definitions.MaxArrayDims+" dimensions")
- setError(tree)
- }
-
//typedSuper
def MixinMissingParentClassNameError(tree: Tree, mix: Name, clazz: Symbol) =
issueNormalTypeError(tree, mix+" does not name a parent class of "+clazz)
@@ -344,6 +339,11 @@ trait ContextErrors {
setError(tree)
}
+ def MacroEtaError(tree: Tree) = {
+ issueNormalTypeError(tree, "macros cannot be eta-expanded")
+ setError(tree)
+ }
+
//typedReturn
def ReturnOutsideOfDefError(tree: Tree) = {
issueNormalTypeError(tree, "return outside method definition")
@@ -453,6 +453,9 @@ trait ContextErrors {
// doTypeApply
//tryNamesDefaults
+ def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) =
+ NormalTypeError(tree, "macros application do not support named and/or default arguments")
+
def WrongNumberOfArgsError(tree: Tree, fun: Tree) =
NormalTypeError(tree, "wrong number of arguments for "+ treeSymTypeMsg(fun))
@@ -581,9 +584,9 @@ trait ContextErrors {
def AbstractExistentiallyOverParamerizedTpeError(tree: Tree, tp: Type) =
issueNormalTypeError(tree, "can't existentially abstract over parameterized type " + tp)
- //manifestTreee
- def MissingManifestError(tree: Tree, full: Boolean, tp: Type) = {
- issueNormalTypeError(tree, "cannot find "+(if (full) "" else "class ")+"manifest for element type "+tp)
+ // classTagTree
+ def MissingClassTagError(tree: Tree, tp: Type) = {
+ issueNormalTypeError(tree, "cannot find class tag for element type "+tp)
setError(tree)
}
@@ -622,7 +625,6 @@ trait ContextErrors {
def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) = {
val isBug = sym0.isAbstractType && sym1.isAbstractType && (sym0.name startsWith "_$")
issueSymbolTypeError(sym0, sym1+" is defined twice in " + context0.unit
- + ( if (sym0.isMacro && sym1.isMacro) "\n(note that macros cannot be overloaded)" else "" )
+ ( if (isBug) "\n(this error is likely due to a bug in the scala compiler involving wildcards in package objects)" else "" )
)
}
@@ -848,6 +850,19 @@ trait ContextErrors {
def TypeSigError(tree: Tree, ex: TypeError) = {
ex match {
+ case CyclicReference(_, _) if tree.symbol.isTermMacro =>
+ // say, we have a macro def `foo` and its macro impl `impl`
+ // if impl: 1) omits return type, 2) has anything implicit in its body, 3) sees foo
+ //
+ // then implicit search will trigger an error
+ // (note that this is not a compilation error, it's an artifact of implicit search algorithm)
+ // normally, such "errors" are discarded by `isCyclicOrErroneous` in Implicits.scala
+ // but in our case this won't work, because isCyclicOrErroneous catches CyclicReference exceptions
+ // while our error will manifest itself as a "recursive method needs a return type"
+ //
+ // hence we (together with reportTypeError in TypeDiagnostics) make sure that this CyclicReference
+ // evades all the handlers on its way and successfully reaches `isCyclicOrErroneous` in Implicits
+ throw ex
case CyclicReference(sym, info: TypeCompleter) =>
issueNormalTypeError(tree, typer.cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage())
case _ =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 9b1f395ad0..fe1c90fe67 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -105,8 +105,8 @@ trait Contexts { self: Analyzer =>
// not inherited to child contexts
var depth: Int = 0
var imports: List[ImportInfo] = List() // currently visible imports
- var openImplicits: List[(Type,Symbol)] = List() // types for which implicit arguments
- // are currently searched
+ var openImplicits: List[(Type,Tree)] = List() // types for which implicit arguments
+ // are currently searched
// for a named application block (Tree) the corresponding NamedApplyInfo
var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None
var prefix: Type = NoPrefix
@@ -119,6 +119,7 @@ trait Contexts { self: Analyzer =>
var diagnostic: List[String] = Nil // these messages are printed when issuing an error
var implicitsEnabled = false
+ var macrosEnabled = true
var checking = false
var retyping = false
@@ -181,6 +182,13 @@ trait Contexts { self: Analyzer =>
def logError(err: AbsTypeError) = buffer += err
+ def withImplicitsEnabled[T](op: => T): T = {
+ val saved = implicitsEnabled
+ implicitsEnabled = true
+ try op
+ finally implicitsEnabled = saved
+ }
+
def withImplicitsDisabled[T](op: => T): T = {
val saved = implicitsEnabled
implicitsEnabled = false
@@ -188,6 +196,20 @@ trait Contexts { self: Analyzer =>
finally implicitsEnabled = saved
}
+ def withMacrosEnabled[T](op: => T): T = {
+ val saved = macrosEnabled
+ macrosEnabled = true
+ try op
+ finally macrosEnabled = saved
+ }
+
+ def withMacrosDisabled[T](op: => T): T = {
+ val saved = macrosEnabled
+ macrosEnabled = false
+ try op
+ finally macrosEnabled = saved
+ }
+
def make(unit: CompilationUnit, tree: Tree, owner: Symbol,
scope: Scope, imports: List[ImportInfo]): Context = {
val c = new Context
@@ -223,6 +245,7 @@ trait Contexts { self: Analyzer =>
c.diagnostic = this.diagnostic
c.typingIndentLevel = typingIndentLevel
c.implicitsEnabled = this.implicitsEnabled
+ c.macrosEnabled = this.macrosEnabled
c.checking = this.checking
c.retyping = this.retyping
c.openImplicits = this.openImplicits
@@ -237,6 +260,7 @@ trait Contexts { self: Analyzer =>
val c = make(unit, EmptyTree, owner, scope, imports)
c.setReportErrors()
c.implicitsEnabled = true
+ c.macrosEnabled = true
c
}
@@ -312,6 +336,7 @@ trait Contexts { self: Analyzer =>
def issue(err: AbsTypeError) {
debugwarn("issue error: " + err.errMsg)
+ if (settings.Yissuedebug.value) (new Exception).printStackTrace()
if (reportErrors) unitError(err.errPos, addDiagString(err.errMsg))
else if (bufferErrors) { buffer += err }
else throw new TypeError(err.errPos, err.errMsg)
@@ -319,6 +344,7 @@ trait Contexts { self: Analyzer =>
def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) {
debugwarn("issue ambiguous error: " + err.errMsg)
+ if (settings.Yissuedebug.value) (new Exception).printStackTrace()
if (ambiguousErrors) {
if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous)
unitError(err.errPos, err.errMsg)
@@ -328,6 +354,7 @@ trait Contexts { self: Analyzer =>
def issueAmbiguousError(err: AbsTypeError) {
debugwarn("issue ambiguous error: " + err.errMsg)
+ if (settings.Yissuedebug.value) (new Exception).printStackTrace()
if (ambiguousErrors)
unitError(err.errPos, addDiagString(err.errMsg))
else if (bufferErrors) { buffer += err }
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 75440a1136..8aa257983a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -32,7 +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)
+ inferImplicit(tree, pt, reportAmbiguous, isView, context, true, NoPosition)
+
+ def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult =
+ inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, NoPosition)
/** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch`
* for more info how the search is conducted.
@@ -48,9 +51,12 @@ trait Implicits {
* @param saveAmbiguousDivergent False if any divergent/ambiguous errors should be ignored after
* implicits search,
* true if they should be reported (used in further typechecking).
+ * @param pos Position that is should be used for tracing and error reporting
+ * (useful when we infer synthetic stuff and pass EmptyTree in the `tree` argument)
+ * If it's set NoPosition, then position-based services will use `tree.pos`
* @return A search result
*/
- def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult = {
+ def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean, pos: Position): SearchResult = {
printInference("[infer %s] %s with pt=%s in %s".format(
if (isView) "view" else "implicit",
tree, pt, context.owner.enclClass)
@@ -71,9 +77,11 @@ trait Implicits {
if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty)
printTyping("typing implicit: %s %s".format(tree, context.undetparamsString))
val implicitSearchContext = context.makeImplicit(reportAmbiguous)
- val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext).bestImplicit
- if (saveAmbiguousDivergent && implicitSearchContext.hasErrors)
+ val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit
+ if (saveAmbiguousDivergent && implicitSearchContext.hasErrors) {
context.updateBuffer(implicitSearchContext.errBuffer.filter(err => err.kind == ErrorKinds.Ambiguous || err.kind == ErrorKinds.Divergent))
+ debugwarn("update buffer: " + implicitSearchContext.errBuffer)
+ }
printInference("[infer implicit] inferred " + result)
context.undetparams = context.undetparams filterNot result.subst.from.contains
@@ -100,8 +108,6 @@ trait Implicits {
improvesCache.clear()
}
- private val ManifestSymbols = Set(PartialManifestClass, FullManifestClass, OptManifestClass)
-
/* Map a polytype to one in which all type parameters and argument-dependent types are replaced by wildcards.
* Consider `implicit def b(implicit x: A): x.T = error("")`. We need to approximate DebruijnIndex types
* when checking whether `b` is a valid implicit, as we haven't even searched a value for the implicit arg `x`,
@@ -251,8 +257,11 @@ trait Implicits {
* @param pt The original expected type of the implicit.
* @param isView We are looking for a view
* @param context0 The context used for the implicit search
+ * @param pos0 Position that is preferable for use in tracing and error reporting
+ * (useful when we infer synthetic stuff and pass EmptyTree in the `tree` argument)
+ * If it's set to NoPosition, then position-based services will use `tree.pos`
*/
- class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context)
+ class ImplicitSearch(tree: Tree, pt: Type, isView: Boolean, context0: Context, pos0: Position = NoPosition)
extends Typer(context0) with ImplicitsContextErrors {
printTyping(
ptBlock("new ImplicitSearch",
@@ -264,6 +273,13 @@ trait Implicits {
)
)
// assert(tree.isEmpty || tree.pos.isDefined, tree)
+ def pos = if (pos0 != NoPosition) pos0 else tree.pos
+
+ def failure(what: Any, reason: String, pos: Position = this.pos): SearchResult = {
+ if (settings.XlogImplicits.value)
+ reporter.echo(pos, what+" is not a valid implicit value for "+pt+" because:\n"+reason)
+ SearchFailure
+ }
import infer._
/** Is implicit info `info1` better than implicit info `info2`?
@@ -351,13 +367,13 @@ trait Implicits {
* @pre `info.tpe` does not contain an error
*/
private def typedImplicit(info: ImplicitInfo, ptChecked: Boolean): SearchResult = {
- (context.openImplicits find { case (tp, sym) => sym == tree.symbol && dominates(pt, tp)}) match {
+ (context.openImplicits find { case (tp, tree1) => tree1.symbol == tree.symbol && dominates(pt, tp)}) match {
case Some(pending) =>
// println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG
throw DivergentImplicit
case None =>
try {
- context.openImplicits = (pt, tree.symbol) :: context.openImplicits
+ context.openImplicits = (pt, tree) :: context.openImplicits
// println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG
typedImplicit0(info, ptChecked)
} catch {
@@ -515,7 +531,7 @@ trait Implicits {
private def typedImplicit1(info: ImplicitInfo): SearchResult = {
incCounter(matchingImplicits)
- val itree = atPos(tree.pos.focus) {
+ val itree = atPos(pos.focus) {
if (info.pre == NoPrefix) Ident(info.name)
else Select(gen.mkAttributedQualifier(info.pre), info.name)
}
@@ -523,11 +539,7 @@ trait Implicits {
typeDebug.ptTree(itree), wildPt, info.name, info.tpe)
)
- def fail(reason: String): SearchResult = {
- if (settings.XlogImplicits.value)
- inform(itree+" is not a valid implicit value for "+pt+" because:\n"+reason)
- SearchFailure
- }
+ def fail(reason: String): SearchResult = failure(itree, reason)
try {
val itree1 =
if (isView) {
@@ -707,6 +719,7 @@ trait Implicits {
info.isCyclicOrErroneous
|| isView && isPredefMemberNamed(info.sym, nme.conforms)
|| isShadowed(info.name)
+ || (!context.macrosEnabled && info.sym.isTermMacro)
)
/** True if a given ImplicitInfo (already known isValid) is eligible.
@@ -825,7 +838,7 @@ trait Implicits {
throw DivergentImplicit
if (invalidImplicits.nonEmpty)
- setAddendum(tree.pos, () =>
+ setAddendum(pos, () =>
"\n Note: implicit "+invalidImplicits.head+" is not applicable here"+
" because it comes after the application point and it lacks an explicit result type")
}
@@ -1085,111 +1098,58 @@ trait Implicits {
implicitInfoss1
}
- /** Creates a tree that calls the relevant factory method in object
- * reflect.Manifest for type 'tp'. An EmptyTree is returned if
- * no manifest is found. todo: make this instantiate take type params as well?
- */
- private def manifestOfType(tp: Type, full: Boolean): SearchResult = {
-
- /** Creates a tree that calls the factory method called constructor in object reflect.Manifest */
- def manifestFactoryCall(constructor: String, tparg: Type, args: Tree*): Tree =
- if (args contains EmptyTree) EmptyTree
- else typedPos(tree.pos.focus) {
- val mani = gen.mkManifestFactoryCall(full, constructor, tparg, args.toList)
- if (settings.debug.value) println("generated manifest: "+mani) // DEBUG
- mani
- }
+ // these should be lazy, otherwise we wouldn't be able to compile scala-library with starr
+ private val TagSymbols = Set(ClassTagClass, TypeTagClass, GroundTypeTagClass)
+ private val TagMaterializers = Map(
+ ClassTagClass -> MacroInternal_materializeClassTag,
+ TypeTagClass -> MacroInternal_materializeTypeTag,
+ GroundTypeTagClass -> MacroInternal_materializeGroundTypeTag
+ )
- /** Creates a tree representing one of the singleton manifests.*/
- def findSingletonManifest(name: String) = typedPos(tree.pos.focus) {
- Select(gen.mkAttributedRef(FullManifestModule), name)
- }
+ def tagOfType(pre: Type, tp: Type, tagClass: Symbol): SearchResult = {
+ def success(arg: Tree) =
+ try {
+ val tree1 = typed(atPos(pos.focus)(arg))
+ def isErroneous = tree exists (_.isErroneous)
+ if (context.hasErrors) failure(tp, "failed to typecheck the materialized typetag: %n%s".format(context.errBuffer.head.errMsg), context.errBuffer.head.errPos)
+ else new SearchResult(tree1, EmptyTreeTypeSubstituter)
+ } catch {
+ case ex: TypeError =>
+ failure(arg, "failed to typecheck the materialized typetag: %n%s".format(ex.msg), ex.pos)
+ }
- /** Re-wraps a type in a manifest before calling inferImplicit on the result */
- def findManifest(tp: Type, manifestClass: Symbol = if (full) FullManifestClass else PartialManifestClass) =
- inferImplicit(tree, appliedType(manifestClass, tp), true, false, context).tree
-
- def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass)
- def mot(tp0: Type, from: List[Symbol], to: List[Type]): SearchResult = {
- implicit def wrapResult(tree: Tree): SearchResult =
- if (tree == EmptyTree) SearchFailure else new SearchResult(tree, if (from.isEmpty) EmptyTreeTypeSubstituter else new TreeTypeSubstituter(from, to))
-
- val tp1 = tp0.normalize
- tp1 match {
- case ThisType(_) | SingleType(_, _) =>
- // can't generate a reference to a value that's abstracted over by an existential
- if (containsExistential(tp1)) EmptyTree
- else manifestFactoryCall("singleType", tp, gen.mkAttributedQualifier(tp1))
- case ConstantType(value) =>
- manifestOfType(tp1.deconst, full)
- case TypeRef(pre, sym, args) =>
- if (isPrimitiveValueClass(sym) || isPhantomClass(sym)) {
- findSingletonManifest(sym.name.toString)
- } else if (sym == ObjectClass || sym == AnyRefClass) {
- findSingletonManifest("Object")
- } else if (sym == RepeatedParamClass || sym == ByNameParamClass) {
- EmptyTree
- } else if (sym == ArrayClass && args.length == 1) {
- manifestFactoryCall("arrayType", args.head, findManifest(args.head))
- } else if (sym.isClass) {
- val classarg0 = gen.mkClassOf(tp1)
- val classarg = tp match {
- case _: ExistentialType => gen.mkCast(classarg0, ClassType(tp))
- case _ => classarg0
- }
- val suffix = classarg :: (args map findSubManifest)
- manifestFactoryCall(
- "classType", tp,
- (if ((pre eq NoPrefix) || pre.typeSymbol.isStaticOwner) suffix
- else findSubManifest(pre) :: suffix): _*)
- } else if (sym.isExistentiallyBound && full) {
- manifestFactoryCall("wildcardType", tp,
- findManifest(tp.bounds.lo), findManifest(tp.bounds.hi))
- }
- // looking for a manifest of a type parameter that hasn't been inferred by now,
- // can't do much, but let's not fail
- else if (undetParams contains sym) {
- // #3859: need to include the mapping from sym -> NothingClass.tpe in the SearchResult
- mot(NothingClass.tpe, sym :: from, NothingClass.tpe :: to)
- } else {
- // a manifest should have been found by normal searchImplicit
- EmptyTree
- }
- case RefinedType(parents, decls) => // !!! not yet: if !full || decls.isEmpty =>
- // refinement is not generated yet
- if (hasLength(parents, 1)) findManifest(parents.head)
- else if (full) manifestFactoryCall("intersectionType", tp, parents map findSubManifest: _*)
- else mot(erasure.intersectionDominator(parents), from, to)
- case ExistentialType(tparams, result) =>
- mot(tp1.skolemizeExistential, from, to)
- case _ =>
- EmptyTree
-/* !!! the following is almost right, but we have to splice nested manifest
- * !!! types into this type. This requires a substantial extension of
- * !!! reifiers.
- val reifier = new Reifier()
- val rtree = reifier.reifyTopLevel(tp1)
- manifestFactoryCall("apply", tp, rtree)
-*/
- }
+ val prefix = (tagClass, pre) match {
+ // ClassTags only exist for scala.reflect.mirror, so their materializer doesn't care about prefixes
+ case (ClassTagClass, _) =>
+ gen.mkAttributedRef(Reflect_mirror) setType singleType(Reflect_mirror.owner.thisPrefix, Reflect_mirror)
+ // [Eugene to Martin] this is the crux of the interaction between implicits and reifiers
+ // here we need to turn a (supposedly path-dependent) type into a tree that will be used as a prefix
+ // I'm not sure if I've done this right - please, review
+ case (_, SingleType(prePre, preSym)) =>
+ gen.mkAttributedRef(prePre, preSym) setType pre
+ // necessary only to compile typetags used inside the Universe cake
+ case (_, ThisType(thisSym)) =>
+ gen.mkAttributedThis(thisSym)
+ case _ =>
+ // 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))
}
- mot(tp, Nil, Nil)
+ // todo. migrate hardcoded materialization in Implicits to corresponding implicit macros
+ var materializer = atPos(pos.focus)(Apply(TypeApply(Ident(TagMaterializers(tagClass)), List(TypeTree(tp))), List(prefix)))
+ if (settings.XlogImplicits.value) println("materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer))
+ success(materializer)
}
- def wrapResult(tree: Tree): SearchResult =
- if (tree == EmptyTree) SearchFailure else new SearchResult(tree, EmptyTreeTypeSubstituter)
-
/** The manifest corresponding to type `pt`, provided `pt` is an instance of Manifest.
*/
- private def implicitManifestOrOfExpectedType(pt: Type): SearchResult = pt.dealias match {
- case TypeRef(_, sym, args) if ManifestSymbols(sym) =>
- manifestOfType(args.head, sym == FullManifestClass) match {
- case SearchFailure if sym == OptManifestClass => wrapResult(gen.mkAttributedRef(NoManifest))
- case result => result
- }
+ private def implicitTagOrOfExpectedType(pt: Type): SearchResult = pt.dealias match {
+ case TypeRef(pre, sym, args) if TagSymbols(sym) =>
+ tagOfType(pre, args.head, sym)
case tp@TypeRef(_, sym, _) if sym.isAbstractType =>
- implicitManifestOrOfExpectedType(tp.bounds.lo) // #3977: use tp (==pt.dealias), not pt (if pt is a type alias, pt.bounds.lo == pt)
+ implicitTagOrOfExpectedType(tp.bounds.lo) // #3977: use tp (==pt.dealias), not pt (if pt is a type alias, pt.bounds.lo == pt)
case _ =>
searchImplicit(implicitsOfExpectedType, false)
// shouldn't we pass `pt` to `implicitsOfExpectedType`, or is the recursive case
@@ -1199,7 +1159,9 @@ trait Implicits {
/** The result of the implicit search:
* First search implicits visible in current context.
* If that fails, search implicits in expected type `pt`.
- * If that fails, and `pt` is an instance of Manifest, try to construct a manifest.
+ * // [Eugene] the following two lines should be deleted after we migrate delegate manifest materialization to implicit macros
+ * If that fails, and `pt` is an instance of a ClassTag, try to construct a class tag.
+ * If that fails, and `pt` is an instance of a TypeTag, try to construct a type tag.
* If all fails return SearchFailure
*/
def bestImplicit: SearchResult = {
@@ -1219,7 +1181,7 @@ trait Implicits {
val failstart = startTimer(oftypeFailNanos)
val succstart = startTimer(oftypeSucceedNanos)
- result = implicitManifestOrOfExpectedType(pt)
+ result = implicitTagOrOfExpectedType(pt)
if (result == SearchFailure) {
context.updateBuffer(previousErrs)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index ebf8e3fc9a..98b8d7673e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -67,7 +67,7 @@ trait Infer {
*/
def freshVar(tparam: Symbol): TypeVar = TypeVar(tparam)
- private class NoInstance(msg: String) extends Throwable(msg) with ControlThrowable { }
+ class NoInstance(msg: String) extends Throwable(msg) with ControlThrowable { }
private class DeferredNoInstance(getmsg: () => String) extends NoInstance("") {
override def getMessage(): String = getmsg()
}
@@ -267,6 +267,16 @@ trait Infer {
setError(tree)
}
else {
+ if (context.owner.isTermMacro && (sym1 hasFlag LOCKED)) {
+ // we must not let CyclicReference to be thrown from sym1.info
+ // because that would mark sym1 erroneous, which it is not
+ // but if it's a true CyclicReference then macro def will report it
+ // see comments to TypeSigError for an explanation of this special case
+ // [Eugene] is there a better way?
+ val dummy = new TypeCompleter { val tree = EmptyTree; override def complete(sym: Symbol) {} }
+ throw CyclicReference(sym1, dummy)
+ }
+
if (sym1.isTerm)
sym1.cookJavaRawInfo() // xform java rawtypes into existentials
@@ -310,6 +320,8 @@ trait Infer {
/** Like weakly compatible but don't apply any implicit conversions yet.
* Used when comparing the result type of a method with its prototype.
+ * [Martin] I think Infer is also created by Erasure, with the default
+ * implementation of isCoercible
*/
def isConservativelyCompatible(tp: Type, pt: Type): Boolean =
context.withImplicitsDisabled(isWeaklyCompatible(tp, pt))
@@ -426,6 +438,9 @@ trait Infer {
tvars map (tvar => WildcardType)
}
+ /** [Martin] Can someone comment this please? I have no idea what it's for
+ * and the code is not exactly readable.
+ */
object AdjustedTypeArgs {
val Result = collection.mutable.LinkedHashMap
type Result = collection.mutable.LinkedHashMap[Symbol, Option[Type]]
@@ -992,6 +1007,7 @@ trait Infer {
PolymorphicExpressionInstantiationError(tree, undetparams, pt)
} else {
new TreeTypeSubstituter(undetparams, targs).traverse(tree)
+ notifyUndetparamsInferred(undetparams, targs)
}
}
@@ -1028,6 +1044,7 @@ trait Infer {
if (checkBounds(fn, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")) {
val treeSubst = new TreeTypeSubstituter(okparams, okargs)
treeSubst traverseTrees fn :: args
+ notifyUndetparamsInferred(okparams, okargs)
leftUndet match {
case Nil => Nil
@@ -1116,6 +1133,7 @@ trait Infer {
(inferFor(pt) orElse inferForApproxPt) map { targs =>
new TreeTypeSubstituter(undetparams, targs).traverse(tree)
+ notifyUndetparamsInferred(undetparams, targs)
} getOrElse {
debugwarn("failed inferConstructorInstance for "+ tree +" : "+ tree.tpe +" under "+ undetparams +" pt = "+ pt +(if(isFullyDefined(pt)) " (fully defined)" else " (not fully defined)"))
// if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt)
@@ -1568,9 +1586,9 @@ trait Infer {
else infer
}
- /** Assign <code>tree</code> the type of unique polymorphic alternative
+ /** Assign <code>tree</code> the type of all polymorphic alternatives
* with <code>nparams</code> as the number of type parameters, if it exists.
- * If several or none such polymorphic alternatives exist, error.
+ * If no such polymorphic alternative exist, error.
*
* @param tree ...
* @param nparams ...
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index e43b1fab0b..3b270a92ad 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -3,135 +3,682 @@ package typechecker
import symtab.Flags._
import scala.tools.nsc.util._
+import scala.tools.nsc.util.ClassPath._
import scala.reflect.ReflectionUtils
+import scala.collection.mutable.ListBuffer
+import scala.compat.Platform.EOL
+import scala.reflect.makro.runtime.{Context => MacroContext}
+import scala.reflect.runtime.Mirror
+/**
+ * Code to deal with macros, namely with:
+ * * Compilation of macro definitions
+ * * Expansion of macro applications
+ *
+ * Say we have in a class C:
+ *
+ * def foo[T](xs: List[T]): T = macro fooBar
+ *
+ * Then fooBar needs to point to a static method of the following form:
+ *
+ * def fooBar[T: c.TypeTag]
+ * (c: scala.reflect.makro.Context)
+ * (xs: c.Expr[List[T]])
+ * : c.mirror.Tree = {
+ * ...
+ * }
+ *
+ * Then, if foo is called in qual.foo[Int](elems), where qual: D,
+ * the macro application is expanded to a reflective invocation of fooBar with parameters
+ *
+ * (simpleMacroContext{ type PrefixType = D; val prefix = qual })
+ * (Expr(elems))
+ * (TypeTag(Int))
+ */
trait Macros { self: Analyzer =>
import global._
import definitions._
- def macroMeth(mac: Symbol): Symbol = {
- var owner = mac.owner
- if (!owner.isModuleClass) owner = owner.companionModule.moduleClass
- owner.info.decl(nme.macroMethodName(mac.name))
- }
+ val macroDebug = settings.Ymacrodebug.value
+ val macroCopypaste = settings.Ymacrocopypaste.value
+ val macroTrace = scala.tools.nsc.util.trace when macroDebug
- def macroArgs(tree: Tree): (List[List[Tree]]) = tree match {
- case Apply(fn, args) =>
- macroArgs(fn) :+ args
- case TypeApply(fn, args) =>
- macroArgs(fn) :+ args
- case Select(qual, name) =>
- List(List(qual))
- case _ =>
- List(List())
- }
+ val globalMacroCache = collection.mutable.Map[Any, Any]()
+ val perRunMacroCache = perRunCaches.newMap[Symbol, collection.mutable.Map[Any, Any]]
- /**
- * The definition of the method implementing a macro. Example:
- * Say we have in a class C
+ /** A list of compatible macro implementation signatures.
*
- * def macro foo[T](xs: List[T]): T = expr
+ * In the example above:
+ * (c: scala.reflect.makro.Context)(xs: c.Expr[List[T]]): c.Expr[T]
*
- * Then the following macro method is generated for `foo`:
- *
- * def defmacro$foo
- * (_context: scala.reflect.macro.Context)
- * (_this: _context.Tree)
- * (T: _context.TypeTree)
- * (xs: _context.Tree): _context.Tree = {
- * import _context._ // this means that all methods of Context can be used unqualified in macro's body
- * expr
- * }
+ * @param macroDef The macro definition symbol
+ * @param tparams The type parameters of the macro definition
+ * @param vparamss The value parameters of the macro definition
+ * @param retTpe The return type of the macro definition
+ */
+ private def macroImplSigs(macroDef: Symbol, tparams: List[TypeDef], vparamss: List[List[ValDef]], retTpe: Type): (List[List[List[Symbol]]], Type) = {
+ // had to move method's body to an object because of the recursive dependencies between sigma and param
+ object SigGenerator {
+ val hasThis = macroDef.owner.isClass
+ val ownerTpe = macroDef.owner match {
+ case owner if owner.isModuleClass => new UniqueThisType(macroDef.owner)
+ case owner if owner.isClass => macroDef.owner.tpe
+ case _ => NoType
+ }
+ val hasTparams = !tparams.isEmpty
+
+ def sigma(tpe: Type): Type = {
+ class SigmaTypeMap extends TypeMap {
+ def apply(tp: Type): Type = tp match {
+ case TypeRef(pre, sym, args) =>
+ val pre1 = pre match {
+ case ThisType(sym) if sym == macroDef.owner =>
+ SingleType(SingleType(SingleType(NoPrefix, paramsCtx(0)), MacroContextPrefix), ExprValue)
+ case SingleType(NoPrefix, sym) =>
+ vparamss.flatten.find(_.symbol == sym) match {
+ case Some(macroDefParam) =>
+ SingleType(SingleType(NoPrefix, param(macroDefParam)), ExprValue)
+ case _ =>
+ pre
+ }
+ case _ =>
+ pre
+ }
+ val args1 = args map mapOver
+ TypeRef(pre1, sym, args1)
+ case _ =>
+ mapOver(tp)
+ }
+ }
+
+ new SigmaTypeMap() apply tpe
+ }
+
+ def makeParam(name: Name, pos: Position, tpe: Type, flags: Long = 0L) =
+ macroDef.newValueParameter(name, pos, flags) setInfo tpe
+ val ctxParam = makeParam(nme.macroContext, macroDef.pos, MacroContextClass.tpe, SYNTHETIC)
+ def implType(isType: Boolean, origTpe: Type): Type =
+ if (isRepeatedParamType(origTpe))
+ appliedType(
+ RepeatedParamClass.typeConstructor,
+ List(implType(isType, sigma(origTpe.typeArgs.head))))
+ else {
+ val tsym = getMember(MacroContextClass, if (isType) tpnme.TypeTag else tpnme.Expr)
+ typeRef(singleType(NoPrefix, ctxParam), tsym, List(sigma(origTpe)))
+ }
+ val paramCache = collection.mutable.Map[Symbol, Symbol]()
+ def param(tree: Tree): Symbol =
+ paramCache.getOrElseUpdate(tree.symbol, {
+ // [Eugene] deskolemization became necessary once I implemented inference of macro def return type
+ // please, verify this solution, but for now I'll leave it here - cargo cult for the win
+ val sym = tree.symbol.deSkolemize
+ val sigParam = makeParam(sym.name, sym.pos, implType(sym.isType, sym.tpe))
+ if (sym.isSynthetic) sigParam.flags |= SYNTHETIC
+ sigParam
+ })
+
+ val paramsCtx = List(ctxParam)
+ val paramsThis = List(makeParam(nme.macroThis, macroDef.pos, implType(false, ownerTpe), SYNTHETIC))
+ val paramsTparams = tparams map param
+ val paramssParams = vparamss map (_ map param)
+
+ var paramsss = List[List[List[Symbol]]]()
+ // tparams are no longer part of a signature, they get into macro implementations via context bounds
+// if (hasTparams && hasThis) paramsss :+= paramsCtx :: paramsThis :: paramsTparams :: paramssParams
+// if (hasTparams) paramsss :+= paramsCtx :: paramsTparams :: paramssParams
+ // _this params are no longer part of a signature, its gets into macro implementations via Context.prefix
+// if (hasThis) paramsss :+= paramsCtx :: paramsThis :: paramssParams
+ paramsss :+= paramsCtx :: paramssParams
+
+ val tsym = getMember(MacroContextClass, tpnme.Expr)
+ val implRetTpe = typeRef(singleType(NoPrefix, ctxParam), tsym, List(sigma(retTpe)))
+ }
+
+ import SigGenerator._
+ macroTrace("generating macroImplSigs for: ")(macroDef)
+ macroTrace("tparams are: ")(tparams)
+ macroTrace("vparamss are: ")(vparamss)
+ macroTrace("retTpe is: ")(retTpe)
+ macroTrace("macroImplSigs are: ")(paramsss, implRetTpe)
+ }
+
+ private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Option[Symbol]): List[List[Symbol]] = {
+ if (paramss.length == 0)
+ return paramss
+
+ val wannabe = if (paramss.head.length == 1) paramss.head.head else NoSymbol
+ val contextParam = if (wannabe != NoSymbol && wannabe.tpe <:< definitions.MacroContextClass.tpe) wannabe else NoSymbol
+
+ val lastParamList0 = paramss.lastOption getOrElse Nil
+ val lastParamList = lastParamList0 flatMap (param => param.tpe match {
+ case TypeRef(SingleType(NoPrefix, contextParam), sym, List(tparam)) =>
+ var wannabe = sym
+ while (wannabe.isAliasType) wannabe = wannabe.info.typeSymbol
+ if (wannabe != definitions.TypeTagClass)
+ List(param)
+ else
+ transform(param, tparam.typeSymbol) map (_ :: Nil) getOrElse Nil
+ case _ =>
+ List(param)
+ })
+
+ var result = paramss.dropRight(1) :+ lastParamList
+ if (lastParamList0.isEmpty ^ lastParamList.isEmpty) {
+ result = result dropRight 1
+ }
+
+ result
+ }
+
+ /** As specified above, body of a macro definition must reference its implementation.
+ * This function verifies that the body indeed refers to a method, and that
+ * the referenced macro implementation is compatible with the given macro definition.
*
- * If macro has no type arguments, the third parameter list is omitted (it's not empty, but omitted altogether).
+ * This means that macro implementation (fooBar in example above) must:
+ * 1) Refer to a statically accessible, non-overloaded method.
+ * 2) Have the right parameter lists as outlined in the SIP / in the doc comment of this class.
*
- * To find out the desugared representation of your particular macro, compile it with -Ymacro-debug.
+ * @return typechecked rhs of the given macro definition
*/
- def macroMethDef(mdef: DefDef): Tree = {
- def paramDef(name: Name, tpt: Tree) = ValDef(Modifiers(PARAM), name, tpt, EmptyTree)
- val contextType = TypeTree(ReflectMacroContext.tpe)
- val globParamSec = List(paramDef(nme.macroContext, contextType))
- def globSelect(name: Name) = Select(Ident(nme.macroContext), name)
- def globTree = globSelect(tpnme.Tree)
- def globTypeTree = globSelect(tpnme.TypeTree)
- val thisParamSec = List(paramDef(newTermName(nme.macroThis), globTree))
- def tparamInMacro(tdef: TypeDef) = paramDef(tdef.name.toTermName, globTypeTree)
- def vparamInMacro(vdef: ValDef): ValDef = paramDef(vdef.name, vdef.tpt match {
- case tpt @ AppliedTypeTree(hk, _) if treeInfo.isRepeatedParamType(tpt) => AppliedTypeTree(hk, List(globTree))
- case _ => globTree
- })
- def wrapImplicit(tree: Tree) = atPos(tree.pos) {
- // implicit hasn't proven useful so far, so I'm disabling it
- //val implicitDecl = ValDef(Modifiers(IMPLICIT), nme.macroContextImplicit, SingletonTypeTree(Ident(nme.macroContext)), Ident(nme.macroContext))
- val importGlob = Import(Ident(nme.macroContext), List(ImportSelector(nme.WILDCARD, -1, null, -1)))
- Block(List(importGlob), tree)
+ def typedMacroBody(typer: Typer, ddef: DefDef): Tree = {
+ import typer.context
+ if (macroDebug) println("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos))
+
+ implicit def augmentString(s: String) = new AugmentedString(s)
+ class AugmentedString(s: String) {
+ def abbreviateCoreAliases: String = { // hack!
+ var result = s
+ result = result.replace("c.mirror.TypeTag", "c.TypeTag")
+ result = result.replace("c.mirror.Expr", "c.Expr")
+ result
+ }
}
- var formals = (mdef.vparamss map (_ map vparamInMacro))
- if (mdef.tparams.nonEmpty) formals = (mdef.tparams map tparamInMacro) :: formals
-
- atPos(mdef.pos) {
- new DefDef( // can't call DefDef here; need to find out why
- mods = mdef.mods &~ MACRO &~ OVERRIDE,
- name = nme.macroMethodName(mdef.name),
- tparams = List(),
- vparamss = globParamSec :: thisParamSec :: formals,
- tpt = globTree,
- wrapImplicit(mdef.rhs))
+
+ var hasErrors = false
+ def reportError(pos: Position, msg: String) = {
+ hasErrors = true
+ context.error(pos, msg)
+ }
+
+ val macroDef = ddef.symbol
+ val defpos = macroDef.pos
+ val implpos = ddef.rhs.pos
+ assert(macroDef.isTermMacro, ddef)
+
+ def invalidBodyError() =
+ reportError(defpos,
+ "macro body has wrong shape:" +
+ "\n required: macro <reference to implementation object>.<implementation method name>" +
+ "\n or : macro <implementation method name>")
+ def validatePreTyper(rhs: Tree): Unit = rhs match {
+ // we do allow macro invocations inside macro bodies
+ // personally I don't mind if pre-typer tree is a macro invocation
+ // that later resolves to a valid reference to a macro implementation
+ // however, I don't think that invalidBodyError() should hint at that
+ // let this be an Easter Egg :)
+ case Apply(_, _) => ;
+ case TypeApply(_, _) => ;
+ case Super(_, _) => ;
+ case This(_) => ;
+ case Ident(_) => ;
+ case Select(_, _) => ;
+ case _ => invalidBodyError()
}
+ def validatePostTyper(rhs1: Tree): Unit = {
+ def loop(tree: Tree): Unit = {
+ def errorNotStatic() =
+ reportError(implpos, "macro implementation must be in statically accessible object")
+
+ def ensureRoot(sym: Symbol) =
+ if (!sym.isModule && !sym.isModuleClass) errorNotStatic()
+
+ def ensureModule(sym: Symbol) =
+ if (!sym.isModule) errorNotStatic()
+
+ tree match {
+ case TypeApply(fun, _) =>
+ loop(fun)
+ case Super(qual, _) =>
+ ensureRoot(macroDef.owner)
+ loop(qual)
+ case This(_) =>
+ ensureRoot(tree.symbol)
+ case Select(qual, name) if name.isTypeName =>
+ loop(qual)
+ case Select(qual, name) if name.isTermName =>
+ if (tree.symbol != rhs1.symbol) ensureModule(tree.symbol)
+ loop(qual)
+ case Ident(name) if name.isTypeName =>
+ ;
+ case Ident(name) if name.isTermName =>
+ if (tree.symbol != rhs1.symbol) ensureModule(tree.symbol)
+ case _ =>
+ invalidBodyError()
+ }
+ }
+
+ loop(rhs1)
+ }
+
+ val rhs = ddef.rhs
+ validatePreTyper(rhs)
+ if (hasErrors) macroTrace("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
+ // unfortunately, this means that we have to manually trigger macro expansion
+ // because it's adapt which is responsible for automatic expansion during typechecking
+ def typecheckRhs(rhs: Tree): Tree = {
+ try {
+ val prevNumErrors = reporter.ERROR.count // [Eugene] funnily enough, the isErroneous check is not enough
+ var rhs1 = if (hasErrors) EmptyTree else typer.typed1(rhs, EXPRmode, WildcardType)
+ def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors
+ def rhsNeedsMacroExpansion = rhs1.symbol != null && rhs1.symbol.isTermMacro && !rhs1.symbol.isErroneous
+ while (!typecheckedWithErrors && rhsNeedsMacroExpansion) {
+ rhs1 = macroExpand1(typer, rhs1) match {
+ case Success(expanded) =>
+ try {
+ val typechecked = typer.typed1(expanded, EXPRmode, WildcardType)
+ if (macroDebug) {
+ println("typechecked1:")
+ println(typechecked)
+ println(showRaw(typechecked))
+ }
+
+ typechecked
+ } finally {
+ openMacros = openMacros.tail
+ }
+ case Fallback(fallback) =>
+ typer.typed1(fallback, EXPRmode, WildcardType)
+ case Other(result) =>
+ result
+ }
+ }
+ rhs1
+ } catch {
+ case ex: TypeError =>
+ typer.reportTypeError(context, rhs.pos, ex)
+ typer.infer.setError(rhs)
+ }
+ }
+
+ val prevNumErrors = reporter.ERROR.count // funnily enough, the isErroneous check is not enough
+ 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)
+
+ val macroImpl = rhs1.symbol
+ macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(rhs1), Nil)
+ if (!hasErrors) {
+ if (macroImpl == null) {
+ invalidBodyError()
+ } else {
+ if (!macroImpl.isMethod)
+ invalidBodyError()
+ if (macroImpl.isOverloaded)
+ reportError(implpos, "macro implementation cannot be overloaded")
+ if (!macroImpl.typeParams.isEmpty && (!rhs1.isInstanceOf[TypeApply]))
+ reportError(implpos, "macro implementation reference needs type arguments")
+ if (!hasErrors)
+ validatePostTyper(rhs1)
+ }
+ if (hasErrors)
+ macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef)
+ }
+
+ if (!hasErrors) {
+ def checkCompatibility(reqparamss: List[List[Symbol]], actparamss: List[List[Symbol]], reqres: Type, actres: Type): List[String] = {
+ var hasErrors = false
+ var errors = List[String]()
+ def compatibilityError(msg: String) {
+ hasErrors = true
+ errors :+= msg
+ }
+
+ val flatreqparams = reqparamss.flatten
+ val flatactparams = actparamss.flatten
+ val tparams = macroImpl.typeParams
+ val tvars = tparams map freshVar
+ def lengthMsg(which: String, extra: Symbol) =
+ "parameter lists have different length, "+which+" extra parameter "+extra.defString
+ if (actparamss.length != reqparamss.length)
+ compatibilityError("number of parameter sections differ")
+
+ if (!hasErrors) {
+ try {
+ for ((rparams, aparams) <- reqparamss zip actparamss) {
+ if (rparams.length < aparams.length)
+ compatibilityError(lengthMsg("found", aparams(rparams.length)))
+ if (aparams.length < rparams.length)
+ compatibilityError(lengthMsg("required", rparams(aparams.length)).abbreviateCoreAliases)
+ }
+ // if the implementation signature is already deemed to be incompatible, we bail out
+ // otherwise, high-order type magic employed below might crash in weird ways
+ if (!hasErrors) {
+ for ((rparams, aparams) <- reqparamss zip actparamss) {
+ for ((rparam, aparam) <- rparams zip aparams) {
+ def isRepeated(param: Symbol) = param.tpe.typeSymbol == RepeatedParamClass
+ if (rparam.name != aparam.name && !rparam.isSynthetic) {
+ val rparam1 = rparam
+ val aparam1 = aparam
+ compatibilityError("parameter names differ: "+rparam.name+" != "+aparam.name)
+ }
+ if (isRepeated(rparam) && !isRepeated(aparam))
+ compatibilityError("types incompatible for parameter "+rparam.name+": corresponding is not a vararg parameter")
+ if (!isRepeated(rparam) && isRepeated(aparam))
+ compatibilityError("types incompatible for parameter "+aparam.name+": corresponding is not a vararg parameter")
+ if (!hasErrors) {
+ var atpe = aparam.tpe.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars)
+
+ // strip the { type PrefixType = ... } refinement off the Context or otherwise we get compatibility errors
+ atpe = atpe match {
+ case RefinedType(List(tpe), Scope(sym)) if tpe == MacroContextClass.tpe && sym.allOverriddenSymbols.contains(MacroContextPrefixType) => tpe
+ case _ => atpe
+ }
+
+ val ok = if (macroDebug) withTypesExplained(rparam.tpe <:< atpe) else rparam.tpe <:< atpe
+ if (!ok) {
+ compatibilityError("type mismatch for parameter "+rparam.name+": "+rparam.tpe.toString.abbreviateCoreAliases+" does not conform to "+atpe)
+ }
+ }
+ }
+ }
+ }
+ if (!hasErrors) {
+ val atpe = actres.substSym(flatactparams, flatreqparams).instantiateTypeParams(tparams, tvars)
+ val ok = if (macroDebug) withTypesExplained(atpe <:< reqres) else atpe <:< reqres
+ if (!ok) {
+ compatibilityError("type mismatch for return type : "+reqres.toString.abbreviateCoreAliases+" does not conform to "+(if (ddef.tpt.tpe != null) atpe.toString else atpe.toString.abbreviateCoreAliases))
+ }
+ }
+ if (!hasErrors) {
+ val targs = solvedTypes(tvars, tparams, tparams map varianceInType(actres), false,
+ lubDepth(flatactparams map (_.tpe)) max lubDepth(flatreqparams map (_.tpe)))
+ val boundsOk = typer.silent(_.infer.checkBounds(ddef, NoPrefix, NoSymbol, tparams, targs, ""))
+ boundsOk match {
+ case SilentResultValue(true) => ;
+ case SilentResultValue(false) | SilentTypeError(_) =>
+ val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds)
+ compatibilityError("type arguments " + targs.mkString("[", ",", "]") +
+ " do not conform to " + tparams.head.owner + "'s type parameter bounds " +
+ (tparams map (_.defString)).mkString("[", ",", "]"))
+ }
+ }
+ } catch {
+ case ex: NoInstance =>
+ compatibilityError(
+ "type parameters "+(tparams map (_.defString) mkString ", ")+" cannot be instantiated\n"+
+ ex.getMessage)
+ }
+ }
+
+ errors.toList
+ }
+
+ var actparamss = macroImpl.paramss
+ actparamss = transformTypeTagEvidenceParams(actparamss, (param, tparam) => None)
+
+ val rettpe = if (ddef.tpt.tpe != null) ddef.tpt.tpe else computeMacroDefTypeFromMacroImpl(ddef, macroDef, macroImpl)
+ val (reqparamsss0, reqres0) = macroImplSigs(macroDef, ddef.tparams, ddef.vparamss, rettpe)
+ var reqparamsss = reqparamsss0
+
+ // prohibit implicit params on macro implementations
+ // we don't have to do this, but it appears to be more clear than allowing them
+ 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)
+ }
+
+ if (!hasErrors) {
+ val reqres = reqres0
+ val actres = macroImpl.tpe.finalResultType
+ def showMeth(pss: List[List[Symbol]], restpe: Type, abbreviate: Boolean) = {
+ var argsPart = (pss map (ps => ps map (_.defString) mkString ("(", ", ", ")"))).mkString
+ if (abbreviate) argsPart = argsPart.abbreviateCoreAliases
+ var retPart = restpe.toString
+ if (abbreviate || ddef.tpt.tpe == null) retPart = retPart.abbreviateCoreAliases
+ argsPart + ": " + retPart
+ }
+ def compatibilityError(addendum: String) =
+ reportError(implpos,
+ "macro implementation has wrong shape:"+
+ "\n required: "+showMeth(reqparamsss.head, reqres, true) +
+ (reqparamsss.tail map (paramss => "\n or : "+showMeth(paramss, reqres, true)) mkString "")+
+ "\n found : "+showMeth(actparamss, actres, false)+
+ "\n"+addendum)
+
+ macroTrace("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) =>
+ println("%s %s".format(if (result.isEmpty) "[ OK ]" else "[FAILED]", reqparamss))
+ result foreach (errorMsg => println(" " + errorMsg))
+ }
+
+ if (results forall (!_.isEmpty)) {
+ var index = reqparamsss indexWhere (_.length == actparamss.length)
+ if (index == -1) index = 0
+ val mostRelevantMessage = results(index).head
+ compatibilityError(mostRelevantMessage)
+ } else {
+ assert((results filter (_.isEmpty)).length == 1, results)
+ if (macroDebug) (reqparamsss zip results) filter (_._2.isEmpty) foreach { case (reqparamss, result) =>
+ println("typechecked macro impl as: " + reqparamss)
+ }
+ }
+ }
+ }
+
+ // if this macro definition is erroneous, then there's no sense in expanding its usages
+ // in the previous prototype macro implementations were magically generated from macro definitions
+ // so macro definitions and its usages couldn't be compiled in the same compilation run
+ // however, now definitions and implementations are decoupled, so it's everything is possible
+ // hence, we now use IS_ERROR flag to serve as an indicator that given macro definition is broken
+ if (hasErrors) {
+ macroDef setFlag IS_ERROR
+ }
+
+ rhs1
}
- def addMacroMethods(templ: Template, namer: Namer): Unit = {
- for (ddef @ DefDef(mods, _, _, _, _, _) <- templ.body if mods hasFlag MACRO) {
- val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value
- val sym = namer.enterSyntheticSym(trace("macro def: ")(macroMethDef(ddef)))
- trace("added to "+namer.context.owner.enclClass+": ")(sym)
+ def computeMacroDefTypeFromMacroImpl(macroDdef: DefDef, macroDef: Symbol, macroImpl: Symbol): Type = {
+ // get return type from method type
+ def unwrapRet(tpe: Type): Type = {
+ def loop(tpe: Type) = tpe match {
+ case NullaryMethodType(ret) => ret
+ case mtpe @ MethodType(_, ret) => unwrapRet(ret)
+ case _ => tpe
+ }
+
+ tpe match {
+ case PolyType(_, tpe) => loop(tpe)
+ case _ => loop(tpe)
+ }
+ }
+ var metaType = unwrapRet(macroImpl.tpe)
+
+ // downgrade from metalevel-0 to metalevel-1
+ def inferRuntimeType(metaType: Type): Type = metaType match {
+ case TypeRef(pre, sym, args) if sym.name == tpnme.Expr && args.length == 1 =>
+ args.head
+ case _ =>
+ AnyClass.tpe
+ }
+ var runtimeType = inferRuntimeType(metaType)
+
+ // transform type parameters of a macro implementation into type parameters of a macro definition
+ runtimeType = runtimeType map {
+ case TypeRef(pre, sym, args) =>
+ // [Eugene] not sure which of these deSkolemizes are necessary
+ // sym.paramPos is unreliable (see another case below)
+ val tparams = macroImpl.typeParams map (_.deSkolemize)
+ val paramPos = tparams indexOf sym.deSkolemize
+ val sym1 = if (paramPos == -1) sym else {
+ val ann = macroDef.getAnnotation(MacroImplAnnotation)
+ ann match {
+ case Some(ann) =>
+ val TypeApply(_, implRefTargs) = ann.args(0)
+ val implRefTarg = implRefTargs(paramPos).tpe.typeSymbol
+ implRefTarg
+ case None =>
+ sym
+ }
+ }
+ TypeRef(pre, sym1, args)
+ case tpe =>
+ tpe
+ }
+
+ // as stated in the spec, before being matched to macroimpl, type and value parameters of macrodef
+ // undergo a special transformation, sigma, that adapts them to the different metalevel macroimpl lives in
+ // as a result, we need to reverse this transformation when inferring macrodef ret from macroimpl ret
+ def unsigma(tpe: Type): Type = {
+ // unfortunately, we cannot dereference ``paramss'', because we're in the middle of inferring a type for ``macroDef''
+// val defParamss = macroDef.paramss
+ val defParamss = macroDdef.vparamss map (_ map (_.symbol))
+ var implParamss = macroImpl.paramss
+ implParamss = transformTypeTagEvidenceParams(implParamss, (param, tparam) => None)
+
+ val implCtxParam = if (implParamss.length > 0 && implParamss(0).length > 0) implParamss(0)(0) else null
+ def implParamToDefParam(implParam: Symbol): Symbol = {
+ val indices = (implParamss drop 1 zipWithIndex) map { case (implParams, index) => (index, implParams indexOf implParam) } filter (_._2 != -1) headOption;
+ val defParam = indices flatMap {
+ case (plistIndex, pIndex) =>
+ if (defParamss.length <= plistIndex) None
+ else if (defParamss(plistIndex).length <= pIndex) None
+ else Some(defParamss(plistIndex)(pIndex))
+ }
+ defParam orNull
+ }
+
+ class UnsigmaTypeMap extends TypeMap {
+ def apply(tp: Type): Type = tp match {
+ case TypeRef(pre, sym, args) =>
+ val pre1 = pre match {
+ case SingleType(SingleType(SingleType(NoPrefix, param), prefix), value) if param == implCtxParam && prefix == MacroContextPrefix && value == ExprValue =>
+ ThisType(macroDef.owner)
+ case SingleType(SingleType(NoPrefix, param), value) if implParamToDefParam(param) != null && value == ExprValue =>
+ val macroDefParam = implParamToDefParam(param)
+ SingleType(NoPrefix, macroDefParam)
+ case _ =>
+ pre
+ }
+ val args1 = args map mapOver
+ TypeRef(pre1, sym, args1)
+ case _ =>
+ mapOver(tp)
+ }
+ }
+
+ new UnsigmaTypeMap() apply tpe
}
+ runtimeType = unsigma(runtimeType)
+
+ runtimeType
}
- 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
- 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) {}
+ /** Primary mirror that is used to resolve and run macro implementations.
+ * Loads classes from -Xmacro-primary-classpath, or from -cp if the option is not specified.
+ */
+ private lazy val primaryMirror: Mirror = {
+ if (global.forMSIL)
+ throw new UnsupportedOperationException("Scala reflection not available on this platform")
+
+ val libraryClassLoader = {
+ if (settings.XmacroPrimaryClasspath.value != "") {
+ if (macroDebug) println("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))
+ val classpath = global.classPath.asURLs
+ var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
+
+ // [Eugene] a 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
}
+ }
+
+ new Mirror(libraryClassLoader) { override def toString = "<primary macro mirror>" }
+ }
- loader
+ /** Fallback mirror that is used to resolve and run macro implementations.
+ * Loads classes from -Xmacro-fallback-classpath aka "macro fallback classpath".
+ */
+ private lazy val fallbackMirror: Mirror = {
+ if (global.forMSIL)
+ 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))
+ val classpath = toURLs(settings.XmacroFallbackClasspath.value)
+ ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
}
- override def defaultReflectiveClassLoader() = libraryClassLoader
+ new Mirror(fallbackClassLoader) { override def toString = "<fallback macro mirror>" }
}
- /** Return optionally address of companion object and implementation method symbol
- * of given macro; or None if implementation classfile cannot be loaded or does
- * not contain the macro implementation.
+ /** Produces a function that can be used to invoke macro implementation for a given macro definition:
+ * 1) Looks up macro implementation symbol in this universe.
+ * 2) Loads its enclosing class from the primary mirror.
+ * 3) Loads the companion of that enclosing class from the primary mirror.
+ * 4) Resolves macro implementation within the loaded companion.
+ * 5) If 2-4 fails, repeats them for the fallback mirror.
+ *
+ * @return Some(runtime) if macro implementation can be loaded successfully from either of the mirrors,
+ * None otherwise.
*/
- def macroImpl(mac: Symbol): Option[(AnyRef, mirror.Symbol)] = {
- val debug = settings.Ymacrodebug.value
- val trace = scala.tools.nsc.util.trace when debug
- trace("looking for macro implementation: ")(mac.fullNameString)
-
- try {
- val mmeth = macroMeth(mac)
- trace("found implementation at: ")(mmeth.fullNameString)
-
- if (mmeth == NoSymbol) None
- else {
- trace("loading implementation class: ")(mmeth.owner.fullName)
- trace("classloader is: ")("%s of type %s".format(mirror.libraryClassLoader, mirror.libraryClassLoader.getClass))
+ 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>"
}
- trace("classpath is: ")(inferClasspath(mirror.libraryClassLoader))
+ macroTrace("classpath is: ")(inferClasspath(macroMirror.classLoader))
- // @xeno.by: relies on the fact that macros can only be defined in static classes
+ // [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 =>
@@ -146,145 +693,535 @@ trait Macros { self: Analyzer =>
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)
+ // [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 (debug) {
- println("receiverClass is: " + receiverClass.fullNameString)
+ if (macroDebug) {
+ println("implClassSymbol is: " + implClassSymbol.fullNameString)
- val jreceiverClass = mirror.classToJava(receiverClass)
- val jreceiverSource = jreceiverClass.getProtectionDomain.getCodeSource
- println("jreceiverClass is %s from %s".format(jreceiverClass, jreceiverSource))
- println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, inferClasspath(jreceiverClass.getClassLoader)))
+ 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)))
+ }
}
- val receiverObj = receiverClass.companionModule
- trace("receiverObj is: ")(receiverObj.fullNameString)
+ val implObjSymbol = implClassSymbol.companionModule
+ macroTrace("implObjSymbol is: ")(implObjSymbol.fullNameString)
- if (receiverObj == mirror.NoSymbol) None
+ if (implObjSymbol == macroMirror.NoSymbol) None
else {
- // @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)
- println("jrmeth is: " + mirror.methodToJava(rmeth))
+ // 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 (rmeth == mirror.NoSymbol) None
+ if (implObj == null) None
else {
- Some((receiver, rmeth))
+ 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 =>
- trace("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 =>
+ None
+ }
+ } else {
+ None
+ }
}
}
- /** Return result of macro expansion.
- * Or, if that fails, and the macro overrides a method return
- * tree that calls this method instead of the macro.
+ /** 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 } =
+ new {
+ val mirror: global.type = global
+ val callsiteTyper: mirror.analyzer.Typer = typer.asInstanceOf[global.analyzer.Typer]
+ // todo. infer precise typetag for this Expr, namely the PrefixType member of the Context refinement
+ 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 */)
+ }
+
+ /** Calculate the arguments to pass to a macro implementation when expanding the provided tree.
+ *
+ * This includes inferring the exact type and instance of the macro context to pass, and also
+ * allowing for missing parameter sections in macro implementation (see ``macroImplParamsss'' for more info).
+ *
+ * @return list of runtime objects to pass to the implementation obtained by ``macroRuntime''
*/
- def macroExpand(tree: Tree, typer: Typer): Option[Any] = {
- val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value
- trace("macroExpand: ")(tree)
-
- val macroDef = tree.symbol
- macroImpl(macroDef) match {
- case Some((receiver, rmeth)) =>
- val argss = List(global) :: macroArgs(tree)
- val paramss = macroMeth(macroDef).paramss
- trace("paramss: ")(paramss)
- val rawArgss = for ((as, ps) <- argss zip paramss) yield {
- if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1)
- else as
- }
- val rawArgs: Seq[Any] = rawArgss.flatten
- trace("rawArgs: ")(rawArgs)
- val savedInfolevel = nodePrinters.infolevel
+ private def macroArgs(typer: Typer, expandee: Tree): Option[List[Any]] = {
+ var prefixTree: Tree = EmptyTree
+ var typeArgs = List[Tree]()
+ val exprArgs = new ListBuffer[List[Expr[_]]]
+ def collectMacroArgs(tree: Tree): Unit = tree match {
+ case Apply(fn, args) =>
+ // todo. infer precise typetag for this Expr, namely the declared type of the corresponding macro impl argument
+ exprArgs.prepend(args map (Expr(_)(TypeTag.Nothing)))
+ collectMacroArgs(fn)
+ case TypeApply(fn, args) =>
+ typeArgs = args
+ collectMacroArgs(fn)
+ case Select(qual, name) =>
+ prefixTree = qual
+ case _ =>
+ }
+ collectMacroArgs(expandee)
+ val context = 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
+ val tparams = macroImpl.typeParams
+ macroTrace("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
+ // nullary def + empty-arglist invocation => illegal Scala code, impossible, everything is okay
+ // empty-paramlist def + nullary invocation => uh-oh, we need to append a List() to argss
+ // empty-paramlist def + empty-arglist invocation => paramss and argss match, everything is okay
+ // that's almost it, but we need to account for the fact that paramss might have context bounds that mask the empty last paramlist
+ val paramss_without_evidences = transformTypeTagEvidenceParams(paramss, (param, tparam) => None)
+ val isEmptyParamlistDef = paramss_without_evidences.length != 0 && paramss_without_evidences.last.isEmpty
+ val isEmptyArglistInvocation = argss.length != 0 && argss.last.isEmpty
+ if (isEmptyParamlistDef && !isEmptyArglistInvocation) {
+ if (macroDebug) println("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss")
+ argss = argss :+ Nil
+ }
+
+ // nb! check partial application against paramss without evidences
+ val numParamLists = paramss_without_evidences.length
+ val numArgLists = argss.length
+ if (numParamLists != numArgLists) {
+ typer.context.error(expandee.pos, "macros cannot be partially applied")
+ return None
+ }
+
+ // if paramss have typetag context bounds, add an arglist to argss if necessary and instantiate the corresponding evidences
+ // consider the following example:
+ //
+ // class D[T] {
+ // class C[U] {
+ // def foo[V] = macro Impls.foo[T, U, V]
+ // }
+ // }
+ //
+ // val outer1 = new D[Int]
+ // val outer2 = new outer1.C[String]
+ // outer2.foo[Boolean]
+ //
+ // then T and U need to be inferred from the lexical scope of the call using ``asSeenFrom''
+ // whereas V won't be resolved by asSeenFrom and need to be loaded directly from ``expandee'' which needs to contain a TypeApply node
+ // also, macro implementation reference may contain a regular type as a type argument, then we pass it verbatim
+ paramss = transformTypeTagEvidenceParams(paramss, (param, tparam) => Some(tparam))
+ if (paramss.lastOption map (params => !params.isEmpty && params.forall(_.isType)) getOrElse false) argss = argss :+ Nil
+ val evidences = paramss.last takeWhile (_.isType) map (tparam => {
+ val TypeApply(_, implRefTargs) = ann.args(0)
+ var implRefTarg = implRefTargs(tparam.paramPos).tpe.typeSymbol
+ val tpe = if (implRefTarg.isTypeParameterOrSkolem) {
+ if (implRefTarg.owner == macroDef) {
+ // [Eugene] doesn't work when macro def is compiled separately from its usages
+ // then implRefTarg is not a skolem and isn't equal to any of macroDef.typeParams
+// val paramPos = implRefTarg.deSkolemize.paramPos
+ val paramPos = macroDef.typeParams.indexWhere(_.name == implRefTarg.name)
+ typeArgs(paramPos).tpe
+ } else
+ implRefTarg.tpe.asSeenFrom(
+ if (prefixTree == EmptyTree) macroDef.owner.tpe else prefixTree.tpe,
+ macroDef.owner)
+ } else
+ implRefTarg.tpe
+ if (macroDebug) println("resolved tparam %s as %s".format(tparam, tpe))
+ tpe
+ }) map (tpe => {
+ val ttag = TypeTag(tpe)
+ if (ttag.isGround) ttag.toGround else ttag
+ })
+ argss = argss.dropRight(1) :+ (evidences ++ argss.last)
+
+ assert(argss.length == paramss.length, "argss: %s, paramss: %s".format(argss, paramss))
+ val rawArgss = for ((as, ps) <- argss zip paramss) yield {
+ if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1)
+ else as
+ }
+ val rawArgs = rawArgss.flatten
+ macroTrace("rawArgs: ")(rawArgs)
+ Some(rawArgs)
+ }
+
+ /** Keeps track of macros in-flight.
+ * See more informations in comments to ``openMacros'' in ``scala.reflect.makro.Context''.
+ */
+ var openMacros = List[MacroContext]()
+
+ /** Performs macro expansion:
+ * 1) Checks whether the expansion needs to be delayed (see ``mustDelayMacroExpansion'')
+ * 2) Loads macro implementation using ``macroMirror''
+ * 3) Synthesizes invocation arguments for the macro implementation
+ * 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
+ * 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,
+ * the expandee unchanged if the expansion has been delayed,
+ * the expandee fully expanded if the expansion has been delayed before and has been expanded now,
+ * 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 {
- // @xeno.by: 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 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
+ 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
}
- } catch {
- case ex =>
- val realex = ReflectionUtils.unwrapThrowable(ex)
- val msg = if (settings.Ymacrodebug.value) {
- val stacktrace = new java.io.StringWriter()
- realex.printStackTrace(new java.io.PrintWriter(stacktrace))
- System.getProperty("line.separator") + stacktrace
- } else {
- realex.getMessage
- }
- typer.context.unit.error(tree.pos, "exception during macro expansion: " + msg)
- None
+ 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 _ => ;
+ }
+
+ var typechecked = typer.context.withImplicitsEnabled(typer.typed(expanded, EXPRmode, expectedTpe))
+ if (macroDebug) {
+ println("typechecked1:")
+ println(typechecked)
+ println(showRaw(typechecked))
+ }
+
+ typechecked = typer.context.withImplicitsEnabled(typer.typed(typechecked, EXPRmode, pt))
+ if (macroDebug) {
+ println("typechecked2:")
+ println(typechecked)
+ println(showRaw(typechecked))
+ }
+
+ typechecked
} finally {
- nodePrinters.infolevel = savedInfolevel
+ openMacros = openMacros.tail
}
- case None =>
- def notFound() = {
- typer.context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name)
- None
- }
- def fallBackToOverridden(tree: Tree): Option[Tree] = {
- tree match {
- case Select(qual, name) if (macroDef.isMacro) =>
- macroDef.allOverriddenSymbols match {
- case first :: _ =>
- Some(Select(qual, name) setPos tree.pos setSymbol first)
+ case Fallback(fallback) =>
+ typer.context.withImplicitsEnabled(typer.typed(fallback, EXPRmode, pt))
+ case Other(result) =>
+ result
+ }
+
+ 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 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))
+ }
+ 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)
+ typer.infer.setError(expandee)
+ Failure(expandee)
+ }
+
+ /** 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))
+ }
+
+ if (!isDelayed(expandee)) {
+ if (macroDebug || macroCopypaste) println("typechecking macro expansion %s at %s".format(expandee, expandee.pos))
+
+ val undetparams = calculateUndetparams(expandee)
+ if (undetparams.size != 0) {
+ macroTrace("macro expansion is delayed: ")(expandee)
+ delayed += expandee -> (typer.context, undetparams)
+ Delay(expandee)
+ } else {
+ 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 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)
+ } 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)
+ }
+ }
+ } 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
+ }
+ } 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 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 _ =>
- trace("macro is not overridden: ")(tree)
+ macroTrace("unexpected tree in fallback: ")(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 _ =>
- trace("unexpected tree in fallback: ")(tree)
- notFound()
- }
- }
- fallBackToOverridden(tree) match {
- case Some(tree1) =>
- trace("falling back to ")(tree1)
- currentRun.macroExpansionFailed = true
- Some(tree1)
- case None =>
- None
+ }
+ fallBackToOverridden(expandee) match {
+ case Some(tree1) =>
+ macroTrace("falling back to ")(tree1)
+ currentRun.macroExpansionFailed = true
+ Fallback(tree1)
+ case None =>
+ fail(typer, expandee)
+ }
}
+ }
+ } else {
+ val undetparams = calculateUndetparams(expandee)
+ if (undetparams.size != 0)
+ Delay(expandee)
+ else
+ Skip(macroExpandAll(typer, expandee))
}
}
+
+ /** Without any restrictions on macro expansion, macro applications will expand at will,
+ * and when type inference is involved, expansions will end up using yet uninferred type params.
+ *
+ * For some macros this might be ok (thanks to TreeTypeSubstituter that replaces
+ * the occurrences of undetparams with their inferred values), but in general case this won't work.
+ * E.g. for reification simple substitution is not enough - we actually need to re-reify inferred types.
+ *
+ * Luckily, there exists a very simple way to fix the problem: delay macro expansion until everything is inferred.
+ * Here are the exact rules. Macro application gets delayed if any of its subtrees contain:
+ * 1) type vars (tpe.isInstanceOf[TypeVar]) // [Eugene] this check is disabled right now, because TypeVars seem to be created from undetparams anyways
+ * 2) undetparams (sym.isTypeParameter && !sym.isSkolem)
+ */
+ var hasPendingMacroExpansions = false
+ private val delayed = perRunCaches.newWeakMap[Tree, (Context, collection.mutable.Set[Int])]
+ 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]()
+ expandee foreach (sub => {
+ def traverse(sym: Symbol) = if (sym != null && (undetparams contains sym.id)) calculated += sym.id
+ if (sub.symbol != null) traverse(sub.symbol)
+ if (sub.tpe != null) sub.tpe foreach (sub => traverse(sub.typeSymbol))
+ })
+ calculated
+ }
+ private val undetparams = perRunCaches.newSet[Int]
+ def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = undetparams ++= newUndets map (_.id)
+ def notifyUndetparamsInferred(undetNoMore: List[Symbol], inferreds: List[Type]): Unit = {
+ undetparams --= undetNoMore map (_.id)
+ 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)
+ }
+ case _ =>
+ // do nothing
+ }
+ }
+
+ /** Performs macro expansion on all subtrees of a given tree.
+ * Innermost macros are expanded first, outermost macros are expanded last.
+ * See the documentation for ``macroExpand'' for more information.
+ */
+ def macroExpandAll(typer: Typer, expandee: Tree): Tree =
+ new Transformer {
+ override def transform(tree: Tree) = super.transform(tree match {
+ // todo. expansion should work from the inside out
+ case wannabe if (delayed contains wannabe) && calculateUndetparams(wannabe).isEmpty =>
+ val (context, _) = delayed(wannabe)
+ delayed -= wannabe
+ macroExpand(newTyper(context), wannabe, WildcardType)
+ case _ =>
+ tree
+ })
+ }.transform(expandee)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 3d8c2ea564..6382e5a847 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -28,6 +28,7 @@ trait MethodSynthesis {
else DefDef(sym, body)
def applyTypeInternal(manifests: List[M[_]]): Type = {
+ // [Eugene to Paul] needs review!!
val symbols = manifests map manifestToSymbol
val container :: args = symbols
val tparams = container.typeConstructor.typeParams
@@ -58,12 +59,13 @@ trait MethodSynthesis {
def newMethodType[F](owner: Symbol)(implicit m: Manifest[F]): Type = {
val fnSymbol = manifestToSymbol(m)
- assert(fnSymbol isSubClass FunctionClass(m.typeArguments.size - 1), (owner, m))
- val symbols = m.typeArguments map (m => manifestToSymbol(m))
- val formals = symbols.init map (_.typeConstructor)
+ assert(fnSymbol isSubClass FunctionClass(m.tpe.typeArguments.size - 1), (owner, m))
+ // [Eugene to Paul] needs review!!
+ // val symbols = m.typeArguments map (m => manifestToSymbol(m))
+ // val formals = symbols.init map (_.typeConstructor)
+ val formals = manifestToType(m).typeArguments
val params = owner newSyntheticValueParams formals
-
- MethodType(params, symbols.last.typeConstructor)
+ MethodType(params, formals.last)
}
}
import synthesisUtil._
@@ -373,7 +375,7 @@ trait MethodSynthesis {
case ExistentialType(_, _) => TypeTree()
case tp => TypeTree(tp)
}
- tpt setPos focusPos(derivedSym.pos)
+ tpt setPos derivedSym.pos.focus
// keep type tree of original abstract field
if (mods.isDeferred)
tpt setOriginal tree.tpt
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 2539091966..696952fe6a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -624,11 +624,6 @@ trait Namers extends MethodSynthesis {
enterCopyMethodOrGetter(tree, tparams)
else
sym setInfo completerOf(tree, tparams)
-
- if (mods hasFlag MACRO) {
- if (!(sym.owner.isClass && sym.owner.isStatic))
- context.error(tree.pos, "macro definition must appear in globally accessible class")
- }
}
def enterClassDef(tree: ClassDef) {
@@ -651,14 +646,6 @@ trait Namers extends MethodSynthesis {
val m = ensureCompanionObject(tree)
classAndNamerOfModule(m) = (tree, null)
}
- val hasMacro = impl.body exists {
- case DefDef(mods, _, _, _, _, _) => mods hasFlag MACRO
- case _ => false
- }
- if (hasMacro) {
- val m = ensureCompanionObject(tree)
- classOfModuleClass(m.moduleClass) = new WeakReference(tree)
- }
val owner = tree.symbol.owner
if (owner.isPackageObjectClass) {
context.unit.warning(tree.pos,
@@ -809,7 +796,9 @@ trait Namers extends MethodSynthesis {
*/
private def assignTypeToTree(tree: ValOrDefDef, defnTyper: Typer, pt: Type): Type = {
// compute result type from rhs
- val typedBody = defnTyper.computeType(tree.rhs, pt)
+ val typedBody =
+ if (tree.symbol.isTermMacro) defnTyper.computeMacroDefType(tree, pt)
+ else defnTyper.computeType(tree.rhs, pt)
val sym = if (owner.isMethod) owner else tree.symbol
val typedDefn = widenIfNecessary(sym, typedBody, pt)
assignTypeToTree(tree, typedDefn)
@@ -871,10 +860,8 @@ trait Namers extends MethodSynthesis {
Namers.this.classOfModuleClass get clazz foreach { cdefRef =>
val cdef = cdefRef()
if (cdef.mods.isCase) addApplyUnapply(cdef, templateNamer)
- if (settings.Xmacros.value) addMacroMethods(cdef.impl, templateNamer)
classOfModuleClass -= clazz
}
- if (settings.Xmacros.value) addMacroMethods(templ, templateNamer)
}
// add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because
@@ -1029,12 +1016,20 @@ trait Namers extends MethodSynthesis {
}
addDefaultGetters(meth, vparamss, tparams, overriddenSymbol)
+ // macro defs need to be typechecked in advance
+ // because @macroImpl annotation only gets assigned during typechecking
+ // otherwise we might find ourselves in the situation when we specified -Xmacro-fallback-classpath
+ // but macros still don't expand
+ // that might happen because macro def doesn't have its link a macro impl yet
+ if (ddef.symbol.isTermMacro) {
+ val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol))
+ typer.computeMacroDefType(ddef, pt)
+ }
+
thisMethodType({
val rt = (
if (!tpt.isEmpty) {
typer.typedType(tpt).tpe
- } else if (meth.isMacro) {
- assignTypeToTree(ddef, AnyClass.tpe)
} else {
// replace deSkolemized symbols with skolemized ones
// (for resultPt computed by looking at overridden symbol, right?)
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 806ee480f0..ad727d4082 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -227,6 +227,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
* 1.8.1 M's type is a subtype of O's type, or
* 1.8.2 M is of type []S, O is of type ()T and S <: T, or
* 1.8.3 M is of type ()S, O is of type []T and S <: T, or
+ * 1.9. If M is a macro def, O cannot be deferred.
+ * 1.10. If M is not a macro def, O cannot be a macro def.
* 2. Check that only abstract classes have deferred members
* 3. Check that concrete classes do not have deferred definitions
* that are not implemented in a subclass.
@@ -416,6 +418,10 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
} else if (other.isValue && other.isLazy && !other.isSourceMethod && !other.isDeferred &&
member.isValue && !member.isLazy) {
overrideError("must be declared lazy to override a concrete lazy value")
+ } else if (other.isDeferred && member.isTermMacro) { // (1.9)
+ overrideError("cannot override an abstract method")
+ } else if (other.isTermMacro && !member.isTermMacro) { // (1.10)
+ overrideError("cannot override a macro")
} else {
checkOverrideTypes()
if (settings.warnNullaryOverride.value) {
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index 3233b7b07c..38c2c5f719 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -456,14 +456,20 @@ trait TypeDiagnostics {
ex match {
case CyclicReference(sym, info: TypeCompleter) =>
- val pos = info.tree match {
- case Import(expr, _) => expr.pos
- case _ => ex.pos
+ if (context0.owner.isTermMacro) {
+ // see comments to TypeSigError for an explanation of this special case
+ // [Eugene] is there a better way?
+ throw ex
+ } else {
+ val pos = info.tree match {
+ case Import(expr, _) => expr.pos
+ case _ => ex.pos
+ }
+ contextError(context0, pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage())
+
+ if (sym == ObjectClass)
+ throw new FatalError("cannot redefine root "+sym)
}
- contextError(context0, pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage())
-
- if (sym == ObjectClass)
- throw new FatalError("cannot redefine root "+sym)
case _ =>
contextError(context0, ex.pos, ex)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index f558e0afc7..1b508a96fe 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -51,6 +51,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
transformed.clear()
}
+ // [Eugene] shouldn't this be converted to resetAllAttrs?
object UnTyper extends Traverser {
override def traverse(tree: Tree) = {
if (tree != EmptyTree) tree.tpe = null
@@ -181,7 +182,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case _ =>
def wrapImplicit(from: Type): Tree = {
val result = inferImplicit(tree, functionType(List(from), to), reportAmbiguous, true, context, saveErrors)
- if (result.subst != EmptyTreeTypeSubstituter) result.subst traverse tree
+ if (result.subst != EmptyTreeTypeSubstituter) {
+ result.subst traverse tree
+ notifyUndetparamsInferred(result.subst.from, result.subst.to)
+ }
result.tree
}
val result = wrapImplicit(from)
@@ -813,7 +817,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case Block(_, tree1) => tree1.symbol
case _ => tree.symbol
}
- if (!meth.isConstructor && !meth.isMacro && isFunctionType(pt)) { // (4.2)
+ if (!meth.isConstructor && !meth.isTermMacro && isFunctionType(pt)) { // (4.2)
debuglog("eta-expanding " + tree + ":" + tree.tpe + " to " + pt)
checkParamsConvertible(tree, tree.tpe)
val tree0 = etaExpand(context.unit, tree)
@@ -1008,12 +1012,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
else TypeApply(tree, tparams1 map (tparam =>
TypeTree(tparam.tpeHK) setPos tree.pos.focus)) setPos tree.pos //@M/tcpolyinfer: changed tparam.tpe to tparam.tpeHK
context.undetparams ++= tparams1
+ notifyUndetparamsAdded(tparams1)
adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original)
case mt: MethodType if mt.isImplicit && ((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) => // (4.1)
adaptToImplicitMethod(mt)
case mt: MethodType if (((mode & (EXPRmode | FUNmode | LHSmode)) == EXPRmode) &&
- (context.undetparams.isEmpty || inPolyMode(mode))) =>
+ (context.undetparams.isEmpty || inPolyMode(mode))) && !(tree.symbol != null && tree.symbol.isTermMacro) =>
instantiateToMethodType(mt)
case _ =>
@@ -1026,13 +1031,10 @@ 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 && !(tree exists (_.isErroneous)))
- macroExpand(tree, this) match {
- case Some(expanded: Tree) =>
- typed(expanded, mode, pt)
- case None =>
- setError(tree) // error already reported
- }
+ 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 ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode))
adaptConstrPattern()
else if (inAllModes(mode, EXPRmode | FUNmode) &&
@@ -1906,8 +1908,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
meth.owner.isAnonOrRefinementClass))
InvalidConstructorDefError(ddef)
typed(ddef.rhs)
- } else if (meth.isMacro) {
- EmptyTree
+ } else if (meth.isTermMacro) {
+ // typechecking macro bodies is sort of unconventional
+ // that's why we employ our custom typing scheme orchestrated outside of the typer
+ transformedOr(ddef.rhs, typedMacroBody(this, ddef))
} else {
transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe)
}
@@ -2212,7 +2216,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
(ps, sel)
case Some((vparams, sel)) =>
val newParamSyms = (vparams, formals).zipped map {(p, tp) =>
- methodSym.newValueParameter(p.name, focusPos(p.pos), SYNTHETIC) setInfo tp
+ methodSym.newValueParameter(p.name, p.pos.focus, SYNTHETIC) setInfo tp
}
(newParamSyms, sel.duplicate)
@@ -2267,7 +2271,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
else {
// applyOrElse's default parameter:
val B1 = methodSym newTypeParameter(newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp)
- val default = methodSym newValueParameter(newTermName("default"), focusPos(tree.pos), SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe)
+ val default = methodSym newValueParameter(newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe)
val paramSyms = List(x, default)
methodSym setInfoAndEnter polyType(List(A1, B1), MethodType(paramSyms, B1.tpe))
@@ -2489,7 +2493,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
var e1 = scope.lookupNextEntry(e)
while ((e1 ne null) && e1.owner == scope) {
if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) &&
- (e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe) || e.sym.isMacro && e1.sym.isMacro))
+ (e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe)))
// default getters are defined twice when multiple overloads have defaults. an
// error for this is issued in RefChecks.checkDefaultsInOverloaded
if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefault &&
@@ -2575,7 +2579,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
else if (isByNameParamType(formals.head)) 0
else BYVALmode
)
- val tree = typedArg(args.head, mode, typedMode, adapted.head)
+ var tree = typedArg(args.head, mode, typedMode, adapted.head)
+ if (hasPendingMacroExpansions) tree = macroExpandAll(this, tree)
// formals may be empty, so don't call tail
tree :: loop(args.tail, formals drop 1, adapted.tail)
}
@@ -2737,6 +2742,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def tryNamesDefaults: Tree = {
val lencmp = compareLengths(args, formals)
+ def checkNotMacro() = {
+ if (fun.symbol != null && fun.symbol.filter(sym => sym != null && sym.isTermMacro) != NoSymbol)
+ duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun))
+ }
+
if (mt.isErroneous) duplErrTree
else if (inPatternMode(mode)) {
// #2064
@@ -2755,8 +2765,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
else if (isIdentity(argPos) && !isNamedApplyBlock(fun)) {
// if there's no re-ordering, and fun is not transformed, no need to transform
// more than an optimization, e.g. important in "synchronized { x = update-x }"
+ checkNotMacro()
doTypedApply(tree, fun, namelessArgs, mode, pt)
} else {
+ checkNotMacro()
transformNamedApplication(Typer.this, mode, pt)(
treeCopy.Apply(tree, fun, namelessArgs), argPos)
}
@@ -2764,6 +2776,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// defaults are needed. they are added to the argument list in named style as
// calls to the default getters. Example:
// foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a))
+ checkNotMacro()
val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x)
if (fun1.isErroneous) duplErrTree
else {
@@ -3111,7 +3124,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
if (hasError) annotationError
- else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(ann).setPos(ann.pos)
+ else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(Apply(typedFun, args)).setPos(ann.pos)
}
} else if (requireJava) {
reportAnnotationError(NestedAnnotationError(ann, annType))
@@ -3151,7 +3164,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def annInfo(t: Tree): AnnotationInfo = t match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
- AnnotationInfo(annType, args, List()).setOriginal(ann).setPos(t.pos)
+ AnnotationInfo(annType, args, List()).setOriginal(typedAnn).setPos(t.pos)
case Block(stats, expr) =>
context.warning(t.pos, "Usage of named or default arguments transformed this annotation\n"+
@@ -3437,7 +3450,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
println(s)
}
- protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
+ def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
def isPatternMode = inPatternMode(mode)
//Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")")
@@ -3451,10 +3464,27 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
def typedAnnotated(ann: Tree, arg1: Tree): Tree = {
- def mkTypeTree(tpe: Type) = TypeTree(tpe) setOriginal tree setPos tree.pos.focus
/** mode for typing the annotation itself */
val annotMode = mode & ~TYPEmode | EXPRmode
+ def resultingTypeTree(tpe: Type) = {
+ // we need symbol-ful originals for reification
+ // hence we go the extra mile to hand-craft tis guy
+ val original =
+ if (arg1.isType)
+ (tree, arg1) match {
+ case (Annotated(annot, arg), tt @ TypeTree()) => Annotated(annot, tt.original)
+ // this clause is needed to correctly compile stuff like "new C @D" or "@(inline @getter)"
+ case (Annotated(annot, arg), _) => Annotated(annot, arg1)
+ case _ => throw new Error("unexpected trees in typedAnnotated: tree = %s, arg1 = %s".format(showRaw(tree), showRaw(arg1)))
+ }
+ else
+ tree
+ original setType ann.tpe
+ original setPos tree.pos.focus
+ TypeTree(tpe) setOriginal original setPos tree.pos.focus
+ }
+
if (arg1.isType) {
// make sure the annotation is only typechecked once
if (ann.tpe == null) {
@@ -3497,11 +3527,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
arg1 // simply drop erroneous annotations
else {
ann.tpe = atype
- mkTypeTree(atype)
+ resultingTypeTree(atype)
}
} else {
// the annotation was typechecked before
- mkTypeTree(ann.tpe)
+ resultingTypeTree(ann.tpe)
}
}
else {
@@ -3510,7 +3540,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
ann.tpe = arg1.tpe.withAnnotation(annotInfo)
}
val atype = ann.tpe
- Typed(arg1, mkTypeTree(atype)) setPos tree.pos setType atype
+ Typed(arg1, resultingTypeTree(atype)) setPos tree.pos.focus setType atype
}
}
@@ -3676,6 +3706,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (checkStablePrefixClassType(tpt0))
if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) {
context.undetparams = cloneSymbols(tpt0.symbol.typeParams)
+ notifyUndetparamsAdded(context.undetparams)
TypeTree().setOriginal(tpt0)
.setType(appliedType(tpt0.tpe, context.undetparams map (_.tpeHK))) // @PP: tpeHK! #3343, #4018, #4347.
} else tpt0
@@ -4534,7 +4565,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
typedNew(tpt)
case Typed(expr, Function(List(), EmptyTree)) =>
- typedEta(checkDead(typed1(expr, mode, pt)))
+ // find out whether the programmer is trying to eta-expand a macro def
+ // to do that we need to typecheck the tree first (we need a symbol of the eta-expandee)
+ // that typecheck must not trigger macro expansions, so we explicitly prohibit them
+ // Q: "but, " - you may ask - ", `typed1` doesn't call adapt, which does macro expansion, so why explicit check?"
+ // A: solely for robustness reasons. this mechanism might change in the future, which might break unprotected code
+ val expr1 = context.withMacrosDisabled(typed1(expr, mode, pt))
+ expr1 match {
+ case macroDef if macroDef.symbol.isTermMacro =>
+ MacroEtaError(expr1)
+ case _ =>
+ typedEta(checkDead(expr1))
+ }
case Typed(expr0, tpt @ Ident(tpnme.WILDCARD_STAR)) =>
val expr = typed(expr0, onlyStickyModes(mode), WildcardType)
@@ -4608,18 +4650,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
tpt.tpe.typeSymbol == ArrayClass &&
args.length == 1 &&
erasure.GenericArray.unapply(tpt.tpe).isDefined) => // !!! todo simplify by using extractor
- // convert new Array[T](len) to evidence[ClassManifest[T]].newArray(len)
- // convert new Array^N[T](len) for N > 1 to evidence[ClassManifest[T]].newArrayN(len)
- val Some((level, manifType)) = erasure.GenericArray.unapply(tpt.tpe)
- if (level > MaxArrayDims)
- MultiDimensionalArrayError(tree)
- else {
- val newArrayApp = atPos(tree.pos) {
- val manif = getManifestTree(tree, manifType, false)
- new ApplyToImplicitArgs(Select(manif, if (level == 1) "newArray" else "newArray"+level), args)
- }
- typed(newArrayApp, mode, pt)
+ // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len)
+ // convert new Array^N[T](len) for N > 1 to evidence[ClassTag[Array[...Array[T]...]]].newArray(len), where Array HK gets applied (N-1) times
+ // [Eugene] no more MaxArrayDims. ClassTags are flexible enough to allow creation of arrays of arbitrary dimensionality (w.r.t JVM restrictions)
+ val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe)
+ val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.asType, List(tpe))).last
+ val newArrayApp = atPos(tree.pos) {
+ val tag = resolveClassTag(tree, tagType)
+ if (tag.isEmpty) MissingClassTagError(tree, tagType)
+ else new ApplyToImplicitArgs(Select(tag, nme.newArray), args)
}
+ typed(newArrayApp, mode, pt)
case tree1 =>
tree1
}
@@ -4679,7 +4720,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case ReferenceToBoxed(idt @ Ident(_)) =>
val id1 = typed1(idt, mode, pt) match { case id: Ident => id }
- treeCopy.ReferenceToBoxed(tree, id1) setType AnyRefClass.tpe
+ // [Eugene] am I doing it right?
+ val erasedTypes = phaseId(currentPeriod) >= currentRun.erasurePhase.id
+ val tpe = capturedVariableType(idt.symbol, erasedTypes = erasedTypes)
+ treeCopy.ReferenceToBoxed(tree, id1) setType tpe
case Literal(value) =>
tree setType (
@@ -4916,31 +4960,75 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def typedTypeConstructor(tree: Tree): Tree = typedTypeConstructor(tree, NOmode)
def computeType(tree: Tree, pt: Type): Type = {
+ // macros employ different logic of `computeType`
+ assert(!context.owner.isTermMacro, context.owner)
val tree1 = typed(tree, pt)
transformed(tree) = tree1
packedType(tree1, context.owner)
}
+ def computeMacroDefType(tree: Tree, pt: Type): Type = {
+ assert(context.owner.isTermMacro, context.owner)
+ assert(tree.symbol.isTermMacro, tree.symbol)
+ assert(tree.isInstanceOf[DefDef], tree.getClass)
+ val ddef = tree.asInstanceOf[DefDef]
+
+ val tree1 =
+ if (transformed contains ddef.rhs) {
+ // macro defs are typechecked in `methodSig` (by calling this method) in order to establish their link to macro implementation asap
+ // if a macro def doesn't have explicitly specified return type, this method will be called again by `assignTypeToTree`
+ // here we guard against this case
+ transformed(ddef.rhs)
+ } else {
+ val tree1 = typedMacroBody(this, ddef)
+ transformed(ddef.rhs) = tree1
+ tree1
+ }
+
+ val isMacroBodyOkay = !tree.symbol.isErroneous && !(tree1 exists (_.isErroneous))
+ if (isMacroBodyOkay) computeMacroDefTypeFromMacroImpl(ddef, tree.symbol, tree1.symbol) else AnyClass.tpe
+ }
+
+ def transformedOr(tree: Tree, op: => Tree): Tree = transformed.get(tree) match {
+ case Some(tree1) => transformed -= tree; tree1
+ case None => op
+ }
+
def transformedOrTyped(tree: Tree, mode: Int, pt: Type): Tree = transformed.get(tree) match {
case Some(tree1) => transformed -= tree; tree1
case None => typed(tree, mode, pt)
}
- def findManifest(tp: Type, full: Boolean) = beforeTyper {
+ // `tree` is only necessary here for its position
+ // but that's invaluable for error reporting, so I decided to include it into this method's contract
+ // before passing EmptyTree, please, consider passing something meaningful first
+ def resolveClassTag(tree: Tree, tp: Type): Tree = beforeTyper {
inferImplicit(
EmptyTree,
- appliedType((if (full) FullManifestClass else PartialManifestClass).typeConstructor, List(tp)),
- true, false, context)
+ appliedType(ClassTagClass.typeConstructor, List(tp)),
+ /*reportAmbiguous =*/ true,
+ /*isView =*/ false,
+ /*context =*/ context,
+ /*saveAmbiguousDivergent =*/ true,
+ /*pos =*/ tree.pos
+ ).tree
}
- def getManifestTree(tree: Tree, tp: Type, full: Boolean): Tree = {
- val manifestOpt = findManifest(tp, full)
- if (manifestOpt.tree.isEmpty) {
- MissingManifestError(tree, full, tp)
- } else {
- manifestOpt.tree
- }
+ // `tree` is only necessary here for its position
+ // but that's invaluable for error reporting, so I decided to include it into this method's contract
+ // before passing EmptyTree, please, consider passing something meaningful first
+ def resolveTypeTag(tree: Tree, pre: Type, tp: Type, full: Boolean): Tree = beforeTyper {
+ inferImplicit(
+ EmptyTree,
+ appliedType(singleType(pre, pre member (if (full) GroundTypeTagClass else TypeTagClass).name), List(tp)),
+ /*reportAmbiguous =*/ true,
+ /*isView =*/ false,
+ /*context =*/ context,
+ /*saveAmbiguousDivergent =*/ true,
+ /*pos =*/ tree.pos
+ ).tree
}
+
/*
def convertToTypeTree(tree: Tree): Tree = tree match {
case TypeTree() => tree
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala
index ce10ee34a2..11d7db5180 100644
--- a/src/compiler/scala/tools/nsc/util/ClassPath.scala
+++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala
@@ -27,7 +27,7 @@ object ClassPath {
def scalaCompiler = locate[Global]
def infoFor[T](value: T) = info(value.getClass)
- def info[T](clazz: Class[T]) = new ClassAndJarInfo()(ClassManifest fromClass clazz)
+ def info[T](clazz: Class[T]) = new ClassAndJarInfo()(ClassManifest[T](clazz))
def info[T: ClassManifest] = new ClassAndJarInfo[T]
def locate[T: ClassManifest] = info[T] rootClasspath
def locateJar[T: ClassManifest] = info[T].rootPossibles find (x => isJarOrZip(x)) map (x => File(x))
diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala
index bc74717366..573f7bc7b2 100644
--- a/src/compiler/scala/tools/nsc/util/Position.scala
+++ b/src/compiler/scala/tools/nsc/util/Position.scala
@@ -33,62 +33,24 @@ object Position {
}
}
-/**
- * A tree does not directly store a Position. It stores a TreeAnnotation, which /typically/ is a Position.
- *
- * A TreeAnnotion may encompass more than just a Position, though, depending on the exact subclass of TreeAnnotation.
- */
-trait TreeAnnotation {
- def pos: Position
-}
+trait Position extends scala.reflect.api.Position with scala.reflect.api.Attachment {
+ /** Exposes itself as payload of Attachment */
+ // necessary for conformance with Attachment
+ def pos: Position = this
+ /** A bit weird method that is necessary to safely update positions without destroying custom attachments */
+ // necessary for conformance with Attachment
+ def withPos(pos: scala.reflect.api.Position) = pos
-/** The Position class and its subclasses represent positions of ASTs and symbols.
- * Except for NoPosition and FakePos, every position refers to a SourceFile
- * and to an offset in the sourcefile (its `point`). For batch compilation,
- * that's all. For interactive IDE's there are also RangePositions
- * and TransparentPositions. A RangePosition indicates a start and an end
- * in addition to its point. TransparentPositions are a subclass of RangePositions.
- * Range positions that are not transparent are called opaque.
- * Trees with RangePositions need to satisfy the following invariants.
- *
- * INV1: A tree with an offset position never contains a child
- * with a range position
- * INV2: If the child of a tree with a range position also has a range position,
- * then the child's range is contained in the parent's range.
- * INV3: Opaque range positions of children of the same node are non-overlapping
- * (this means their overlap is at most a single point).
- *
- * The following tests are useful on positions:
- *
- * pos.isDefined true if position is not a NoPosition nor a FakePosition
- * pos.isRange true if position is a range
- * pos.isOpaqueRange true if position is an opaque range
- *
- * The following accessor methods are provided:
- *
- * pos.source The source file of the position, which must be defined
- * pos.point The offset of the position's point, which must be defined
- * pos.start The start of the position, which must be a range
- * pos.end The end of the position, which must be a range
- *
- * There are also convenience methods, such as
- *
- * pos.startOrPoint
- * pos.endOrPoint
- * pos.pointOrElse(default)
- *
- * These are less strict about the kind of position on which they can be applied.
- *
- * The following conversion methods are often used:
- *
- * pos.focus converts a range position to an offset position, keeping its point;
- * returns all other positions unchanged.
- * pos.makeTransparent converts an opaque range position into a transparent one.
- * returns all other positions unchanged.
- */
-trait Position extends TreeAnnotation {
- def pos: Position = this
+ /** Java file corresponding to the source file of this position.
+ */
+ // necessary for conformance with scala.reflect.api.Position
+ def fileInfo: java.io.File = source.file.file
+
+ /** Contents of the source file that contains this position.
+ */
+ // necessary for conformance with scala.reflect.api.Position
+ def fileContent: Array[Char] = source.content
/** An optional value containing the source file referred to by this position, or
* None if not defined.
@@ -134,74 +96,74 @@ trait Position extends TreeAnnotation {
def offset: Option[Int] = if (isDefined) Some(point) else None
/** The same position with a different start value (if a range) */
- def withStart(off: Int) = this
+ def withStart(off: Int): Position = this
/** The same position with a different end value (if a range) */
- def withEnd(off: Int) = this
+ def withEnd(off: Int): Position = this
/** The same position with a different point value (if a range or offset) */
- def withPoint(off: Int) = this
+ def withPoint(off: Int): Position = this
/** The same position with a different source value, and its values shifted by given offset */
- def withSource(source: SourceFile, shift: Int) = this
+ def withSource(source: SourceFile, shift: Int): Position = this
/** If this is a range, the union with the other range, with the point of this position.
* Otherwise, this position
*/
- def union(pos: Position) = this
+ def union(pos: scala.reflect.api.Position): Position = this
/** If this is a range position, the offset position of its start.
* Otherwise the position itself
*/
- def focusStart = this
+ def focusStart: Position = this
/** If this is a range position, the offset position of its point.
* Otherwise the position itself
*/
- def focus = this
+ def focus: Position = this
/** If this is a range position, the offset position of its end.
* Otherwise the position itself
*/
- def focusEnd = this
+ def focusEnd: Position = this
/** Does this position include the given position `pos`.
* This holds if `this` is a range position and its range [start..end]
* is the same or covers the range of the given position, which may or may not be a range position.
*/
- def includes(pos: Position) = false
+ def includes(pos: scala.reflect.api.Position): Boolean = false
/** Does this position properly include the given position `pos` ("properly" meaning their
* ranges are not the same)?
*/
- def properlyIncludes(pos: Position) =
+ def properlyIncludes(pos: scala.reflect.api.Position): Boolean =
includes(pos) && (start < pos.startOrPoint || pos.endOrPoint < end)
/** Does this position precede that position?
* This holds if both positions are defined and the end point of this position
* is not larger than the start point of the given position.
*/
- def precedes(pos: Position) =
+ def precedes(pos: scala.reflect.api.Position): Boolean =
isDefined && pos.isDefined && endOrPoint <= pos.startOrPoint
/** Does this position properly precede the given position `pos` ("properly" meaning their ranges
* do not share a common point).
*/
- def properlyPrecedes(pos: Position) =
+ def properlyPrecedes(pos: scala.reflect.api.Position): Boolean =
isDefined && pos.isDefined && endOrPoint < pos.startOrPoint
/** Does this position overlap with that position?
* This holds if both positions are ranges and there is an interval of
* non-zero length that is shared by both position ranges.
*/
- def overlaps(pos: Position) =
+ def overlaps(pos: scala.reflect.api.Position): Boolean =
isRange && pos.isRange &&
((pos.start < end && start < pos.end) || (start < pos.end && pos.start < end))
/** Does this position cover the same range as that position?
* Holds only if both position are ranges
*/
- def sameRange(pos: Position) =
+ def sameRange(pos: scala.reflect.api.Position): Boolean =
isRange && pos.isRange && start == pos.start && end == pos.end
def line: Int = throw new UnsupportedOperationException("Position.line")
@@ -219,11 +181,11 @@ trait Position extends TreeAnnotation {
* file. If the SourceFile is a normal SourceFile, simply
* return this.
*/
- def inUltimateSource(source : SourceFile) =
+ def inUltimateSource(source : SourceFile): Position =
if (source == null) this else source.positionInUltimateSource(this)
- def dbgString = toString
- def safeLine = try line catch { case _: UnsupportedOperationException => -1 }
+ def dbgString: String = toString
+ def safeLine: Int = try line catch { case _: UnsupportedOperationException => -1 }
def show: String = "["+toString+"]"
}
@@ -254,8 +216,10 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e
col + 1
}
- override def union(pos: Position) =
- if (pos.isRange) pos else this
+ override def union(pos: scala.reflect.api.Position) =
+ // [Eugene] how do I get rid of this cast?
+ // I could introduce a "type PositionType <: scala.reflect.api.Position", but that's also ugly
+ if (pos.isRange) pos.asInstanceOf[Position] else this
override def equals(that : Any) = that match {
case that : OffsetPosition => point == that.point && source.file == that.source.file
@@ -265,7 +229,7 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e
override def toString = {
val pointmsg = if (point > source.length) "out-of-bounds-" else "offset="
- "source-%s,line-%s,%s%s".format(source.path, line, pointmsg, point)
+ "source-%s,line-%s,%s%s".format(source.file.canonicalPath, line, pointmsg, point)
}
override def show = "["+point+"]"
}
@@ -289,8 +253,8 @@ extends OffsetPosition(source, point) {
}
override def focusEnd = new OffsetPosition(source, end)
override def makeTransparent = new TransparentPosition(source, start, point, end)
- override def includes(pos: Position) = pos.isDefined && start <= pos.startOrPoint && pos.endOrPoint <= end
- override def union(pos: Position) =
+ override def includes(pos: scala.reflect.api.Position) = pos.isDefined && start <= pos.startOrPoint && pos.endOrPoint <= end
+ override def union(pos: scala.reflect.api.Position): Position =
if (pos.isRange) new RangePosition(source, start min pos.start, point, end max pos.end) else this
override def toSingleLine: Position = source match {
@@ -301,7 +265,7 @@ extends OffsetPosition(source, point) {
case _ => this
}
- override def toString = "RangePosition("+source+", "+start+", "+point+", "+end+")"
+ override def toString = "RangePosition("+source.file.canonicalPath+", "+start+", "+point+", "+end+")"
override def show = "["+start+":"+end+"]"
private var focusCache: Position = NoPosition
}
@@ -311,10 +275,4 @@ class TransparentPosition(source: SourceFile, start: Int, point: Int, end: Int)
override def isTransparent = true
override def makeTransparent = this
override def show = "<"+start+":"+end+">"
-}
-
-
-
-
-
-
+} \ No newline at end of file