summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/Global.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/Global.scala')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala1300
1 files changed, 585 insertions, 715 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index aea3e0d930..1c9dbad4dd 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -3,22 +3,23 @@
* @author Martin Odersky
*/
-package scala.tools.nsc
+package scala
+package tools
+package nsc
import java.io.{ File, FileOutputStream, PrintWriter, IOException, FileNotFoundException }
+import java.net.URL
import java.nio.charset.{ Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException }
import scala.compat.Platform.currentTime
-import scala.tools.util.PathResolver
import scala.collection.{ mutable, immutable }
import io.{ SourceReader, AbstractFile, Path }
import reporters.{ Reporter, ConsoleReporter }
-import util.{ Exceptional, ClassPath, MergedClassPath, StatisticsInfo, ScalaClassLoader, returning }
-import scala.reflect.internal.util.{ NoPosition, OffsetPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile }
-import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat }
-import settings.{ AestheticSettings }
-import symtab.{ Flags, SymbolTable, SymbolLoaders, SymbolTrackers }
+import util.{ ClassFileLookup, ClassPath, MergedClassPath, StatisticsInfo, returning }
+import scala.reflect.ClassTag
+import scala.reflect.internal.util.{ SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile }
+import scala.reflect.internal.pickling.PickleBuffer
+import symtab.{ Flags, SymbolTable, SymbolTrackers }
import symtab.classfile.Pickler
-import dependencies.DependencyAnalysis
import plugins.Plugins
import ast._
import ast.parser._
@@ -26,13 +27,15 @@ import typechecker._
import transform.patmat.PatternMatching
import transform._
import backend.icode.{ ICodes, GenICode, ICodeCheckers }
-import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform }
-import backend.jvm.{GenJVM, GenASM}
-import backend.opt.{ Inliners, InlineExceptionHandlers, ClosureElimination, DeadCodeElimination }
+import backend.{ ScalaPrimitives, JavaPlatform }
+import backend.jvm.GenBCode
+import backend.jvm.GenASM
+import backend.opt.{ Inliners, InlineExceptionHandlers, ConstantOptimization, ClosureElimination, DeadCodeElimination }
import backend.icode.analysis._
import scala.language.postfixOps
-import scala.reflect.internal.StdAttachments
-import scala.reflect.ClassTag
+import scala.tools.nsc.ast.{TreeGen => AstTreeGen}
+import scala.tools.nsc.classpath.FlatClassPath
+import scala.tools.nsc.settings.ClassPathRepresentationType
class Global(var currentSettings: Settings, var reporter: Reporter)
extends SymbolTable
@@ -42,17 +45,29 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
with Trees
with Printers
with DocComments
- with Positions { self =>
+ with Positions
+ with Reporting
+ with Parsing { self =>
// the mirror --------------------------------------------------
override def isCompilerUniverse = true
+ override val useOffsetPositions = !currentSettings.Yrangepos
+
+ type RuntimeClass = java.lang.Class[_]
+ implicit val RuntimeClassTag: ClassTag[RuntimeClass] = ClassTag[RuntimeClass](classOf[RuntimeClass])
class GlobalMirror extends Roots(NoSymbol) {
val universe: self.type = self
- def rootLoader: LazyType = platform.rootLoader
+ def rootLoader: LazyType = {
+ settings.YclasspathImpl.value match {
+ case ClassPathRepresentationType.Flat => new loaders.PackageLoaderUsingFlatClassPath(FlatClassPath.RootPackage, flatClassPath)
+ case ClassPathRepresentationType.Recursive => new loaders.PackageLoader(recursiveClassPath)
+ }
+ }
override def toString = "compiler mirror"
}
+ implicit val MirrorTag: ClassTag[Mirror] = ClassTag[Mirror](classOf[GlobalMirror])
lazy val rootMirror: Mirror = {
val rm = new GlobalMirror
@@ -69,51 +84,56 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
override def settings = currentSettings
+ /** Switch to turn on detailed type logs */
+ var printTypings = settings.Ytyperdebug.value
+
def this(reporter: Reporter) =
this(new Settings(err => reporter.error(null, err)), reporter)
def this(settings: Settings) =
this(settings, new ConsoleReporter(settings))
- def mkAttributedQualifier(tpe: Type, termSym: Symbol): Tree = gen.mkAttributedQualifier(tpe, termSym)
-
def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase
+ def erasurePhase: Phase = if (currentRun.isDefined) currentRun.erasurePhase else NoPhase
+
// platform specific elements
- type ThisPlatform = Platform { val global: Global.this.type }
+ protected class GlobalPlatform extends {
+ val global: Global.this.type = Global.this
+ val settings: Settings = Global.this.settings
+ } with JavaPlatform
- lazy val platform: ThisPlatform =
- if (forMSIL) new { val global: Global.this.type = Global.this } with MSILPlatform
- else new { val global: Global.this.type = Global.this } with JavaPlatform
+ type ThisPlatform = JavaPlatform { val global: Global.this.type }
+ lazy val platform: ThisPlatform = new GlobalPlatform
- type PlatformClassPath = ClassPath[platform.BinaryRepr]
+ type PlatformClassPath = ClassPath[AbstractFile]
type OptClassPath = Option[PlatformClassPath]
- def classPath: PlatformClassPath = platform.classPath
+ def classPath: ClassFileLookup[AbstractFile] = settings.YclasspathImpl.value match {
+ case ClassPathRepresentationType.Flat => flatClassPath
+ case ClassPathRepresentationType.Recursive => recursiveClassPath
+ }
- // sub-components --------------------------------------------------
+ private def recursiveClassPath: ClassPath[AbstractFile] = platform.classPath
- /** Generate ASTs */
- type TreeGen = scala.tools.nsc.ast.TreeGen
+ private def flatClassPath: FlatClassPath = platform.flatClassPath
+
+ // sub-components --------------------------------------------------
/** Tree generation, usually based on existing symbols. */
override object gen extends {
val global: Global.this.type = Global.this
- } with TreeGen {
+ } with AstTreeGen {
def mkAttributedCast(tree: Tree, pt: Type): Tree =
typer.typed(mkCast(tree, pt))
}
- /** Trees fresh from the oven, mostly for use by the parser. */
- object treeBuilder extends {
- val global: Global.this.type = Global.this
- } with TreeBuilder {
- def freshName(prefix: String): Name = freshTermName(prefix)
- def freshTermName(prefix: String): TermName = currentUnit.freshTermName(prefix)
- def freshTypeName(prefix: String): TypeName = currentUnit.freshTypeName(prefix)
- def o2p(offset: Int): Position = new OffsetPosition(currentUnit.source, offset)
- def r2p(start: Int, mid: Int, end: Int): Position = rangePos(currentUnit.source, start, mid, end)
+ /** A spare instance of TreeBuilder left for backwards compatibility. */
+ lazy val treeBuilder: TreeBuilder { val global: Global.this.type } = new TreeBuilder {
+ val global: Global.this.type = Global.this;
+ def unit = currentUnit
+ def source = currentUnit.source
}
/** Fold constants */
@@ -136,6 +156,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val global: Global.this.type = Global.this
} with OverridingPairs
+ type SymbolPair = overridingPairs.SymbolPair
+
// Optimizer components
/** ICode analysis for optimization */
@@ -173,7 +195,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
if (lastPrintedSource == source)
println(": tree is unchanged since " + lastPrintedPhase)
else {
- lastPrintedPhase = phase.prev // since we're running inside "afterPhase"
+ lastPrintedPhase = phase.prev // since we're running inside "exitingPhase"
lastPrintedSource = source
println("")
println(source)
@@ -210,6 +232,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** Called from parser, which signals hereby that a method definition has been parsed. */
def signalParseProgress(pos: Position) {}
+ /** Called by ScalaDocAnalyzer when a doc comment has been parsed. */
+ def signalParsedDocComment(comment: String, pos: Position) = {
+ // TODO: this is all very broken (only works for scaladoc comments, not regular ones)
+ // --> add hooks to parser and refactor Interactive global to handle comments directly
+ // in any case don't use reporter for parser hooks
+ reporter.comment(pos, comment)
+ }
+
/** Register new context; called for every created context
*/
def registerContext(c: analyzer.Context) {
@@ -220,65 +250,53 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
*/
def registerTopLevelSym(sym: Symbol) {}
-// ------------------ Reporting -------------------------------------
-
- // not deprecated yet, but a method called "error" imported into
- // nearly every trait really must go. For now using globalError.
- def error(msg: String) = globalError(msg)
- def inform(msg: String) = reporter.echo(msg)
- override def globalError(msg: String) = reporter.error(NoPosition, msg)
- override def warning(msg: String) =
- if (settings.fatalWarnings.value) globalError(msg)
- else reporter.warning(NoPosition, msg)
+// ------------------ Debugging -------------------------------------
// Getting in front of Predef's asserts to supplement with more info.
// This has the happy side effect of masking the one argument forms
// of assert and require (but for now I've reproduced them here,
// because there are a million to fix.)
@inline final def assert(assertion: Boolean, message: => Any) {
- Predef.assert(assertion, supplementErrorMessage("" + message))
+ // calling Predef.assert would send a freshly allocated closure wrapping the one received as argument.
+ if (!assertion)
+ throw new java.lang.AssertionError("assertion failed: "+ supplementErrorMessage("" + message))
}
@inline final def assert(assertion: Boolean) {
assert(assertion, "")
}
@inline final def require(requirement: Boolean, message: => Any) {
- Predef.require(requirement, supplementErrorMessage("" + message))
+ // calling Predef.require would send a freshly allocated closure wrapping the one received as argument.
+ if (!requirement)
+ throw new IllegalArgumentException("requirement failed: "+ supplementErrorMessage("" + message))
}
@inline final def require(requirement: Boolean) {
require(requirement, "")
}
- // Needs to call error to make sure the compile fails.
- override def abort(msg: String): Nothing = {
- error(msg)
- super.abort(msg)
- }
-
@inline final def ifDebug(body: => Unit) {
- if (settings.debug.value)
+ if (settings.debug)
body
}
- // Warnings issued only under -Ydebug. For messages which should reach
- // developer ears, but are not adequately actionable by users.
- @inline final override def debugwarn(msg: => String) {
- if (settings.debug.value)
- warning(msg)
- }
- private def elapsedMessage(msg: String, start: Long) =
- msg + " in " + (currentTime - start) + "ms"
+ override protected def isDeveloper = settings.developer || super.isDeveloper
- def informComplete(msg: String): Unit = reporter.withoutTruncating(inform(msg))
- def informProgress(msg: String) = if (opt.verbose) inform("[" + msg + "]")
- def inform[T](msg: String, value: T): T = returning(value)(x => inform(msg + x))
- def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start))
+ /** This is for WARNINGS which should reach the ears of scala developers
+ * whenever they occur, but are not useful for normal users. They should
+ * be precise, explanatory, and infrequent. Please don't use this as a
+ * logging mechanism. !!! is prefixed to all messages issued via this route
+ * to make them visually distinct.
+ */
+ @inline final override def devWarning(msg: => String): Unit = devWarning(NoPosition, msg)
+ @inline final def devWarning(pos: Position, msg: => String) {
+ def pos_s = if (pos eq NoPosition) "" else s" [@ $pos]"
+ if (isDeveloper)
+ warning(pos, "!!! " + msg)
+ else
+ log(s"!!!$pos_s $msg") // such warnings always at least logged
+ }
def logError(msg: String, t: Throwable): Unit = ()
- def logAfterEveryPhase[T](msg: String)(op: => T) {
- log("Running operation '%s' after every phase.\n".format(msg) + describeAfterEveryPhase(op))
- }
-
override def shouldLogAtThisPhase = settings.log.isSetByUser && (
(settings.log containsPhase globalPhase) || (settings.log containsPhase phase)
)
@@ -289,7 +307,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
@inline final override def debuglog(msg: => String) {
- if (settings.debug.value)
+ if (settings.debug)
log(msg)
}
@@ -302,7 +320,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
private val reader: SourceReader = {
val defaultEncoding = Properties.sourceEncoding
- val defaultReader = Properties.sourceReader
def loadCharset(name: String) =
try Some(Charset.forName(name))
@@ -315,7 +332,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
None
}
- val charset = opt.encoding flatMap loadCharset getOrElse {
+ val charset = settings.encoding.valueSetByUser flatMap loadCharset getOrElse {
settings.encoding.value = defaultEncoding // A mandatory charset
Charset.forName(defaultEncoding)
}
@@ -330,61 +347,16 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
}
- opt.sourceReader flatMap loadReader getOrElse {
+ settings.sourceReader.valueSetByUser flatMap loadReader getOrElse {
new SourceReader(charset.newDecoder(), reporter)
}
}
- if (!dependencyAnalysis.off)
- dependencyAnalysis.loadDependencyAnalysis()
-
- if (opt.verbose || opt.logClasspath) {
- // Uses the "do not truncate" inform
- informComplete("[search path for source files: " + classPath.sourcepaths.mkString(",") + "]")
- informComplete("[search path for class files: " + classPath.asClasspathString + "]")
- }
-
- object opt extends AestheticSettings {
- def settings = Global.this.settings
-
- // protected implicit lazy val globalPhaseOrdering: Ordering[Phase] = Ordering[Int] on (_.id)
- def isActive(ph: Settings#PhasesSetting) = ph containsPhase globalPhase
- def wasActive(ph: Settings#PhasesSetting) = ph containsPhase globalPhase.prev
-
- // Allows for syntax like scalac -Xshow-class Random@erasure,typer
- private def splitClassAndPhase(str: String, term: Boolean): Name = {
- def mkName(s: String) = if (term) newTermName(s) else newTypeName(s)
- (str indexOf '@') match {
- case -1 => mkName(str)
- case idx =>
- val phasePart = str drop (idx + 1)
- settings.Yshow.tryToSetColon(phasePart split ',' toList)
- mkName(str take idx)
- }
- }
-
- // behavior
-
- // debugging
- def checkPhase = wasActive(settings.check)
- def logPhase = isActive(settings.log)
-
- // Write *.icode files right after GenICode when -Xprint-icode was given.
- def writeICodeAtICode = settings.writeICode.isSetByUser && isActive(settings.writeICode)
-
- // showing/printing things
- def browsePhase = isActive(settings.browse)
- def echoFilenames = opt.debug && (opt.verbose || currentRun.size < 5)
- def noShow = settings.Yshow.isDefault
- def printLate = settings.printLate.value
- def printPhase = isActive(settings.Xprint)
- def showNames = List(showClass, showObject).flatten
- def showPhase = isActive(settings.Yshow)
- def showSymbols = settings.Yshowsyms.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))
- }
+ if (settings.verbose || settings.Ylogcp)
+ reporter.echo(
+ s"[search path for source files: ${classPath.asSourcePathString}]\n" +
+ s"[search path for class files: ${classPath.asClassPathString}]"
+ )
// The current division between scala.reflect.* and scala.tools.nsc.* is pretty
// clunky. It is often difficult to have a setting influence something without having
@@ -394,12 +366,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// Here comes another one...
override protected val enableTypeVarExperimentals = settings.Xexperimental.value
- // True if -Xscript has been set, indicating a script run.
- def isScriptRun = opt.script.isDefined
-
- def getSourceFile(f: AbstractFile): BatchSourceFile =
- if (isScriptRun) ScriptSourceFile(f, reader read f)
- else new BatchSourceFile(f, reader read f)
+ def getSourceFile(f: AbstractFile): BatchSourceFile = new BatchSourceFile(f, reader read f)
def getSourceFile(name: String): SourceFile = {
val f = AbstractFile.getFile(name)
@@ -408,9 +375,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
getSourceFile(f)
}
- lazy val loaders = new SymbolLoaders {
+ lazy val loaders = new {
val global: Global.this.type = Global.this
- }
+ val platform: Global.this.platform.type = Global.this.platform
+ } with GlobalSymbolLoaders
/** Returns the mirror that loaded given symbol */
def mirrorThatLoaded(sym: Symbol): Mirror = rootMirror
@@ -449,11 +417,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
reporter.cancelled || unit.isJava && this.id > maxJavaPhase
}
- final def applyPhase(unit: CompilationUnit) {
+ final def withCurrentUnit(unit: CompilationUnit)(task: => Unit) {
if ((unit ne null) && unit.exists)
lastSeenSourceFile = unit.source
- if (opt.echoFilenames)
+ if (settings.debug && (settings.verbose || currentRun.size < 5))
inform("[running phase " + name + " on " + unit + "]")
val unit0 = currentUnit
@@ -461,26 +429,28 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
currentRun.currentUnit = unit
if (!cancelled(unit)) {
currentRun.informUnitStarting(this, unit)
- apply(unit)
+ task
}
- currentRun.advanceUnit
+ currentRun.advanceUnit()
} finally {
//assert(currentUnit == unit)
currentRun.currentUnit = unit0
}
}
- }
- /** Switch to turn on detailed type logs */
- var printTypings = settings.Ytyperdebug.value
- var printInfers = settings.Yinferdebug.value
+ final def applyPhase(unit: CompilationUnit) = withCurrentUnit(unit)(apply(unit))
+ }
// phaseName = "parser"
- object syntaxAnalyzer extends {
+ lazy val syntaxAnalyzer = new {
val global: Global.this.type = Global.this
+ } with SyntaxAnalyzer {
val runsAfter = List[String]()
val runsRightAfter = None
- } with SyntaxAnalyzer
+ override val initial = true
+ }
+
+ import syntaxAnalyzer.{ UnitScanner, UnitParser }
// !!! I think we're overdue for all these phase objects being lazy vals.
// There's no way for a Global subclass to provide a custom typer
@@ -498,9 +468,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
object patmat extends {
val global: Global.this.type = Global.this
val runsAfter = List("typer")
- // patmat doesn't need to be right after typer, as long as we run before supperaccesors
- // (sbt does need to run right after typer, so don't conflict)
val runsRightAfter = None
+ // patmat doesn't need to be right after typer, as long as we run before superaccessors
+ // (sbt does need to run right after typer, so don't conflict)
} with PatternMatching
// phaseName = "superaccessors"
@@ -567,7 +537,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
} with Erasure
// phaseName = "posterasure"
- object postErasure extends {
+ override object postErasure extends {
val global: Global.this.type = Global.this
val runsAfter = List("erasure")
val runsRightAfter = Some("erasure")
@@ -615,6 +585,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val runsRightAfter = None
} with CleanUp
+ // phaseName = "delambdafy"
+ object delambdafy extends {
+ val global: Global.this.type = Global.this
+ val runsAfter = List("cleanup")
+ val runsRightAfter = None
+ } with Delambdafy
+
// phaseName = "icode"
object genicode extends {
val global: Global.this.type = Global.this
@@ -629,7 +606,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val runsRightAfter = None
} with Inliners
- // phaseName = "inlineExceptionHandlers"
+ // phaseName = "inlinehandlers"
object inlineExceptionHandlers extends {
val global: Global.this.type = Global.this
val runsAfter = List("inliner")
@@ -639,23 +616,23 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// phaseName = "closelim"
object closureElimination extends {
val global: Global.this.type = Global.this
- val runsAfter = List("inlineExceptionHandlers")
+ val runsAfter = List("inlinehandlers")
val runsRightAfter = None
} with ClosureElimination
- // phaseName = "dce"
- object deadCode extends {
+ // phaseName = "constopt"
+ object constantOptimization extends {
val global: Global.this.type = Global.this
val runsAfter = List("closelim")
val runsRightAfter = None
- } with DeadCodeElimination
+ } with ConstantOptimization
- // phaseName = "jvm", FJBG-based version
- object genJVM extends {
+ // phaseName = "dce"
+ object deadCode extends {
val global: Global.this.type = Global.this
- val runsAfter = List("dce")
+ val runsAfter = List("closelim")
val runsRightAfter = None
- } with GenJVM
+ } with DeadCodeElimination
// phaseName = "jvm", ASM-based version
object genASM extends {
@@ -664,40 +641,31 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val runsRightAfter = None
} with GenASM
- // This phase is optional: only added if settings.make option is given.
- // phaseName = "dependencyAnalysis"
- object dependencyAnalysis extends {
+ // phaseName = "bcode"
+ object genBCode extends {
val global: Global.this.type = Global.this
- val runsAfter = List("jvm")
+ val runsAfter = List("dce")
val runsRightAfter = None
- } with DependencyAnalysis
+ } with GenBCode
// phaseName = "terminal"
object terminal extends {
val global: Global.this.type = Global.this
+ } with SubComponent {
val phaseName = "terminal"
- val runsAfter = List("jvm", "msil")
+ val runsAfter = List("jvm")
val runsRightAfter = None
- } with SubComponent {
- private var cache: Option[GlobalPhase] = None
- def reset(): Unit = cache = None
-
- def newPhase(prev: Phase): GlobalPhase =
- cache getOrElse returning(new TerminalPhase(prev))(x => cache = Some(x))
+ override val terminal = true
- class TerminalPhase(prev: Phase) extends GlobalPhase(prev) {
- def name = "terminal"
+ def newPhase(prev: Phase): GlobalPhase = {
+ new TerminalPhase(prev)
+ }
+ private class TerminalPhase(prev: Phase) extends GlobalPhase(prev) {
+ def name = phaseName
def apply(unit: CompilationUnit) {}
}
}
- // phaseName = "SAMPLE PHASE"
- object sampleTransform extends {
- val global: Global.this.type = Global.this
- val runsAfter = List[String]()
- val runsRightAfter = None
- } with SampleTransform
-
/** The checkers are for validating the compiler data structures
* at phase boundaries.
*/
@@ -721,7 +689,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** Add the internal compiler phases to the phases set.
* This implementation creates a description map at the same time.
*/
- protected def computeInternalPhases() {
+ protected def computeInternalPhases(): Unit = {
// Note: this fits -Xshow-phases into 80 column width, which it is
// desirable to preserve.
val phs = List(
@@ -737,20 +705,22 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
uncurry -> "uncurry, translate function values to anonymous classes",
tailCalls -> "replace tail calls by jumps",
specializeTypes -> "@specialized-driven class and method specialization",
- explicitOuter -> "this refs to outer pointers, translate patterns",
+ explicitOuter -> "this refs to outer pointers",
erasure -> "erase types, add interfaces for traits",
postErasure -> "clean up erased inline classes",
lazyVals -> "allocate bitmaps, translate lazy vals into lazified defs",
lambdaLift -> "move nested functions to top level",
constructors -> "move field definitions into constructors",
mixer -> "mixin composition",
+ delambdafy -> "remove lambdas",
cleanup -> "platform-specific cleanups, generate reflective calls",
genicode -> "generate portable intermediate code",
inliner -> "optimization: do inlining",
inlineExceptionHandlers -> "optimization: inline exception handlers",
closureElimination -> "optimization: eliminate uncalled closures",
+ constantOptimization -> "optimization: optimize null and other constants",
deadCode -> "optimization: eliminate dead code",
- terminal -> "The last phase in the compiler chain"
+ terminal -> "the last phase during a compilation run"
)
phs foreach (addToPhasesSet _).tupled
@@ -768,13 +738,21 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// sequences the phase assembly
protected def computePhaseDescriptors: List[SubComponent] = {
- computeInternalPhases() // Global.scala
- computePlatformPhases() // backend/Platform.scala
- computePluginPhases() // plugins/Plugins.scala
- buildCompilerFromPhasesSet() // PhaseAssembly.scala
+ /** Allow phases to opt out of the phase assembly. */
+ def cullPhases(phases: List[SubComponent]) = {
+ val enabled = if (settings.debug && settings.isInfo) phases else phases filter (_.enabled)
+ def isEnabled(q: String) = enabled exists (_.phaseName == q)
+ val (satisfied, unhappy) = enabled partition (_.requires forall isEnabled)
+ unhappy foreach (u => globalError(s"Phase '${u.phaseName}' requires: ${u.requires filterNot isEnabled}"))
+ satisfied // they're happy now, but they may need an unhappy phase that was booted
+ }
+ computeInternalPhases() // Global.scala
+ computePlatformPhases() // backend/Platform.scala
+ computePluginPhases() // plugins/Plugins.scala
+ cullPhases(computePhaseAssembly()) // PhaseAssembly.scala
}
- /* The phase descriptor list */
+ /* The phase descriptor list. Components that are phase factories. */
lazy val phaseDescriptors: List[SubComponent] = computePhaseDescriptors
/* The set of phase objects that is the basis for the compiler phase chain */
@@ -792,132 +770,148 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
phaseDescriptors map (_.phaseName)
}
- /** A description of the phases that will run */
- def phaseDescriptions: String = {
- val width = phaseNames map (_.length) max
- val fmt = "%" + width + "s %2s %s\n"
+ /** A description of the phases that will run in this configuration, or all if -Ydebug. */
+ def phaseDescriptions: String = phaseHelp("description", elliptically = true, phasesDescMap)
- val line1 = fmt.format("phase name", "id", "description")
- val line2 = fmt.format("----------", "--", "-----------")
- val descs = phaseDescriptors.zipWithIndex map {
- case (ph, idx) => fmt.format(ph.phaseName, idx + 1, phasesDescMap(ph))
+ /** Summary of the per-phase values of nextFlags and newFlags, shown under -Xshow-phases -Ydebug. */
+ def phaseFlagDescriptions: String = {
+ def fmt(ph: SubComponent) = {
+ def fstr1 = if (ph.phaseNewFlags == 0L) "" else "[START] " + Flags.flagsToString(ph.phaseNewFlags)
+ def fstr2 = if (ph.phaseNextFlags == 0L) "" else "[END] " + Flags.flagsToString(ph.phaseNextFlags)
+ if (ph.initial) Flags.flagsToString(Flags.InitialFlags)
+ else if (ph.phaseNewFlags != 0L && ph.phaseNextFlags != 0L) fstr1 + " " + fstr2
+ else fstr1 + fstr2
}
- line1 :: line2 :: descs mkString
+ phaseHelp("new flags", elliptically = false, fmt)
}
- /** Summary of the per-phase values of nextFlags and newFlags, shown
- * with -Xshow-phases if -Ydebug also given.
+
+ /** Emit a verbose phase table.
+ * The table includes the phase id in the current assembly,
+ * or "oo" to indicate a skipped phase, or "xx" to indicate
+ * a disabled phase.
+ *
+ * @param title descriptive header
+ * @param elliptically whether to truncate the description with an ellipsis (...)
+ * @param describe how to describe a component
*/
- def phaseFlagDescriptions: String = {
- val width = phaseNames map (_.length) max
- val fmt = "%" + width + "s %2s %s\n"
-
- val line1 = fmt.format("phase name", "id", "new flags")
- val line2 = fmt.format("----------", "--", "---------")
- val descs = phaseDescriptors.zipWithIndex map {
- case (ph, idx) =>
- def fstr1 = if (ph.phaseNewFlags == 0L) "" else "[START] " + Flags.flagsToString(ph.phaseNewFlags)
- def fstr2 = if (ph.phaseNextFlags == 0L) "" else "[END] " + Flags.flagsToString(ph.phaseNextFlags)
- val fstr = (
- if (ph.ownPhase.id == 1) Flags.flagsToString(Flags.InitialFlags)
- else if (ph.phaseNewFlags != 0L && ph.phaseNextFlags != 0L) fstr1 + " " + fstr2
- else fstr1 + fstr2
- )
- fmt.format(ph.phaseName, idx + 1, fstr)
+ def phaseHelp(title: String, elliptically: Boolean, describe: SubComponent => String) = {
+ val Limit = 16 // phase names should not be absurdly long
+ val MaxCol = 80 // because some of us edit on green screens
+ val maxName = phaseNames map (_.length) max
+ val width = maxName min Limit
+ val maxDesc = MaxCol - (width + 6) // descriptions not novels
+ val fmt = if (settings.verbose || !elliptically) s"%${maxName}s %2s %s%n"
+ else s"%${width}.${width}s %2s %.${maxDesc}s%n"
+
+ val line1 = fmt.format("phase name", "id", title)
+ val line2 = fmt.format("----------", "--", "-" * title.length)
+
+ // built-in string precision merely truncates
+ import java.util.{ Formattable, FormattableFlags, Formatter }
+ def dotfmt(s: String) = new Formattable {
+ def elliptically(s: String, max: Int) = (
+ if (max < 0 || s.length <= max) s
+ else if (max < 4) s.take(max)
+ else s.take(max - 3) + "..."
+ )
+ override def formatTo(formatter: Formatter, flags: Int, width: Int, precision: Int) {
+ val p = elliptically(s, precision)
+ val w = if (width > 0 && p.length < width) {
+ import FormattableFlags.LEFT_JUSTIFY
+ val leftly = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY
+ val sb = new StringBuilder
+ def pad() = 1 to width - p.length foreach (_ => sb.append(' '))
+ if (!leftly) pad()
+ sb.append(p)
+ if (leftly) pad()
+ sb.toString
+ } else p
+ formatter.out.append(w)
+ }
+ }
+
+ // phase id in run, or suitable icon
+ def idOf(p: SubComponent) = (
+ if (settings.skip contains p.phaseName) "oo" // (currentRun skipPhase p.phaseName)
+ else if (!p.enabled) "xx"
+ else p.ownPhase.id.toString
+ )
+ def mkText(p: SubComponent) = {
+ val (name, text) = if (elliptically) (dotfmt(p.phaseName), dotfmt(describe(p)))
+ else (p.phaseName, describe(p))
+ fmt.format(name, idOf(p), text)
}
- line1 :: line2 :: descs mkString
+ line1 :: line2 :: (phaseDescriptors map mkText) mkString
}
/** Returns List of (phase, value) pairs, including only those
* where the value compares unequal to the previous phase's value.
*/
- def afterEachPhase[T](op: => T): List[(Phase, T)] = {
+ def afterEachPhase[T](op: => T): List[(Phase, T)] = { // used in tests
phaseDescriptors.map(_.ownPhase).filterNot(_ eq NoPhase).foldLeft(List[(Phase, T)]()) { (res, ph) =>
- val value = afterPhase(ph)(op)
+ val value = exitingPhase(ph)(op)
if (res.nonEmpty && res.head._2 == value) res
else ((ph, value)) :: res
} reverse
}
- /** Returns List of ChangeAfterPhase objects, encapsulating those
- * phase transitions where the result of the operation gave a different
- * list than it had when run during the previous phase.
- */
- def changesAfterEachPhase[T](op: => List[T]): List[ChangeAfterPhase[T]] = {
- val ops = ((NoPhase, Nil)) :: afterEachPhase(op)
-
- ops sliding 2 map {
- case (_, before) :: (ph, after) :: Nil =>
- val lost = before filterNot (after contains _)
- val gained = after filterNot (before contains _)
- ChangeAfterPhase(ph, lost, gained)
- case _ => ???
- } toList
- }
- private def numberedPhase(ph: Phase) = "%2d/%s".format(ph.id, ph.name)
+ // ------------ REPL utilities ---------------------------------
- case class ChangeAfterPhase[+T](ph: Phase, lost: List[T], gained: List[T]) {
- private def mkStr(what: String, xs: List[_]) = (
- if (xs.isEmpty) ""
- else xs.mkString(what + " after " + numberedPhase(ph) + " {\n ", "\n ", "\n}\n")
- )
- override def toString = mkStr("Lost", lost) + mkStr("Gained", gained)
- }
-
- def describeAfterEachPhase[T](op: => T): List[String] =
- afterEachPhase(op) map { case (ph, t) => "[after %-15s] %s".format(numberedPhase(ph), t) }
-
- def describeAfterEveryPhase[T](op: => T): String =
- describeAfterEachPhase(op) map (" " + _ + "\n") mkString
+ /** Extend classpath of `platform` and rescan updated packages. */
+ def extendCompilerClassPath(urls: URL*): Unit = {
+ if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat)
+ throw new UnsupportedOperationException("Flat classpath doesn't support extending the compiler classpath")
- def printAfterEachPhase[T](op: => T): Unit =
- describeAfterEachPhase(op) foreach (m => println(" " + m))
+ val newClassPath = platform.classPath.mergeUrlsIntoClassPath(urls: _*)
+ platform.currentClassPath = Some(newClassPath)
+ // Reload all specified jars into this compiler instance
+ invalidateClassPathEntries(urls.map(_.getPath): _*)
+ }
// ------------ Invalidations ---------------------------------
/** Is given package class a system package class that cannot be invalidated?
*/
private def isSystemPackageClass(pkg: Symbol) =
- pkg == RootClass ||
- pkg == definitions.ScalaPackageClass || {
- val pkgname = pkg.fullName
- (pkgname startsWith "scala.") && !(pkgname startsWith "scala.tools")
- }
+ pkg == RootClass || (pkg.hasTransOwner(definitions.ScalaPackageClass) && !pkg.hasTransOwner(this.rootMirror.staticPackage("scala.tools").moduleClass.asClass))
/** Invalidates packages that contain classes defined in a classpath entry, and
* rescans that entry.
- * @param path A fully qualified name that refers to a directory or jar file that's
- * an entry on the classpath.
- * First, causes the classpath entry referred to by `path` to be rescanned, so that
- * any new files or deleted files or changes in subpackages are picked up.
- * Second, invalidates any packages for which one of the following considitions is met:
-
- * - the classpath entry contained during the last compilation run classfiles
- * that represent a member in the package
- * - the classpath entry now contains classfiles
- * that represent a member in the package
+ *
+ * First, the classpath entry referred to by one of the `paths` is rescanned,
+ * so that any new files or changes in subpackages are picked up.
+ * Second, any packages for which one of the following conditions is met is invalidated:
+ * - the classpath entry contained during the last compilation run now contains classfiles
+ * that represent a member in the package;
+ * - the classpath entry now contains classfiles that represent a member in the package;
* - the set of subpackages has changed.
*
* The invalidated packages are reset in their entirety; all member classes and member packages
* are re-accessed using the new classpath.
- * Not invalidated are system packages that the compiler needs to access as parts
- * of standard definitions. The criterion what is a system package is currently:
- * any package rooted in "scala", with the exception of packages rooted in "scala.tools".
- * This can be refined later.
- * @return A pair consisting of
- * - a list of invalidated packages
- * - a list of of packages that should have been invalidated but were not because
- * they are system packages.
+ *
+ * System packages that the compiler needs to access as part of standard definitions
+ * are not invalidated. A system package is:
+ * Any package rooted in "scala", with the exception of packages rooted in "scala.tools".
+ *
+ * @param paths Fully-qualified names that refer to directories or jar files that are
+ * entries on the classpath.
*/
- def invalidateClassPathEntries(paths: String*): (List[ClassSymbol], List[ClassSymbol]) = {
+ def invalidateClassPathEntries(paths: String*): Unit = {
+ if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat)
+ throw new UnsupportedOperationException("Flat classpath doesn't support the classpath invalidation")
+
+ implicit object ClassPathOrdering extends Ordering[PlatformClassPath] {
+ def compare(a:PlatformClassPath, b:PlatformClassPath) = a.asClassPathString compare b.asClassPathString
+ }
val invalidated, failed = new mutable.ListBuffer[ClassSymbol]
classPath match {
case cp: MergedClassPath[_] =>
def assoc(path: String): List[(PlatformClassPath, PlatformClassPath)] = {
- val dir = AbstractFile getDirectory path
+ val dir = AbstractFile.getDirectory(path)
val canonical = dir.canonicalPath
def matchesCanonical(e: ClassPath[_]) = e.origin match {
case Some(opath) =>
- (AbstractFile getDirectory opath).canonicalPath == canonical
+ AbstractFile.getDirectory(opath).canonicalPath == canonical
case None =>
false
}
@@ -925,21 +919,20 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
case Some(oldEntry) =>
List(oldEntry -> cp.context.newClassPath(dir))
case None =>
- println(s"canonical = $canonical, origins = ${cp.entries map (_.origin)}")
- error(s"cannot invalidate: no entry named $path in classpath $classPath")
+ error(s"Error adding entry to classpath. During invalidation, no entry named $path in classpath $classPath")
List()
}
}
- val subst = Map(paths flatMap assoc: _*)
+ val subst = immutable.TreeMap(paths flatMap assoc: _*)
if (subst.nonEmpty) {
platform updateClassPath subst
informProgress(s"classpath updated on entries [${subst.keys mkString ","}]")
def mkClassPath(elems: Iterable[PlatformClassPath]): PlatformClassPath =
if (elems.size == 1) elems.head
- else new MergedClassPath(elems, classPath.context)
+ else new MergedClassPath(elems, recursiveClassPath.context)
val oldEntries = mkClassPath(subst.keys)
val newEntries = mkClassPath(subst.values)
- reSync(RootClass, Some(classPath), Some(oldEntries), Some(newEntries), invalidated, failed)
+ mergeNewEntries(newEntries, RootClass, Some(recursiveClassPath), Some(oldEntries), invalidated, failed)
}
}
def show(msg: String, syms: scala.collection.Traversable[Symbol]) =
@@ -947,40 +940,36 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
informProgress(s"$msg: ${syms map (_.fullName) mkString ","}")
show("invalidated packages", invalidated)
show("could not invalidate system packages", failed)
- (invalidated.toList, failed.toList)
}
- /** Re-syncs symbol table with classpath
+ /** Merges new classpath entries into the symbol table
+ *
+ * @param newEntries The new classpath entries
* @param root The root symbol to be resynced (a package class)
- * @param allEntries Optionally, the corresponding package in the complete current classPath
- * @param oldEntries Optionally, the corresponding package in the old classPath entries
- * @param newEntries Optionally, the corresponding package in the new classPath entries
+ * @param allEntries Optionally, the corresponding package in the complete current classpath
+ * @param oldEntries Optionally, the corresponding package in the old classpath entries
* @param invalidated A listbuffer collecting the invalidated package classes
* @param failed A listbuffer collecting system package classes which could not be invalidated
- * The resyncing strategy is determined by the absence or presence of classes and packages.
- * If either oldEntries or newEntries contains classes, root is invalidated, provided a corresponding package
- * exists in allEntries, or otherwise is removed.
- * Otherwise, the action is determined by the following matrix, with columns:
*
- * old new all sym action
- * + + + + recurse into all child packages of old ++ new
- * + - + + invalidate root
- * + - - + remove root from its scope
- * - + + + invalidate root
- * - + + - create and enter root
- * - - * * no action
+ * The merging strategy is determined by the absence or presence of classes and packages.
*
- * Here, old, new, all mean classpaths and sym means symboltable. + is presence of an
- * entry in its column, - is absence, * is don't care.
+ * If either oldEntries or newEntries contains classes, root is invalidated provided that a corresponding package
+ * exists in allEntries. Otherwise it is removed.
+ * Otherwise, the action is determined by the following matrix, with columns:
+ *
+ * old sym action
+ * + + recurse into all child packages of newEntries
+ * - + invalidate root
+ * - - create and enter root
*
- * Note that new <= all and old <= sym, so the matrix above covers all possibilities.
+ * Here, old means classpath, and sym means symboltable. + is presence of an entry in its column, - is absence.
*/
- private def reSync(root: ClassSymbol,
- allEntries: OptClassPath, oldEntries: OptClassPath, newEntries: OptClassPath,
+ private def mergeNewEntries(newEntries: PlatformClassPath, root: ClassSymbol,
+ allEntries: OptClassPath, oldEntries: OptClassPath,
invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]) {
ifDebug(informProgress(s"syncing $root, $oldEntries -> $newEntries"))
- val getName: ClassPath[platform.BinaryRepr] => String = (_.name)
+ val getName: ClassPath[AbstractFile] => String = (_.name)
def hasClasses(cp: OptClassPath) = cp.isDefined && cp.get.classes.nonEmpty
def invalidateOrRemove(root: ClassSymbol) = {
allEntries match {
@@ -989,11 +978,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
invalidated += root
}
- def packageNames(cp: PlatformClassPath): Set[String] = cp.packages.toSet map getName
def subPackage(cp: PlatformClassPath, name: String): OptClassPath =
cp.packages find (cp1 => getName(cp1) == name)
- val classesFound = hasClasses(oldEntries) || hasClasses(newEntries)
+ val classesFound = hasClasses(oldEntries) || newEntries.classes.nonEmpty
if (classesFound && !isSystemPackageClass(root)) {
invalidateOrRemove(root)
} else {
@@ -1001,80 +989,30 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
if (root.isRoot) invalidateOrRemove(EmptyPackageClass)
else failed += root
}
- (oldEntries, newEntries) match {
- case (Some(oldcp) , Some(newcp)) =>
- for (pstr <- packageNames(oldcp) ++ packageNames(newcp)) {
- val pname = newTermName(pstr)
- val pkg = (root.info decl pname) orElse {
- // package was created by external agent, create symbol to track it
- assert(!subPackage(oldcp, pstr).isDefined)
- loaders.enterPackage(root, pstr, new loaders.PackageLoader(allEntries.get))
- }
- reSync(
- pkg.moduleClass.asInstanceOf[ClassSymbol],
- subPackage(allEntries.get, pstr), subPackage(oldcp, pstr), subPackage(newcp, pstr),
- invalidated, failed)
+ if (!oldEntries.isDefined) invalidateOrRemove(root)
+ else
+ for (pstr <- newEntries.packages.map(getName)) {
+ val pname = newTermName(pstr)
+ val pkg = (root.info decl pname) orElse {
+ // package does not exist in symbol table, create symbol to track it
+ assert(!subPackage(oldEntries.get, pstr).isDefined)
+ loaders.enterPackage(root, pstr, new loaders.PackageLoader(allEntries.get))
}
- case (Some(oldcp), None) =>
- invalidateOrRemove(root)
- case (None, Some(newcp)) =>
- invalidateOrRemove(root)
- case (None, None) =>
- }
+ mergeNewEntries(subPackage(newEntries, pstr).get, pkg.moduleClass.asClass,
+ subPackage(allEntries.get, pstr), subPackage(oldEntries.get, pstr),
+ invalidated, failed)
+ }
}
}
- /** Invalidate contents of setting -Yinvalidate */
- def doInvalidation() = settings.Yinvalidate.value match {
- case "" =>
- case entry => invalidateClassPathEntries(entry)
- }
-
// ----------- Runs ---------------------------------------
private var curRun: Run = null
private var curRunId = 0
- /** A hook that lets subclasses of `Global` define whether a package or class should be kept loaded for the
- * next compiler run. If the parameter `sym` is a class or object, and `clearOnNextRun(sym)` returns `true`,
- * then the symbol is unloaded and reset to its state before the last compiler run. If the parameter `sym` is
- * a package, and clearOnNextRun(sym)` returns `true`, the package is recursively searched for
- * classes to drop.
- *
- * Example: Let's say I want a compiler that drops all classes corresponding to the current project
- * between runs. Then `keepForNextRun` of a toplevel class or object should return `true` if the
- * class or object does not form part of the current project, `false` otherwise. For a package,
- * clearOnNextRun should return `true` if no class in that package forms part of the current project,
- * `false` otherwise.
- *
- * @param sym A class symbol, object symbol, package, or package class.
- */
- @deprecated("use invalidateClassPathEntries instead", "2.10.0")
- def clearOnNextRun(sym: Symbol) = false
- /* To try out clearOnNext run on the scala.tools.nsc project itself
- * replace `false` above with the following code
-
- settings.Xexperimental.value && { sym.isRoot || {
- sym.fullName match {
- case "scala" | "scala.tools" | "scala.tools.nsc" => true
- case _ => sym.owner.fullName.startsWith("scala.tools.nsc")
- }
- }}
-
- * Then, fsc -Xexperimental clears the nsc project between successive runs of `fsc`.
- */
-
- /** Remove the current run when not needed anymore. Used by the build
- * manager to save on the memory foot print. The current run holds on
- * to all compilation units, which in turn hold on to trees.
- */
- private [nsc] def dropRun() {
- curRun = null
- }
-
object typeDeconstruct extends {
val global: Global.this.type = Global.this
- } with interpreter.StructuredTypeStrings
+ } with typechecker.StructuredTypeStrings
/** There are common error conditions where when the exception hits
* here, currentRun.currentUnit is null. This robs us of the knowledge
@@ -1093,124 +1031,118 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
def currentRun: Run = curRun
def currentUnit: CompilationUnit = if (currentRun eq null) NoCompilationUnit else currentRun.currentUnit
def currentSource: SourceFile = if (currentUnit.exists) currentUnit.source else lastSeenSourceFile
+ def currentFreshNameCreator = currentUnit.fresh
- // TODO - trim these to the absolute minimum.
- @inline final def afterErasure[T](op: => T): T = afterPhase(currentRun.erasurePhase)(op)
- @inline final def afterPostErasure[T](op: => T): T = afterPhase(currentRun.posterasurePhase)(op)
- @inline final def afterExplicitOuter[T](op: => T): T = afterPhase(currentRun.explicitouterPhase)(op)
- @inline final def afterFlatten[T](op: => T): T = afterPhase(currentRun.flattenPhase)(op)
- @inline final def afterIcode[T](op: => T): T = afterPhase(currentRun.icodePhase)(op)
- @inline final def afterMixin[T](op: => T): T = afterPhase(currentRun.mixinPhase)(op)
- @inline final def afterPickler[T](op: => T): T = afterPhase(currentRun.picklerPhase)(op)
- @inline final def afterRefchecks[T](op: => T): T = afterPhase(currentRun.refchecksPhase)(op)
- @inline final def afterSpecialize[T](op: => T): T = afterPhase(currentRun.specializePhase)(op)
- @inline final def afterTyper[T](op: => T): T = afterPhase(currentRun.typerPhase)(op)
- @inline final def afterUncurry[T](op: => T): T = afterPhase(currentRun.uncurryPhase)(op)
- @inline final def beforeErasure[T](op: => T): T = beforePhase(currentRun.erasurePhase)(op)
- @inline final def beforeExplicitOuter[T](op: => T): T = beforePhase(currentRun.explicitouterPhase)(op)
- @inline final def beforeFlatten[T](op: => T): T = beforePhase(currentRun.flattenPhase)(op)
- @inline final def beforeIcode[T](op: => T): T = beforePhase(currentRun.icodePhase)(op)
- @inline final def beforeMixin[T](op: => T): T = beforePhase(currentRun.mixinPhase)(op)
- @inline final def beforePickler[T](op: => T): T = beforePhase(currentRun.picklerPhase)(op)
- @inline final def beforeRefchecks[T](op: => T): T = beforePhase(currentRun.refchecksPhase)(op)
- @inline final def beforeSpecialize[T](op: => T): T = beforePhase(currentRun.specializePhase)(op)
- @inline final def beforeTyper[T](op: => T): T = beforePhase(currentRun.typerPhase)(op)
- @inline final def beforeUncurry[T](op: => T): T = beforePhase(currentRun.uncurryPhase)(op)
-
- def explainContext(c: analyzer.Context): String = (
- if (c == null) "" else (
- """| context owners: %s
- |
- |Enclosing block or template:
- |%s""".format(
- c.owner.ownerChain.takeWhile(!_.isPackageClass).mkString(" -> "),
- nodePrinters.nodeToString(c.enclClassOrMethod.tree)
- )
- )
+ def isGlobalInitialized = (
+ definitions.isDefinitionsInitialized
+ && rootMirror.isMirrorInitialized
)
- // Owners up to and including the first package class.
+ override def isPastTyper = (
+ (curRun ne null)
+ && isGlobalInitialized // defense against init order issues
+ && (globalPhase.id > currentRun.typerPhase.id)
+ )
+
+ // TODO - trim these to the absolute minimum.
+ @inline final def exitingErasure[T](op: => T): T = exitingPhase(currentRun.erasurePhase)(op)
+ @inline final def exitingPostErasure[T](op: => T): T = exitingPhase(currentRun.posterasurePhase)(op)
+ @inline final def exitingExplicitOuter[T](op: => T): T = exitingPhase(currentRun.explicitouterPhase)(op)
+ @inline final def exitingFlatten[T](op: => T): T = exitingPhase(currentRun.flattenPhase)(op)
+ @inline final def exitingMixin[T](op: => T): T = exitingPhase(currentRun.mixinPhase)(op)
+ @inline final def exitingDelambdafy[T](op: => T): T = exitingPhase(currentRun.delambdafyPhase)(op)
+ @inline final def exitingPickler[T](op: => T): T = exitingPhase(currentRun.picklerPhase)(op)
+ @inline final def exitingRefchecks[T](op: => T): T = exitingPhase(currentRun.refchecksPhase)(op)
+ @inline final def exitingSpecialize[T](op: => T): T = exitingPhase(currentRun.specializePhase)(op)
+ @inline final def exitingTyper[T](op: => T): T = exitingPhase(currentRun.typerPhase)(op)
+ @inline final def exitingUncurry[T](op: => T): T = exitingPhase(currentRun.uncurryPhase)(op)
+ @inline final def enteringErasure[T](op: => T): T = enteringPhase(currentRun.erasurePhase)(op)
+ @inline final def enteringExplicitOuter[T](op: => T): T = enteringPhase(currentRun.explicitouterPhase)(op)
+ @inline final def enteringFlatten[T](op: => T): T = enteringPhase(currentRun.flattenPhase)(op)
+ @inline final def enteringIcode[T](op: => T): T = enteringPhase(currentRun.icodePhase)(op)
+ @inline final def enteringMixin[T](op: => T): T = enteringPhase(currentRun.mixinPhase)(op)
+ @inline final def enteringDelambdafy[T](op: => T): T = enteringPhase(currentRun.delambdafyPhase)(op)
+ @inline final def enteringPickler[T](op: => T): T = enteringPhase(currentRun.picklerPhase)(op)
+ @inline final def enteringSpecialize[T](op: => T): T = enteringPhase(currentRun.specializePhase)(op)
+ @inline final def enteringTyper[T](op: => T): T = enteringPhase(currentRun.typerPhase)(op)
+ @inline final def enteringUncurry[T](op: => T): T = enteringPhase(currentRun.uncurryPhase)(op)
+
+ // Owners which aren't package classes.
private def ownerChainString(sym: Symbol): String = (
if (sym == null) ""
- else sym.ownerChain.span(!_.isPackageClass) match {
- case (xs, pkg :: _) => (xs :+ pkg) mkString " -> "
- case _ => sym.ownerChain mkString " -> " // unlikely
- }
+ else sym.ownerChain takeWhile (!_.isPackageClass) mkString " -> "
)
+
private def formatExplain(pairs: (String, Any)*): String = (
pairs.toList collect { case (k, v) if v != null => "%20s: %s".format(k, v) } mkString "\n"
)
- def explainTree(t: Tree): String = formatExplain(
- )
-
/** Don't want to introduce new errors trying to report errors,
* so swallow exceptions.
*/
- override def supplementErrorMessage(errorMessage: String): String =
- if (currentRun.supplementedError) errorMessage
- else try {
- val tree = analyzer.lastTreeToTyper
- val sym = tree.symbol
- val tpe = tree.tpe
- val enclosing = lastSeenContext.enclClassOrMethod.tree
-
- val info1 = formatExplain(
- "while compiling" -> currentSource.path,
- "during phase" -> ( if (globalPhase eq phase) phase else "global=%s, atPhase=%s".format(globalPhase, phase) ),
- "library version" -> scala.util.Properties.versionString,
- "compiler version" -> Properties.versionString,
- "reconstructed args" -> settings.recreateArgs.mkString(" ")
- )
- val info2 = formatExplain(
- "last tree to typer" -> tree.summaryString,
- "symbol" -> Option(sym).fold("null")(_.debugLocationString),
- "symbol definition" -> Option(sym).fold("null")(_.defString),
- "tpe" -> tpe,
- "symbol owners" -> ownerChainString(sym),
- "context owners" -> ownerChainString(lastSeenContext.owner)
- )
- val info3: List[String] = (
- ( List("== Enclosing template or block ==", nodePrinters.nodeToString(enclosing).trim) )
- ++ ( if (tpe eq null) Nil else List("== Expanded type of tree ==", typeDeconstruct.show(tpe)) )
- ++ ( if (!opt.debug) Nil else List("== Current unit body ==", nodePrinters.nodeToString(currentUnit.body)) )
- ++ ( List(errorMessage) )
- )
-
- currentRun.supplementedError = true
-
- ("\n" + info1) :: info2 :: info3 mkString "\n\n"
+ override def supplementTyperState(errorMessage: String): String = try {
+ val tree = analyzer.lastTreeToTyper
+ val sym = tree.symbol
+ val tpe = tree.tpe
+ val site = lastSeenContext.enclClassOrMethod.owner
+ val pos_s = if (tree.pos.isDefined) s"line ${tree.pos.line} of ${tree.pos.source.file}" else "<unknown>"
+ val context_s = try {
+ // Taking 3 before, 3 after the fingered line.
+ val start = 0 max (tree.pos.line - 3)
+ val xs = scala.reflect.io.File(tree.pos.source.file.file).lines drop start take 7
+ val strs = xs.zipWithIndex map { case (line, idx) => f"${start + idx}%6d $line" }
+ strs.mkString("== Source file context for tree position ==\n\n", "\n", "")
}
- catch { case _: Exception | _: TypeError => errorMessage }
+ catch { case t: Exception => devWarning("" + t) ; "<Cannot read source file>" }
+
+ val info1 = formatExplain(
+ "while compiling" -> currentSource.path,
+ "during phase" -> ( if (globalPhase eq phase) phase else "globalPhase=%s, enteringPhase=%s".format(globalPhase, phase) ),
+ "library version" -> scala.util.Properties.versionString,
+ "compiler version" -> Properties.versionString,
+ "reconstructed args" -> settings.recreateArgs.mkString(" ")
+ )
+ val info2 = formatExplain(
+ "last tree to typer" -> tree.summaryString,
+ "tree position" -> pos_s,
+ "tree tpe" -> tpe,
+ "symbol" -> Option(sym).fold("null")(_.debugLocationString),
+ "symbol definition" -> Option(sym).fold("null")(s => s.defString + s" (a ${s.shortSymbolClass})"),
+ "symbol package" -> sym.enclosingPackage.fullName,
+ "symbol owners" -> ownerChainString(sym),
+ "call site" -> (site.fullLocationString + " in " + site.enclosingPackage)
+ )
+ ("\n " + errorMessage + "\n" + info1) :: info2 :: context_s :: Nil mkString "\n\n"
+ } catch { case _: Exception | _: TypeError => errorMessage }
+
/** The id of the currently active run
*/
override def currentRunId = curRunId
def echoPhaseSummary(ph: Phase) = {
- /** Only output a summary message under debug if we aren't echoing each file. */
- if (opt.debug && !opt.echoFilenames)
+ /* Only output a summary message under debug if we aren't echoing each file. */
+ if (settings.debug && !(settings.verbose || currentRun.size < 5))
inform("[running phase " + ph.name + " on " + currentRun.size + " compilation units]")
}
- /** Collects for certain classes of warnings during this run. */
- class ConditionalWarning(what: String, option: Settings#BooleanSetting) {
- val warnings = mutable.LinkedHashMap[Position, String]()
- def warn(pos: Position, msg: String) =
- if (option.value) reporter.warning(pos, msg)
- else if (!(warnings contains pos)) warnings += ((pos, msg))
- def summarize() =
- if (option.isDefault && warnings.nonEmpty)
- reporter.warning(NoPosition, "there were %d %s warning(s); re-run with %s for details".format(warnings.size, what, option.name))
- }
+ def newSourceFile(code: String, filename: String = "<console>") =
+ new BatchSourceFile(filename, code)
+
+ def newCompilationUnit(code: String, filename: String = "<console>") =
+ new CompilationUnit(newSourceFile(code, filename))
+
+ def newUnitScanner(unit: CompilationUnit): UnitScanner =
+ new UnitScanner(unit)
+
+ def newUnitParser(unit: CompilationUnit): UnitParser =
+ new UnitParser(unit)
- def newUnitParser(code: String) = new syntaxAnalyzer.UnitParser(newCompilationUnit(code))
- def newUnitScanner(code: String) = new syntaxAnalyzer.UnitScanner(newCompilationUnit(code))
- def newCompilationUnit(code: String) = new CompilationUnit(newSourceFile(code))
- def newSourceFile(code: String) = new BatchSourceFile("<console>", code)
+ def newUnitParser(code: String, filename: String = "<console>"): UnitParser =
+ newUnitParser(newCompilationUnit(code, filename))
- /** A Run is a single execution of the compiler on a sets of units
+ /** A Run is a single execution of the compiler on a set of units.
*/
- class Run extends RunContextApi {
+ class Run extends RunContextApi with RunReporting with RunParsing {
/** Have been running into too many init order issues with Run
* during erroneous conditions. Moved all these vals up to the
* top of the file so at least they're not trivially null.
@@ -1219,30 +1151,31 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** The currently compiled unit; set from GlobalPhase */
var currentUnit: CompilationUnit = NoCompilationUnit
- // This change broke sbt; I gave it the thrilling name of uncheckedWarnings0 so
- // as to recover uncheckedWarnings for its ever-fragile compiler interface.
- val deprecationWarnings0 = new ConditionalWarning("deprecation", settings.deprecation)
- val uncheckedWarnings0 = new ConditionalWarning("unchecked", settings.unchecked)
- val featureWarnings = new ConditionalWarning("feature", settings.feature)
- val inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings)
- val allConditionalWarnings = List(deprecationWarnings0, uncheckedWarnings0, featureWarnings, inlinerWarnings)
-
- // for sbt's benefit
- def uncheckedWarnings: List[(Position, String)] = uncheckedWarnings0.warnings.toList
- def deprecationWarnings: List[(Position, String)] = deprecationWarnings0.warnings.toList
-
- var reportedFeature = Set[Symbol]()
-
- /** Has any macro expansion used a fallback during this run? */
- var seenMacroExpansionsFallingBack = false
-
- /** Have we already supplemented the error message of a compiler crash? */
- private[nsc] final var supplementedError = false
+ // used in sbt
+ def uncheckedWarnings: List[(Position, String)] = reporting.uncheckedWarnings
+ // used in sbt
+ def deprecationWarnings: List[(Position, String)] = reporting.deprecationWarnings
+
+ private class SyncedCompilationBuffer { self =>
+ private val underlying = new mutable.ArrayBuffer[CompilationUnit]
+ def size = synchronized { underlying.size }
+ def +=(cu: CompilationUnit): this.type = { synchronized { underlying += cu }; this }
+ def head: CompilationUnit = synchronized{ underlying.head }
+ def apply(i: Int): CompilationUnit = synchronized { underlying(i) }
+ def iterator: Iterator[CompilationUnit] = new collection.AbstractIterator[CompilationUnit] {
+ private var used = 0
+ def hasNext = self.synchronized{ used < underlying.size }
+ def next = self.synchronized {
+ if (!hasNext) throw new NoSuchElementException("next on empty Iterator")
+ used += 1
+ underlying(used-1)
+ }
+ }
+ def toList: List[CompilationUnit] = synchronized{ underlying.toList }
+ }
- /** To be initialized from firstPhase. */
- private var terminalPhase: Phase = NoPhase
+ private val unitbuf = new SyncedCompilationBuffer
- private val unitbuf = new mutable.ListBuffer[CompilationUnit]
val compiledFiles = new mutable.HashSet[String]
/** A map from compiled top-level symbols to their source files */
@@ -1251,105 +1184,99 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** A map from compiled top-level symbols to their picklers */
val symData = new mutable.HashMap[Symbol, PickleBuffer]
- private var phasec: Int = 0 // phases completed
- private var unitc: Int = 0 // units completed this phase
- private var _unitbufSize = 0
+ private var phasec: Int = 0 // phases completed
+ private var unitc: Int = 0 // units completed this phase
- def size = _unitbufSize
+ def size = unitbuf.size
override def toString = "scalac Run for:\n " + compiledFiles.toList.sorted.mkString("\n ")
// Calculate where to stop based on settings -Ystop-before or -Ystop-after.
- // Slightly complicated logic due to wanting -Ystop-before:parser to fail rather
- // than mysteriously running to completion.
+ // The result is the phase to stop at BEFORE running it.
private lazy val stopPhaseSetting = {
- val result = phaseDescriptors sliding 2 collectFirst {
- case xs if xs exists (settings.stopBefore contains _.phaseName) => if (settings.stopBefore contains xs.head.phaseName) xs.head else xs.last
- case xs if settings.stopAfter contains xs.head.phaseName => xs.last
+ def isBefore(pd: SubComponent) = settings.stopBefore contains pd.phaseName
+ phaseDescriptors sliding 2 collectFirst {
+ case xs if xs exists isBefore
+ => (xs find isBefore).get
+ case xs if settings.stopAfter contains xs.head.phaseName
+ => xs.last
}
- if (result exists (_.phaseName == "parser"))
- globalError("Cannot stop before parser phase.")
-
- result
}
- // The phase to stop BEFORE running.
+ /** Should we stop right before entering the given phase? */
protected def stopPhase(name: String) = stopPhaseSetting exists (_.phaseName == name)
+ /** Should we skip the given phase? */
protected def skipPhase(name: String) = settings.skip contains name
- /** As definitions.init requires phase != NoPhase, and calling phaseDescriptors.head
- * will force init, there is some jockeying herein regarding init order: instead of
- * taking the head descriptor we create a parser phase directly.
- */
private val firstPhase = {
- /** Initialization. */
+ // Initialization. definitions.init requires phase != NoPhase
+ import scala.reflect.internal.SomePhase
curRunId += 1
curRun = this
-
- /** Set phase to a newly created syntaxAnalyzer and call definitions.init. */
- val parserPhase: Phase = syntaxAnalyzer.newPhase(NoPhase)
- phase = parserPhase
+ phase = SomePhase
+ phaseWithId(phase.id) = phase
definitions.init()
- // Flush the cache in the terminal phase: the chain could have been built
- // before without being used. (This happens in the interpreter.)
- terminal.reset
-
- // Each subcomponent supplies a phase, which are chained together.
- // If -Ystop:phase is given, neither that phase nor any beyond it is added.
- // If -Yskip:phase is given, that phase will be skipped.
- val phaseLinks = {
- val phs = (
- phaseDescriptors.tail
- takeWhile (pd => !stopPhase(pd.phaseName))
- filterNot (pd => skipPhase(pd.phaseName))
- )
+ // the components to use, omitting those named by -Yskip and stopping at the -Ystop phase
+ val components = {
+ // stop on a dime, but this test fails if pd is after the stop phase
+ def unstoppable(pd: SubComponent) = {
+ val stoppable = stopPhase(pd.phaseName)
+ if (stoppable && pd.initial) {
+ globalError(s"Cannot stop before initial phase '${pd.phaseName}'.")
+ true
+ } else
+ !stoppable
+ }
+ // skip a component for -Yskip or if not enabled
+ def skippable(pd: SubComponent) = {
+ val skippable = skipPhase(pd.phaseName)
+ if (skippable && (pd.initial || pd.terminal)) {
+ globalError(s"Cannot skip an initial or terminal phase '${pd.phaseName}'.")
+ false
+ } else
+ skippable || !pd.enabled
+ }
+ val phs = phaseDescriptors takeWhile unstoppable filterNot skippable
// Ensure there is a terminal phase at the end, since -Ystop may have limited the phases.
- if (phs.isEmpty || (phs.last ne terminal)) phs :+ terminal
- else phs
- }
- // Link them together.
- phaseLinks.foldLeft(parserPhase)((chain, ph) => ph newPhase chain)
- parserPhase
- }
-
- /** Reset all classes contained in current project, as determined by
- * the clearOnNextRun hook
- */
- @deprecated("use invalidateClassPathEntries instead", "2.10.0")
- def resetProjectClasses(root: Symbol): Unit = try {
- def unlink(sym: Symbol) =
- if (sym != NoSymbol) root.info.decls.unlink(sym)
- if (settings.verbose.value) inform("[reset] recursing in "+root)
- val toReload = mutable.Set[String]()
- for (sym <- root.info.decls) {
- if (sym.isInitialized && clearOnNextRun(sym))
- if (sym.isPackage) {
- resetProjectClasses(sym.moduleClass)
- openPackageModule(sym.moduleClass)
- } else {
- unlink(sym)
- unlink(root.info.decls.lookup(
- if (sym.isTerm) sym.name.toTypeName else sym.name.toTermName))
- toReload += sym.fullName
- // note: toReload could be set twice with the same name
- // but reinit must happen only once per name. That's why
- // the following classPath.findClass { ... } code cannot be moved here.
- }
+ if (phs.isEmpty || !phs.last.terminal) {
+ val t = if (phaseDescriptors.last.terminal) phaseDescriptors.last else terminal
+ phs :+ t
+ } else phs
}
- for (fullname <- toReload)
- classPath.findClass(fullname) match {
- case Some(classRep) =>
- if (settings.verbose.value) inform("[reset] reinit "+fullname)
- loaders.initializeFromClassPath(root, classRep)
- case _ =>
+ // Create phases and link them together. We supply the previous, and the ctor sets prev.next.
+ val last = components.foldLeft(NoPhase: Phase)((prev, c) => c newPhase prev)
+ // rewind (Iterator.iterate(last)(_.prev) dropWhile (_.prev ne NoPhase)).next
+ val first = { var p = last ; while (p.prev ne NoPhase) p = p.prev ; p }
+ val ss = settings
+
+ // As a final courtesy, see if the settings make any sense at all.
+ // If a setting selects no phase, it's a mistake. If a name prefix
+ // doesn't select a unique phase, that might be surprising too.
+ def checkPhaseSettings(including: Boolean, specs: Seq[String]*) = {
+ def isRange(s: String) = s.forall(c => c.isDigit || c == '-')
+ def isSpecial(s: String) = (s == "all" || isRange(s))
+ val setting = new ss.PhasesSetting("fake","fake")
+ for (p <- specs.flatten.to[Set]) {
+ setting.value = List(p)
+ val count = (
+ if (including) first.iterator count (setting containsPhase _)
+ else phaseDescriptors count (setting contains _.phaseName)
+ )
+ if (count == 0) warning(s"'$p' specifies no phase")
+ if (count > 1 && !isSpecial(p)) warning(s"'$p' selects $count phases")
+ if (!including && isSpecial(p)) globalError(s"-Yskip and -Ystop values must name phases: '$p'")
+ setting.clear()
}
- } catch {
- case ex: Throwable =>
- // this handler should not be nessasary, but it seems that `fsc`
- // eats exceptions if they appear here. Need to find out the cause for
- // this and fix it.
- inform("[reset] exception happened: "+ex);
- ex.printStackTrace();
- throw ex
+ }
+ // phases that are excluded; for historical reasons, these settings only select by phase name
+ val exclusions = List(ss.stopBefore, ss.stopAfter, ss.skip)
+ val inclusions = ss.visibleSettings collect {
+ case s: ss.PhasesSetting if !(exclusions contains s) => s.value
+ }
+ checkPhaseSettings(including = true, inclusions.toSeq: _*)
+ checkPhaseSettings(including = false, exclusions map (_.value): _*)
+
+ phase = first //parserPhase
+ first
}
// --------------- Miscellania -------------------------------
@@ -1374,16 +1301,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
def advancePhase() {
unitc = 0
phasec += 1
- refreshProgress
+ refreshProgress()
}
/** take note that a phase on a unit is completed
* (for progress reporting)
*/
def advanceUnit() {
unitc += 1
- refreshProgress
+ refreshProgress()
}
+ // for sbt
def cancel() { reporter.cancelled = true }
private def currentProgress = (phasec * size) + unitc
@@ -1402,7 +1330,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val namerPhase = phaseNamed("namer")
// val packageobjectsPhase = phaseNamed("packageobjects")
val typerPhase = phaseNamed("typer")
- val inlineclassesPhase = phaseNamed("inlineclasses")
+ // val inlineclassesPhase = phaseNamed("inlineclasses")
// val superaccessorsPhase = phaseNamed("superaccessors")
val picklerPhase = phaseNamed("pickler")
val refchecksPhase = phaseNamed("refchecks")
@@ -1419,18 +1347,16 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// val constructorsPhase = phaseNamed("constructors")
val flattenPhase = phaseNamed("flatten")
val mixinPhase = phaseNamed("mixin")
+ val delambdafyPhase = phaseNamed("delambdafy")
val cleanupPhase = phaseNamed("cleanup")
val icodePhase = phaseNamed("icode")
val inlinerPhase = phaseNamed("inliner")
- val inlineExceptionHandlersPhase = phaseNamed("inlineExceptionHandlers")
+ val inlineExceptionHandlersPhase = phaseNamed("inlinehandlers")
val closelimPhase = phaseNamed("closelim")
val dcePhase = phaseNamed("dce")
- val jvmPhase = phaseNamed("jvm")
- // val msilPhase = phaseNamed("msil")
+ // val jvmPhase = phaseNamed("jvm")
def runIsAt(ph: Phase) = globalPhase.id == ph.id
- def runIsPast(ph: Phase) = globalPhase.id > ph.id
- // def runIsAtBytecodeGen = (runIsAt(jvmPhase) || runIsAt(msilPhase))
def runIsAtOptimiz = {
runIsAt(inlinerPhase) || // listing phases in full for robustness when -Ystop-after has been given.
runIsAt(inlineExceptionHandlersPhase) ||
@@ -1446,34 +1372,35 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** add unit to be compiled in this run */
private def addUnit(unit: CompilationUnit) {
unitbuf += unit
- _unitbufSize += 1 // counting as they're added so size is cheap
compiledFiles += unit.source.file.path
}
private def checkDeprecatedSettings(unit: CompilationUnit) {
// issue warnings for any usage of deprecated settings
settings.userSetSettings filter (_.isDeprecated) foreach { s =>
- unit.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get)
+ currentRun.reporting.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get)
}
if (settings.target.value.contains("jvm-1.5"))
- unit.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated: use target for Java 1.6 or above.")
+ currentRun.reporting.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated: use target for Java 1.6 or above.")
}
/* An iterator returning all the units being compiled in this run */
/* !!! Note: changing this to unitbuf.toList.iterator breaks a bunch
of tests in tests/res. This is bad, it means the resident compiler
relies on an iterator of a mutable data structure reflecting changes
- made to the underlying structure (in whatever accidental way it is
- currently depending upon.)
+ made to the underlying structure.
*/
def units: Iterator[CompilationUnit] = unitbuf.iterator
def registerPickle(sym: Symbol): Unit = ()
/** does this run compile given class, module, or case factory? */
+ // NOTE: Early initialized members temporarily typechecked before the enclosing class, see typedPrimaryConstrBody!
+ // Here we work around that wrinkle by claiming that a early-initialized member is compiled in
+ // *every* run. This approximation works because this method is exclusively called with `this` == `currentRun`.
def compiles(sym: Symbol): Boolean =
if (sym == NoSymbol) false
else if (symSource.isDefinedAt(sym)) true
- else if (!sym.owner.isPackageClass) compiles(sym.enclosingTopLevelClass)
+ else if (!sym.isTopLevel) compiles(sym.enclosingTopLevelClassOrDummy)
else if (sym.isModuleClass) compiles(sym.sourceModule)
else false
@@ -1494,13 +1421,29 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
if (canCheck) {
phase = globalPhase
- if (globalPhase.id >= icodePhase.id) icodeChecker.checkICodes
- else treeChecker.checkTrees
+ if (globalPhase.id >= icodePhase.id) icodeChecker.checkICodes()
+ else treeChecker.checkTrees()
}
}
- private def showMembers() =
- opt.showNames foreach (x => showDef(x, opt.declsOnly, globalPhase))
+ private def showMembers() = {
+ // Allows for syntax like scalac -Xshow-class Random@erasure,typer
+ def splitClassAndPhase(str: String, term: Boolean): Name = {
+ def mkName(s: String) = if (term) newTermName(s) else newTypeName(s)
+ (str indexOf '@') match {
+ case -1 => mkName(str)
+ case idx =>
+ val phasePart = str drop (idx + 1)
+ settings.Yshow.tryToSetColon(phasePart split ',' toList)
+ mkName(str take idx)
+ }
+ }
+ if (settings.Xshowcls.isSetByUser)
+ showDef(splitClassAndPhase(settings.Xshowcls.value, term = false), declsOnly = false, globalPhase)
+
+ if (settings.Xshowobj.isSetByUser)
+ showDef(splitClassAndPhase(settings.Xshowobj.value, term = true), declsOnly = false, globalPhase)
+ }
// Similarly, this will only be created under -Yshow-syms.
object trackerFactory extends SymbolTrackers {
@@ -1508,7 +1451,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
lazy val trackers = currentRun.units.toList map (x => SymbolTracker(x))
def snapshot() = {
inform("\n[[symbol layout at end of " + phase + "]]")
- afterPhase(phase) {
+ exitingPhase(phase) {
trackers foreach { t =>
t.snapshot()
inform(t.show("Heading from " + phase.prev.name + " to " + phase.name))
@@ -1517,59 +1460,33 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
}
- def reportCompileErrors() {
- if (reporter.hasErrors) {
- for ((sym, file) <- symSource.iterator) {
- sym.reset(new loaders.SourcefileLoader(file))
- if (sym.isTerm)
- sym.moduleClass reset loaders.moduleClassLoader
- }
- }
- else {
- allConditionalWarnings foreach (_.summarize)
- if (seenMacroExpansionsFallingBack)
- warning("some macros could not be expanded and code fell back to overridden methods;"+
- "\nrecompiling with generated classfiles on the classpath might help.")
- // todo: migrationWarnings
- }
- }
+ /** Caching member symbols that are def-s in Definitions because they might change from Run to Run. */
+ val runDefinitions: definitions.RunDefinitions = new definitions.RunDefinitions
- /** Compile list of source files */
- def compileSources(_sources: List[SourceFile]) {
- val depSources = dependencyAnalysis calculateFiles _sources.distinct
- val sources = coreClassesFirst(depSources)
- // there is a problem already, e.g. a plugin was passed a bad option
- if (reporter.hasErrors)
- return
+ /** Compile list of source files,
+ * unless there is a problem already,
+ * such as a plugin was passed a bad option.
+ */
+ def compileSources(sources: List[SourceFile]) = if (!reporter.hasErrors) {
- // nothing to compile, but we should still report use of deprecated options
- if (sources.isEmpty) {
+ def checkDeprecations() = {
checkDeprecatedSettings(newCompilationUnit(""))
- reportCompileErrors()
- return
+ reporting.summarizeErrors()
}
- compileUnits(sources map (new CompilationUnit(_)), firstPhase)
- }
+ val units = sources map scripted map (new CompilationUnit(_))
- def compileUnits(units: List[CompilationUnit], fromPhase: Phase) {
- try compileUnitsInternal(units, fromPhase)
- catch { case ex: Throwable =>
- val shown = if (settings.verbose.value) {
- val pw = new java.io.PrintWriter(new java.io.StringWriter)
- ex.printStackTrace(pw)
- pw.toString
- } else ex.getClass.getName
- // ex.printStackTrace(Console.out) // DEBUG for fsc, note that error stacktraces do not print in fsc
- globalError(supplementErrorMessage("uncaught exception during compilation: " + shown))
- throw ex
+ units match {
+ case Nil => checkDeprecations() // nothing to compile, report deprecated options
+ case _ => compileUnits(units, firstPhase)
}
}
- private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) {
- doInvalidation()
+ def compileUnits(units: List[CompilationUnit], fromPhase: Phase): Unit =
+ compileUnitsInternal(units, fromPhase)
+ private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) {
units foreach addUnit
val startTime = currentTime
@@ -1577,73 +1494,74 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
checkDeprecatedSettings(unitbuf.head)
globalPhase = fromPhase
- while (globalPhase.hasNext && !reporter.hasErrors) {
+ while (globalPhase.hasNext && !reporter.hasErrors) {
val startTime = currentTime
phase = globalPhase
- globalPhase.run
+ globalPhase.run()
// progress update
informTime(globalPhase.description, startTime)
-
- if (opt.writeICodeAtICode || (opt.printPhase && runIsAtOptimiz)) {
+ val shouldWriteIcode = (
+ (settings.writeICode.isSetByUser && (settings.writeICode containsPhase globalPhase))
+ || (!settings.Xprint.doAllPhases && (settings.Xprint containsPhase globalPhase) && runIsAtOptimiz)
+ )
+ if (shouldWriteIcode) {
// Write *.icode files when -Xprint-icode or -Xprint:<some-optimiz-phase> was given.
writeICode()
- } else if (opt.printPhase || opt.printLate && runIsAt(cleanupPhase)) {
+ } else if ((settings.Xprint containsPhase globalPhase) || settings.printLate && runIsAt(cleanupPhase)) {
// print trees
- if (opt.showTrees) nodePrinters.printAll()
+ if (settings.Xshowtrees || settings.XshowtreesCompact || settings.XshowtreesStringified) nodePrinters.printAll()
else printAllUnits()
}
// print the symbols presently attached to AST nodes
- if (opt.showSymbols)
+ if (settings.Yshowsyms)
trackerFactory.snapshot()
// print members
- if (opt.showPhase)
+ if (settings.Yshow containsPhase globalPhase)
showMembers()
// browse trees with swing tree viewer
- if (opt.browsePhase)
+ if (settings.browse containsPhase globalPhase)
treeBrowser browse (phase.name, units)
// move the pointer
globalPhase = globalPhase.next
// run tree/icode checkers
- if (opt.checkPhase)
+ if (settings.check containsPhase globalPhase.prev)
runCheckers()
// output collected statistics
- if (opt.printStats)
+ if (settings.YstatisticsEnabled)
statistics.print(phase)
- advancePhase
+ advancePhase()
}
+ reporting.summarizeErrors()
+
if (traceSymbolActivity)
units map (_.body) foreach (traceSymbols recordSymbolsInTree _)
// In case no phase was specified for -Xshow-class/object, show it now for sure.
- if (opt.noShow)
+ if (settings.Yshow.isDefault)
showMembers()
- reportCompileErrors()
+ if (reporter.hasErrors) {
+ for ((sym, file) <- symSource.iterator) {
+ sym.reset(new loaders.SourcefileLoader(file))
+ if (sym.isTerm)
+ sym.moduleClass reset loaders.moduleClassLoader
+ }
+ }
symSource.keys foreach (x => resetPackageClass(x.owner))
- informTime("total", startTime)
- // record dependency data
- if (!dependencyAnalysis.off)
- dependencyAnalysis.saveDependencyAnalysis()
+ informTime("total", startTime)
// Clear any sets or maps created via perRunCaches.
perRunCaches.clearAll()
-
- // Reset project
- if (!stopPhase("namer")) {
- atPhase(namerPhase) {
- resetProjectClasses(RootClass)
- }
- }
}
/** Compile list of abstract files. */
@@ -1656,7 +1574,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
def compile(filenames: List[String]) {
try {
val sources: List[SourceFile] =
- if (isScriptRun && filenames.size > 1) returning(Nil)(_ => globalError("can only compile one script at a time"))
+ if (settings.script.isSetByUser && filenames.size > 1) returning(Nil)(_ => globalError("can only compile one script at a time"))
else filenames map getSourceFile
compileSources(sources)
@@ -1664,12 +1582,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
catch { case ex: IOException => globalError(ex.getMessage()) }
}
+ /** If this compilation is scripted, convert the source to a script source. */
+ private def scripted(s: SourceFile) = s match {
+ case b: BatchSourceFile if settings.script.isSetByUser => ScriptSourceFile(b)
+ case _ => s
+ }
+
/** Compile abstract file until `globalPhase`, but at least
* to phase "namer".
*/
def compileLate(file: AbstractFile) {
if (!compiledFiles(file.path))
- compileLate(new CompilationUnit(getSourceFile(file)))
+ compileLate(new CompilationUnit(scripted(getSourceFile(file))))
}
/** Compile abstract file until `globalPhase`, but at least to phase "namer".
@@ -1680,65 +1604,24 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
if (firstPhase ne null) { // we might get here during initialization, is a source is newer than the binary
val maxId = math.max(globalPhase.id, typerPhase.id)
firstPhase.iterator takeWhile (_.id < maxId) foreach (ph =>
- atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit))
- refreshProgress
+ enteringPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit))
+ refreshProgress()
}
}
- /** Reset package class to state at typer (not sure what this
- * is needed for?)
+ /** Reset package class to state at typer (not sure what this is needed for?)
*/
- private def resetPackageClass(pclazz: Symbol) {
- atPhase(firstPhase) {
- pclazz.setInfo(atPhase(typerPhase)(pclazz.info))
+ private def resetPackageClass(pclazz: Symbol): Unit = if (typerPhase != NoPhase) {
+ enteringPhase(firstPhase) {
+ pclazz.setInfo(enteringPhase(typerPhase)(pclazz.info))
}
if (!pclazz.isRoot) resetPackageClass(pclazz.owner)
}
-
- /**
- * Re-orders the source files to
- * 1. This Space Intentionally Left Blank
- * 2. LowPriorityImplicits / EmbeddedControls (i.e. parents of Predef)
- * 3. the rest
- *
- * 1 is to avoid cyclic reference errors.
- * 2 is due to the following. When completing "Predef" (*), typedIdent is called
- * for its parents (e.g. "LowPriorityImplicits"). typedIdent checks whether
- * the symbol reallyExists, which tests if the type of the symbol after running
- * its completer is != NoType.
- * If the "namer" phase has not yet run for "LowPriorityImplicits", the symbol
- * has a SourcefileLoader as type. Calling "doComplete" on it does nothing at
- * all, because the source file is part of the files to be compiled anyway.
- * So the "reallyExists" test will return "false".
- * Only after the namer, the symbol has a lazy type which actually computes
- * the info, and "reallyExists" behaves as expected.
- * So we need to make sure that the "namer" phase is run on predef's parents
- * before running it on predef.
- *
- * (*) Predef is completed early when calling "mkAttributedRef" during the
- * addition of "import Predef._" to sourcefiles. So this situation can't
- * happen for user classes.
- *
- */
- private def coreClassesFirst(files: List[SourceFile]) = {
- val goLast = 4
- def rank(f: SourceFile) = {
- if (f.file.container.name != "scala") goLast
- else f.file.name match {
- case "LowPriorityImplicits.scala" => 2
- case "StandardEmbeddings.scala" => 2
- case "EmbeddedControls.scala" => 2
- case "Predef.scala" => 3 /* Predef.scala before Any.scala, etc. */
- case _ => goLast
- }
- }
- files sortBy rank
- }
} // class Run
def printAllUnits() {
print("[[syntax trees at end of %25s]]".format(phase))
- afterPhase(phase)(currentRun.units foreach { unit =>
+ exitingPhase(phase)(currentRun.units foreach { unit =>
nodePrinters showUnit unit
})
}
@@ -1747,7 +1630,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
*/
def showDef(fullName: Name, declsOnly: Boolean, ph: Phase) = {
val boringOwners = Set[Symbol](definitions.AnyClass, definitions.AnyRefClass, definitions.ObjectClass)
- def phased[T](body: => T): T = afterPhase(ph)(body)
+ def phased[T](body: => T): T = exitingPhase(ph)(body)
def boringMember(sym: Symbol) = boringOwners(sym.owner)
def symString(sym: Symbol) = if (sym.isTerm) sym.defString else sym.toString
@@ -1792,10 +1675,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
private def writeICode() {
val printer = new icodes.TextPrinter(null, icodes.linearizer)
icodes.classes.values.foreach((cls) => {
- val suffix = if (cls.symbol.hasModuleFlag) "$.icode" else ".icode"
- var file = getFile(cls.symbol, suffix)
-// if (file.exists())
-// file = new File(file.getParentFile(), file.getName() + "1")
+ val moduleSfx = if (cls.symbol.hasModuleFlag) "$" else ""
+ val phaseSfx = if (settings.debug) phase else "" // only for debugging, appending the full phasename breaks windows build
+ val file = getFile(cls.symbol, s"$moduleSfx$phaseSfx.icode")
+
try {
val stream = new FileOutputStream(file)
printer.setWriter(new PrintWriter(stream, true))
@@ -1803,25 +1686,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
informProgress("wrote " + file)
} catch {
case ex: IOException =>
- if (opt.debug) ex.printStackTrace()
+ if (settings.debug) ex.printStackTrace()
globalError("could not write file " + file)
}
})
}
- // In order to not outright break code which overrides onlyPresentation (like sbt 0.7.5.RC0)
- // I restored and deprecated it. That would be enough to avoid the compilation
- // failure, but the override wouldn't accomplish anything. So now forInteractive
- // and forScaladoc default to onlyPresentation, which is the same as defaulting
- // to false except in old code. The downside is that this leaves us calling a
- // deprecated method: but I see no simple way out, so I leave it for now.
- def forJVM = opt.jvm
- override def forMSIL = opt.msil
- def forInteractive = onlyPresentation
- def forScaladoc = onlyPresentation
def createJavadoc = false
-
- @deprecated("Use forInteractive or forScaladoc, depending on what you're after", "2.9.0")
- def onlyPresentation = false
}
object Global {