summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala165
-rw-r--r--src/compiler/scala/tools/nsc/PhaseAssembly.scala2
-rw-r--r--src/compiler/scala/tools/nsc/SubComponent.scala21
-rw-r--r--src/reflect/scala/reflect/internal/Phase.scala9
-rwxr-xr-xtest/files/neg/t6446-additional.check2
-rwxr-xr-xtest/files/neg/t6446-missing.check2
-rw-r--r--test/files/neg/t6446-show-phases.check2
-rw-r--r--test/files/neg/t7494-no-options.check2
-rw-r--r--test/files/run/programmatic-main.check2
9 files changed, 133 insertions, 74 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 3f2d759a6d..a0a9a2fdb7 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -47,7 +47,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// the mirror --------------------------------------------------
override def isCompilerUniverse = true
- override val useOffsetPositions = !currentSettings.Yrangepos.value
+ override val useOffsetPositions = !currentSettings.Yrangepos
class GlobalMirror extends Roots(NoSymbol) {
val universe: self.type = self
@@ -430,9 +430,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// phaseName = "parser"
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 }
@@ -452,9 +454,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"
@@ -628,18 +630,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// phaseName = "terminal"
object terminal extends {
val global: Global.this.type = Global.this
- val phaseName = "terminal"
+ } with SubComponent {
+ final val phaseName = "terminal"
val runsAfter = List("jvm")
val runsRightAfter = None
- } with SubComponent {
- private var cache: Option[GlobalPhase] = None
- def reset(): Unit = cache = None
+ override val terminal = true
- def newPhase(prev: Phase): GlobalPhase =
- cache getOrElse returning(new TerminalPhase(prev))(x => cache = Some(x))
-
- 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) {}
}
}
@@ -667,7 +668,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(
@@ -697,7 +698,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
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
@@ -715,13 +716,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 */
@@ -1147,7 +1156,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
def newUnitParser(unit: CompilationUnit): UnitParser = new UnitParser(unit)
def newUnitParser(code: String): UnitParser = newUnitParser(newCompilationUnit(code))
- /** 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 {
/** Have been running into too many init order issues with Run
@@ -1186,64 +1195,100 @@ 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 phasec: Int = 0 // phases completed
+ private var unitc: Int = 0 // units completed this phase
private var _unitbufSize = 0
def size = _unitbufSize
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
+ if (phs.isEmpty || !phs.last.terminal) {
+ val t = if (phaseDescriptors.last.terminal) phaseDescriptors.last else terminal
+ phs :+ t
+ } else phs
}
- // Link them together.
- phaseLinks.foldLeft(parserPhase)((chain, ph) => ph newPhase chain)
- parserPhase
+ // 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()
+ }
+ }
+ // 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
}
/** Reset all classes contained in current project, as determined by
diff --git a/src/compiler/scala/tools/nsc/PhaseAssembly.scala b/src/compiler/scala/tools/nsc/PhaseAssembly.scala
index c962cc8bb7..45502a19d6 100644
--- a/src/compiler/scala/tools/nsc/PhaseAssembly.scala
+++ b/src/compiler/scala/tools/nsc/PhaseAssembly.scala
@@ -194,7 +194,7 @@ trait PhaseAssembly {
/** Called by Global#computePhaseDescriptors to compute phase order. */
- def buildCompilerFromPhasesSet(): List[SubComponent] = {
+ def computePhaseAssembly(): List[SubComponent] = {
// Add all phases in the set to the graph
val graph = phasesSetToDepGraph(phasesSet)
diff --git a/src/compiler/scala/tools/nsc/SubComponent.scala b/src/compiler/scala/tools/nsc/SubComponent.scala
index 9b8582ae02..b21d156145 100644
--- a/src/compiler/scala/tools/nsc/SubComponent.scala
+++ b/src/compiler/scala/tools/nsc/SubComponent.scala
@@ -19,19 +19,30 @@ abstract class SubComponent {
/** The name of the phase */
val phaseName: String
- /** List of phase names, this phase should run after */
+ /** Names of phases that must run before this phase. */
val runsAfter: List[String]
- /** List of phase names, this phase should run before */
+ /** Names of phases that must run after this phase. Default is `Nil`. */
val runsBefore: List[String] = Nil
- /** Phase name this phase will attach itself to, not allowing any phase to come between it
- * and the phase name declared */
+ /** Name of the phase that this phase must follow immediately. */
val runsRightAfter: Option[String]
- /** Internal flag to tell external from internal phases */
+ /** Names of phases required by this component. Default is `Nil`. */
+ val requires: List[String] = Nil
+
+ /** Is this component enabled? Default is true. */
+ def enabled: Boolean = true
+
+ /** True if this phase is not provided by a plug-in. */
val internal: Boolean = true
+ /** True if this phase runs before all other phases. Usually, `parser`. */
+ val initial: Boolean = false
+
+ /** True if this phase runs after all other phases. Usually, `terminal`. */
+ val terminal: Boolean = false
+
/** SubComponent are added to a HashSet and two phases are the same if they have the same name */
override def hashCode() = phaseName.hashCode()
diff --git a/src/reflect/scala/reflect/internal/Phase.scala b/src/reflect/scala/reflect/internal/Phase.scala
index 450c90ed56..1ecc202a07 100644
--- a/src/reflect/scala/reflect/internal/Phase.scala
+++ b/src/reflect/scala/reflect/internal/Phase.scala
@@ -26,11 +26,14 @@ abstract class Phase(val prev: Phase) {
)
def flagMask: Long = fmask
- private var nx: Phase = this
+ private var nx: Phase = NoPhase
- def next: Phase = nx
+ // does anyone rely on next == this for terminus?
+ def next: Phase = if (nx eq NoPhase) this else nx
def hasNext = next != this
- def iterator = Iterator.iterate(this)(_.next) takeWhile (p => p.next != p)
+ // this definition excludes the terminal phase
+ //def iterator = Iterator.iterate(this)(_.nx) takeWhile (p => p.next != p)
+ def iterator = Iterator.iterate(this)(_.nx) takeWhile (_ ne NoPhase)
def name: String
def description: String = name
diff --git a/test/files/neg/t6446-additional.check b/test/files/neg/t6446-additional.check
index 24201c07c2..be928095d3 100755
--- a/test/files/neg/t6446-additional.check
+++ b/test/files/neg/t6446-additional.check
@@ -29,4 +29,4 @@ inlinehandlers 24 optimization: inline exception handlers
dce 27 optimization: eliminate dead code
jvm 28 generate JVM bytecode
ploogin 29 A sample phase that does so many things it's kind of hard...
- terminal 30 The last phase in the compiler chain
+ terminal 30 the last phase during a compilation run
diff --git a/test/files/neg/t6446-missing.check b/test/files/neg/t6446-missing.check
index 6e5bdcf07c..8ede1bd260 100755
--- a/test/files/neg/t6446-missing.check
+++ b/test/files/neg/t6446-missing.check
@@ -29,4 +29,4 @@ inlinehandlers 24 optimization: inline exception handlers
constopt 26 optimization: optimize null and other constants
dce 27 optimization: eliminate dead code
jvm 28 generate JVM bytecode
- terminal 29 The last phase in the compiler chain
+ terminal 29 the last phase during a compilation run
diff --git a/test/files/neg/t6446-show-phases.check b/test/files/neg/t6446-show-phases.check
index a1bf408506..73cbfb6659 100644
--- a/test/files/neg/t6446-show-phases.check
+++ b/test/files/neg/t6446-show-phases.check
@@ -28,4 +28,4 @@ inlinehandlers 24 optimization: inline exception handlers
constopt 26 optimization: optimize null and other constants
dce 27 optimization: eliminate dead code
jvm 28 generate JVM bytecode
- terminal 29 The last phase in the compiler chain
+ terminal 29 the last phase during a compilation run
diff --git a/test/files/neg/t7494-no-options.check b/test/files/neg/t7494-no-options.check
index c197d2a671..efc91a6215 100644
--- a/test/files/neg/t7494-no-options.check
+++ b/test/files/neg/t7494-no-options.check
@@ -30,4 +30,4 @@ inlinehandlers 24 optimization: inline exception handlers
dce 27 optimization: eliminate dead code
jvm 28 generate JVM bytecode
ploogin 29 A sample phase that does so many things it's kind of hard...
- terminal 30 The last phase in the compiler chain
+ terminal 30 the last phase during a compilation run
diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check
index 61b947214c..cc325cafc0 100644
--- a/test/files/run/programmatic-main.check
+++ b/test/files/run/programmatic-main.check
@@ -28,5 +28,5 @@ inlinehandlers 24 optimization: inline exception handlers
constopt 26 optimization: optimize null and other constants
dce 27 optimization: eliminate dead code
jvm 28 generate JVM bytecode
- terminal 29 The last phase in the compiler chain
+ terminal 29 the last phase during a compilation run