diff options
author | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2013-09-12 10:26:46 -0700 |
---|---|---|
committer | Grzegorz Kossakowski <grzegorz.kossakowski@gmail.com> | 2013-09-12 10:26:46 -0700 |
commit | 33a819f61b8b9c19708e8ae22bf25adf6cc7ac24 (patch) | |
tree | 30fb8d7295f359aca662eb3fb96bc9f9a0646f1f | |
parent | e748f2ed8fda553fd8fe36499bac63aa115a82a9 (diff) | |
parent | dfe3fe335bf7358e04e7f422fecf16f445c14f2b (diff) | |
download | scala-33a819f61b8b9c19708e8ae22bf25adf6cc7ac24.tar.gz scala-33a819f61b8b9c19708e8ae22bf25adf6cc7ac24.tar.bz2 scala-33a819f61b8b9c19708e8ae22bf25adf6cc7ac24.zip |
Merge pull request #2859 from som-snytt/issue/7622-phaser
SI-7622 Clean Up Phase Assembly
52 files changed, 549 insertions, 264 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index f1f5130fb8..bab0768ca9 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -27,7 +27,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { |-- Notes on option parsing -- |Boolean settings are always false unless set. |Where multiple values are accepted, they should be comma-separated. - | example: -Xplugin:plugin1,plugin2 + | example: -Xplugin:option1,option2 |<phases> means one or a comma-separated list of: | (partial) phase names, phase ids, phase id ranges, or the string "all". | example: -Xprint:all prints all phases. @@ -80,23 +80,23 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { def xusageMsg = createUsageMsg("Possible advanced", shouldExplain = true, _.isAdvanced) def yusageMsg = createUsageMsg("Possible private", shouldExplain = true, _.isPrivate) - // If any of these settings is set, the compiler shouldn't start; - // an informative message of some sort should be printed instead. - def shouldStopWithInfo = { - import settings.{ Setting => _, _ } - Set[BooleanSetting](help, Xhelp, Yhelp, showPlugins, showPhases) exists (_.value) - } + /** For info settings, compiler should just print a message and quit. */ + def shouldStopWithInfo = settings.isInfo def getInfoMessage(global: Global): String = { import settings._ - if (help.value) usageMsg + global.pluginOptionsHelp - else if (Xhelp.value) xusageMsg - else if (Yhelp.value) yusageMsg - else if (showPlugins.value) global.pluginDescriptions - else if (showPhases.value) global.phaseDescriptions + ( - if (debug.value) "\n" + global.phaseFlagDescriptions else "" + if (help) usageMsg + global.pluginOptionsHelp + else if (Xhelp) xusageMsg + else if (Yhelp) yusageMsg + else if (showPlugins) global.pluginDescriptions + else if (showPhases) global.phaseDescriptions + ( + if (debug) "\n" + global.phaseFlagDescriptions else "" ) - else "" + else if (genPhaseGraph.isSetByUser) { + val components = global.phaseNames // global.phaseDescriptors // one initializes + s"Phase graph of ${components.size} components output to ${genPhaseGraph.value}*.dot." + } + else "" } /** diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 0e3b2993c7..f6d4b26cda 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 @@ -428,9 +428,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 } @@ -450,9 +452,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" @@ -626,18 +628,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) {} } } @@ -665,7 +666,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( @@ -695,7 +696,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 @@ -713,13 +714,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 */ @@ -737,22 +746,50 @@ class Global(var currentSettings: Settings, var reporter: Reporter) phaseDescriptors map (_.phaseName) } - /** A description of the phases that will run */ - def phaseDescriptions: String = { + /** A description of the phases that will run in this configuration, or all if -Ydebug. */ + def phaseDescriptions: String = phaseHelp("description", elliptically = true, phasesDescMap) + + /** 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 + } + phaseHelp("new flags", elliptically = false, fmt) + } + + /** 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 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 = (0 /: phaseNames)(_ max _.length) + val maxName = phaseNames map (_.length) max val width = maxName min Limit val maxDesc = MaxCol - (width + 6) // descriptions not novels - val fmt = if (settings.verbose) s"%${maxName}s %2s %s%n" + 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", "description") - val line2 = fmt.format("----------", "--", "-----------") + 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 fmtable(s: String) = new Formattable { + 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) { @@ -768,36 +805,19 @@ class Global(var currentSettings: Settings, var reporter: Reporter) formatter.out.append(w) } } - 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) + "..." - val descs = phaseDescriptors.zipWithIndex map { - case (ph, idx) => fmt.format(fmtable(ph.phaseName), idx + 1, fmtable(phasesDescMap(ph))) - } - line1 :: line2 :: descs mkString - } - /** Summary of the per-phase values of nextFlags and newFlags, shown - * with -Xshow-phases if -Ydebug also given. - */ - 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) + + // 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 @@ -1145,7 +1165,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 @@ -1184,64 +1204,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 ae71eb7255..996f6efe55 100644 --- a/src/compiler/scala/tools/nsc/PhaseAssembly.scala +++ b/src/compiler/scala/tools/nsc/PhaseAssembly.scala @@ -6,15 +6,12 @@ package scala.tools.nsc -import java.io.{ BufferedWriter, FileWriter } import scala.collection.mutable import scala.language.postfixOps -/** - * PhaseAssembly - * Trait made to separate the constraint solving of the phase order from - * the rest of the compiler. See SIP 00002 - * +/** Converts an unordered morass of components into an order that + * satisfies their mutual constraints. + * @see SIP 00002. You have read SIP 00002? */ trait PhaseAssembly { self: Global => @@ -23,18 +20,16 @@ trait PhaseAssembly { * Aux datastructure for solving the constraint system * The depency graph container with helper methods for node and edge creation */ - class DependencyGraph { + private class DependencyGraph { - /** - * Simple edge with to and from refs - */ - class Edge(var frm: Node, var to: Node, var hard: Boolean) + /** Simple edge with to and from refs */ + case class Edge(var frm: Node, var to: Node, var hard: Boolean) /** * Simple node with name and object ref for the phase object, * also sets of in and out going dependencies */ - class Node(name: String) { + case class Node(name: String) { val phasename = name var phaseobj: Option[List[SubComponent]] = None val after = new mutable.HashSet[Edge]() @@ -51,8 +46,8 @@ trait PhaseAssembly { val nodes = new mutable.HashMap[String,Node]() val edges = new mutable.HashSet[Edge]() - /* Given a phase object, get the node for this phase object. If the - * node object does not exist, then create it. + /** Given a phase object, get the node for this phase object. If the + * node object does not exist, then create it. */ def getNodeByPhase(phs: SubComponent): Node = { val node: Node = getNodeByPhase(phs.phaseName) @@ -105,9 +100,8 @@ trait PhaseAssembly { */ def collapseHardLinksAndLevels(node: Node, lvl: Int) { if (node.visited) { - throw new FatalError( - "Cycle in compiler phase dependencies detected, phase " + - node.phasename + " reacted twice!") + dump("phase-cycle") + throw new FatalError(s"Cycle in phase dependencies detected at ${node.phasename}, created phase-cycle.dot") } if (node.level < lvl) node.level = lvl @@ -140,7 +134,8 @@ trait PhaseAssembly { var hardlinks = edges.filter(_.hard) for (hl <- hardlinks) { if (hl.frm.after.size > 1) { - throw new FatalError("phase " + hl.frm.phasename + " want to run right after " + hl.to.phasename + ", but some phase has declared to run before " + hl.frm.phasename + ". Re-run with -Xgenerate-phase-graph <filename> to better see the problem.") + dump("phase-order") + throw new FatalError(s"Phase ${hl.frm.phasename} can't follow ${hl.to.phasename}, created phase-order.dot") } } @@ -153,15 +148,9 @@ trait PhaseAssembly { if (sanity.length == 0) { throw new FatalError("There is no runs right after dependency, where there should be one! This is not supposed to happen!") } else if (sanity.length > 1) { - var msg = "Multiple phases want to run right after the phase " + sanity.head.to.phasename + "\n" - msg += "Phases: " - sanity = sanity sortBy (_.frm.phasename) - for (edge <- sanity) { - msg += edge.frm.phasename + ", " - } - msg += "\nRe-run with -Xgenerate-phase-graph <filename> to better see the problem." - throw new FatalError(msg) - + dump("phase-order") + val following = (sanity map (_.frm.phasename)).sorted mkString "," + throw new FatalError(s"Multiple phases want to run right after ${sanity.head.to.phasename}; followers: $following; created phase-order.dot") } else { val promote = hl.to.before.filter(e => (!e.hard)) @@ -199,39 +188,38 @@ trait PhaseAssembly { } } } + + def dump(title: String = "phase-assembly") = graphToDotFile(this, s"$title.dot") } - /* Method called from computePhaseDescriptors in class Global - */ - def buildCompilerFromPhasesSet(): List[SubComponent] = { + + /** Called by Global#computePhaseDescriptors to compute phase order. */ + def computePhaseAssembly(): List[SubComponent] = { // Add all phases in the set to the graph val graph = phasesSetToDepGraph(phasesSet) + val dot = if (settings.genPhaseGraph.isSetByUser) Some(settings.genPhaseGraph.value) else None + // Output the phase dependency graph at this stage - if (settings.genPhaseGraph.value != "") - graphToDotFile(graph, settings.genPhaseGraph.value + "1.dot") + def dump(stage: Int) = dot foreach (n => graphToDotFile(graph, s"$n-$stage.dot")) + + dump(1) // Remove nodes without phaseobj graph.removeDanglingNodes() - // Output the phase dependency graph at this stage - if (settings.genPhaseGraph.value != "") - graphToDotFile(graph, settings.genPhaseGraph.value + "2.dot") + dump(2) // Validate and Enforce hardlinks / runsRightAfter and promote nodes down the tree graph.validateAndEnforceHardlinks() - // Output the phase dependency graph at this stage - if (settings.genPhaseGraph.value != "") - graphToDotFile(graph, settings.genPhaseGraph.value + "3.dot") + dump(3) // test for cycles, assign levels and collapse hard links into nodes graph.collapseHardLinksAndLevels(graph.getNodeByPhase("parser"), 1) - // Output the phase dependency graph at this stage - if (settings.genPhaseGraph.value != "") - graphToDotFile(graph, settings.genPhaseGraph.value + "4.dot") + dump(4) // assemble the compiler graph.compilerPhaseList() @@ -288,16 +276,11 @@ trait PhaseAssembly { sbuf.append("digraph G {\n") for (edge <- graph.edges) { sbuf.append("\"" + edge.frm.allPhaseNames + "(" + edge.frm.level + ")" + "\"->\"" + edge.to.allPhaseNames + "(" + edge.to.level + ")" + "\"") - if (! edge.frm.phaseobj.get.head.internal) { - extnodes += edge.frm - } - edge.frm.phaseobj match { case None => null case Some(ln) => if(ln.size > 1) fatnodes += edge.frm } - edge.to.phaseobj match { case None => null case Some(ln) => if(ln.size > 1) fatnodes += edge.to } - if (edge.hard) { - sbuf.append(" [color=\"#0000ff\"]\n") - } else { - sbuf.append(" [color=\"#000000\"]\n") - } + if (!edge.frm.phaseobj.get.head.internal) extnodes += edge.frm + edge.frm.phaseobj foreach (phobjs => if (phobjs.tail.nonEmpty) fatnodes += edge.frm ) + edge.to.phaseobj foreach (phobjs => if (phobjs.tail.nonEmpty) fatnodes += edge.to ) + val color = if (edge.hard) "#0000ff" else "#000000" + sbuf.append(s""" [color="$color"]\n""") } for (node <- extnodes) { sbuf.append("\"" + node.allPhaseNames + "(" + node.level + ")" + "\" [color=\"#00ff00\"]\n") @@ -306,10 +289,7 @@ trait PhaseAssembly { sbuf.append("\"" + node.allPhaseNames + "(" + node.level + ")" + "\" [color=\"#0000ff\"]\n") } sbuf.append("}\n") - val out = new BufferedWriter(new FileWriter(filename)) - out.write(sbuf.toString) - out.flush() - out.close() + import reflect.io._ + for (d <- settings.outputDirs.getSingleOutput if !d.isVirtual) Path(d.file) / File(filename) writeAll sbuf.toString } - } 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/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index bde17b28fc..c49f23852f 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -18,6 +18,8 @@ abstract class ClosureElimination extends SubComponent { val phaseName = "closelim" + override val enabled: Boolean = settings.Xcloselim + /** Create a new phase */ override def newPhase(p: Phase) = new ClosureEliminationPhase(p) diff --git a/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala b/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala index 43c8527f41..64a0727440 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala @@ -34,6 +34,8 @@ abstract class ConstantOptimization extends SubComponent { /** Create a new phase */ override def newPhase(p: Phase) = new ConstantOptimizationPhase(p) + override val enabled: Boolean = settings.YconstOptimization + /** * The constant optimization phase. */ diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 7511da8b00..193aae37b6 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -22,6 +22,8 @@ abstract class DeadCodeElimination extends SubComponent { val phaseName = "dce" + override val enabled: Boolean = settings.Xdce + /** Create a new phase */ override def newPhase(p: Phase) = new DeadCodeEliminationPhase(p) diff --git a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala index cecabda171..f4e97a91d8 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala @@ -57,6 +57,8 @@ abstract class InlineExceptionHandlers extends SubComponent { /** Create a new phase */ override def newPhase(p: Phase) = new InlineExceptionHandlersPhase(p) + override def enabled = settings.inlineHandlers + /** * Inlining Exception Handlers */ diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 09095879bf..181f4bde4e 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -49,6 +49,8 @@ abstract class Inliners extends SubComponent { val phaseName = "inliner" + override val enabled: Boolean = settings.inline + /** Debug - for timing the inliner. */ /**** private def timed[T](s: String, body: => T): T = { diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala index 4fd6ba7d9d..1578caff26 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala @@ -41,12 +41,31 @@ abstract class Plugin { */ val global: Global - /** Handle any plugin-specific options. The `-P:plugname:` part - * will not be present. + def options: List[String] = { + // Process plugin options of form plugin:option + def namec = name + ":" + global.settings.pluginOptions.value filter (_ startsWith namec) map (_ stripPrefix namec) + } + + /** Handle any plugin-specific options. + * The user writes `-P:plugname:opt1,opt2`, + * but the plugin sees `List(opt1, opt2)`. + * The plugin can opt out of further processing + * by returning false. For example, if the plugin + * has an "enable" flag, now would be a good time + * to sit on the bench. + * @param options plugin arguments + * @param error error function + * @return true to continue, or false to opt out */ - def processOptions(options: List[String], error: String => Unit) { - if (!options.isEmpty) - error("Error: " + name + " has no options") + def init(options: List[String], error: String => Unit): Boolean = { + processOptions(options, error) + true + } + + @deprecated("use Plugin#init instead", since="2.11") + def processOptions(options: List[String], error: String => Unit): Unit = { + if (!options.isEmpty) error(s"Error: $name takes no options") } /** A description of this plugin's options, suitable as a response diff --git a/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala b/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala index c6e1af7ea4..a6df08c331 100644 --- a/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala +++ b/src/compiler/scala/tools/nsc/plugins/PluginComponent.scala @@ -15,12 +15,10 @@ package plugins */ abstract class PluginComponent extends SubComponent { - /** Internal flag to tell external from internal phases */ + /** By definition, plugin phases are externally provided. */ final override val internal = false - /** Phases supplied by plugins should not have to supply the - * runsRightAfter constraint, but can override it. - */ + /** Only plugins are granted a reprieve from specifying whether they follow. */ val runsRightAfter: Option[String] = None /** Useful for -Xshow-phases. */ diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 8f7794fa90..4769705404 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -78,27 +78,18 @@ trait Plugins { val plugs = pick(roughPluginsList, Set(), (phasesSet map (_.phaseName)).toSet) - /* Verify requirements are present. */ + // Verify required plugins are present. for (req <- settings.require.value ; if !(plugs exists (_.name == req))) globalError("Missing required plugin: " + req) - /* Process plugin options. */ - def namec(plug: Plugin) = plug.name + ":" - def optList(xs: List[String], p: Plugin) = xs filter (_ startsWith namec(p)) - def doOpts(p: Plugin): List[String] = - optList(settings.pluginOptions.value, p) map (_ stripPrefix namec(p)) + // Verify no non-existent plugin given with -P + for { + opt <- settings.pluginOptions.value + if !(plugs exists (opt startsWith _.name + ":")) + } globalError("bad option: -P:" + opt) - for (p <- plugs) { - val opts = doOpts(p) - if (!opts.isEmpty) - p.processOptions(opts, globalError) - } - - /* Verify no non-existent plugin given with -P */ - for (opt <- settings.pluginOptions.value ; if plugs forall (p => optList(List(opt), p).isEmpty)) - globalError("bad option: -P:" + opt) - - plugs + // Plugins may opt out, unless we just want to show info + plugs filter (p => p.init(p.options, globalError) || (settings.debug && settings.isInfo)) } lazy val plugins: List[Plugin] = loadPlugins() diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index b37d0ba283..96c93a838b 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -41,6 +41,12 @@ trait ScalaSettings extends AbsScalaSettings /** Enabled under -optimise. */ def optimiseSettings = List[BooleanSetting](inline, inlineHandlers, Xcloselim, Xdce, YconstOptimization) + /** If any of these settings is enabled, the compiler should print a message and exit. */ + def infoSettings = List[Setting](help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph) + + /** Is an info setting set? */ + def isInfo = infoSettings exists (_.isSetByUser) + /** Internal use - syntax enhancements. */ private class EnableSettings[T <: BooleanSetting](val s: T) { def enabling(toEnable: List[BooleanSetting]): s.type = s withPostSetHook (_ => toEnable foreach (_.value = s.value)) diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 29480576ea..98d0695865 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -8,7 +8,7 @@ trait CPSUtils { val global: Global import global._ - var cpsEnabled = false + val cpsEnabled: Boolean val verbose: Boolean = System.getProperty("cpsVerbose", "false") == "true" def vprintln(x: =>Any): Unit = if (verbose) println(x) diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala index d3b02d74f4..a7e82e949b 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveCPSPlugin.scala @@ -11,41 +11,44 @@ class SelectiveCPSPlugin(val global: Global) extends Plugin { val name = "continuations" val description = "applies selective cps conversion" - val anfPhase = new {val global = SelectiveCPSPlugin.this.global } with SelectiveANFTransform() { + val pluginEnabled = options contains "enable" + + val anfPhase = new { + val global = SelectiveCPSPlugin.this.global + val cpsEnabled = pluginEnabled + override val enabled = cpsEnabled + } with SelectiveANFTransform { val runsAfter = List("pickler") } - val cpsPhase = new {val global = SelectiveCPSPlugin.this.global } with SelectiveCPSTransform() { + val cpsPhase = new { + val global = SelectiveCPSPlugin.this.global + val cpsEnabled = pluginEnabled + override val enabled = cpsEnabled + } with SelectiveCPSTransform { val runsAfter = List("selectiveanf") override val runsBefore = List("uncurry") } val components = List[PluginComponent](anfPhase, cpsPhase) - val checker = new { val global: SelectiveCPSPlugin.this.global.type = SelectiveCPSPlugin.this.global } with CPSAnnotationChecker + val checker = new { + val global: SelectiveCPSPlugin.this.global.type = SelectiveCPSPlugin.this.global + val cpsEnabled = pluginEnabled + } with CPSAnnotationChecker + + // TODO don't muck up global with unused checkers global.addAnnotationChecker(checker.checker) global.analyzer.addAnalyzerPlugin(checker.plugin) global.log("instantiated cps plugin: " + this) - def setEnabled(flag: Boolean) = { - checker.cpsEnabled = flag - anfPhase.cpsEnabled = flag - cpsPhase.cpsEnabled = flag - } - - // TODO: require -enabled command-line flag - - override def processOptions(options: List[String], error: String => Unit) = { - var enabled = false - for (option <- options) { - if (option == "enable") { - enabled = true - } else { - error("Option not understood: "+option) - } + override def init(options: List[String], error: String => Unit) = { + options foreach { + case "enable" => // in initializer + case arg => error(s"Bad argument: $arg") } - setEnabled(enabled) + pluginEnabled } override val optionsHelp: Option[String] = diff --git a/src/partest-extras/scala/tools/partest/IcodeComparison.scala b/src/partest-extras/scala/tools/partest/IcodeComparison.scala new file mode 100644 index 0000000000..5da51c9d58 --- /dev/null +++ b/src/partest-extras/scala/tools/partest/IcodeComparison.scala @@ -0,0 +1,74 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.partest + +import scala.tools.partest.nest.FileManager.compareContents +import scala.compat.Platform.EOL + +/** A class for testing icode. All you need is this in a + * partest source file -- + * {{{ + * object Test extends IcodeComparison + * }}} + * -- and the generated output will be the icode for everything + * in that file. See scaladoc for possible customizations. + * TODO promote me to partest + */ +abstract class IcodeComparison extends DirectTest { + /** The phase after which icode is printed. + * Override to check icode at a different point, + * but you can't print at a phase that is not enabled + * in this compiler run. Defaults to "icode". + */ + def printIcodeAfterPhase = "icode" + + /** When comparing the output of two phases, this is + * the other phase of interest, normally the preceding + * phase. Defaults to "icode" for tests of optimizer phases. + */ + def printSuboptimalIcodeAfterPhase = "icode" + + /** The source code to compile defaults to the test file. + * I.e., the test file compiles itself. For a comparison, + * the test file will be compiled three times. + */ + def code = testPath.slurp() + + /** By default, the test code is compiled with -usejavacp. */ + override def extraSettings: String = "-usejavacp" + + /** Compile the test code and return the contents of all + * (sorted) .icode files, which are immediately deleted. + * @param arg0 at least one arg is required + * @param args must include -Xprint-icode:phase + */ + def collectIcode(arg0: String, args: String*): List[String] = { + compile("-d" :: testOutput.path :: arg0 :: args.toList : _*) + val icodeFiles = testOutput.files.toList filter (_ hasExtension "icode") + + try icodeFiles sortBy (_.name) flatMap (f => f.lines.toList) + finally icodeFiles foreach (f => f.delete()) + } + + /** Collect icode at the default phase, `printIcodeAfterPhase`. */ + def collectIcode(): List[String] = collectIcode(s"-Xprint-icode:$printIcodeAfterPhase") + + /** Default show is showComparison. May be overridden for showIcode or similar. */ + def show() = showComparison() + + /** Compile the test code with and without optimization, and + * then print the diff of the icode. + */ + def showComparison() = { + val lines1 = collectIcode(s"-Xprint-icode:$printSuboptimalIcodeAfterPhase") + val lines2 = collectIcode("-optimise", s"-Xprint-icode:$printIcodeAfterPhase") + + println(compareContents(lines1, lines2)) + } + + /** Print icode at the default phase, `printIcodeAfterPhase`. */ + def showIcode() = println(collectIcode() mkString EOL) +} 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..6dfe072913 100755 --- a/test/files/neg/t6446-additional.check +++ b/test/files/neg/t6446-additional.check @@ -22,6 +22,7 @@ superaccessors 6 add super accessors in traits and nested classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls icode 22 generate portable intermediate code +#partest -optimise inliner 23 optimization: do inlining inlinehandlers 24 optimization: inline exception handlers closelim 25 optimization: eliminate uncalled closures @@ -29,4 +30,9 @@ 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 +#partest !-optimise + jvm 23 generate JVM bytecode + ploogin 24 A sample phase that does so many things it's kind of hard... + terminal 25 the last phase during a compilation run +#partest diff --git a/test/files/neg/t6446-missing.check b/test/files/neg/t6446-missing.check index 6e5bdcf07c..d4afa9b630 100755 --- a/test/files/neg/t6446-missing.check +++ b/test/files/neg/t6446-missing.check @@ -23,10 +23,15 @@ superaccessors 6 add super accessors in traits and nested classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls icode 22 generate portable intermediate code +#partest !-optimise + jvm 23 generate JVM bytecode + terminal 24 the last phase during a compilation run +#partest -optimise inliner 23 optimization: do inlining inlinehandlers 24 optimization: inline exception handlers closelim 25 optimization: eliminate uncalled closures 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 +#partest diff --git a/test/files/neg/t6446-show-phases.check b/test/files/neg/t6446-show-phases.check index a1bf408506..10a9e08b86 100644 --- a/test/files/neg/t6446-show-phases.check +++ b/test/files/neg/t6446-show-phases.check @@ -22,10 +22,15 @@ superaccessors 6 add super accessors in traits and nested classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls icode 22 generate portable intermediate code +#partest !-optimise + jvm 23 generate JVM bytecode + terminal 24 the last phase during a compilation run +#partest -optimise inliner 23 optimization: do inlining inlinehandlers 24 optimization: inline exception handlers closelim 25 optimization: eliminate uncalled closures 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 +#partest diff --git a/test/files/neg/t7494-multi-right-after.check b/test/files/neg/t7494-multi-right-after.check index 9c6fdbe91f..151d177414 100644 --- a/test/files/neg/t7494-multi-right-after.check +++ b/test/files/neg/t7494-multi-right-after.check @@ -1,3 +1 @@ -error: Multiple phases want to run right after the phase explicitouter -Phases: erasure, multi-rafter, -Re-run with -Xgenerate-phase-graph <filename> to better see the problem. +error: Multiple phases want to run right after explicitouter; followers: erasure,multi-rafter; created phase-order.dot diff --git a/test/files/neg/t7494-no-options.check b/test/files/neg/t7494-no-options.check index c197d2a671..0bde84c96c 100644 --- a/test/files/neg/t7494-no-options.check +++ b/test/files/neg/t7494-no-options.check @@ -1,4 +1,4 @@ -error: Error: ploogin has no options +error: Error: ploogin takes no options phase name id description ---------- -- ----------- parser 1 parse source into ASTs, perform simple desugaring @@ -23,6 +23,11 @@ superaccessors 6 add super accessors in traits and nested classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls icode 22 generate portable intermediate code +#partest !-optimise + jvm 23 generate JVM bytecode + ploogin 24 A sample phase that does so many things it's kind of hard... + terminal 25 the last phase during a compilation run +#partest -optimise inliner 23 optimization: do inlining inlinehandlers 24 optimization: inline exception handlers closelim 25 optimization: eliminate uncalled closures @@ -30,4 +35,5 @@ 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 +#partest diff --git a/test/files/neg/t7494-right-after-before.check b/test/files/neg/t7494-right-after-before.check index 9b57a7711b..7e83daab4a 100644 --- a/test/files/neg/t7494-right-after-before.check +++ b/test/files/neg/t7494-right-after-before.check @@ -1 +1 @@ -error: phase erasure want to run right after explicitouter, but some phase has declared to run before erasure. Re-run with -Xgenerate-phase-graph <filename> to better see the problem. +error: Phase erasure can't follow explicitouter, created phase-order.dot diff --git a/test/files/neg/t7622-cyclic-dependency.check b/test/files/neg/t7622-cyclic-dependency.check new file mode 100644 index 0000000000..3546964f5f --- /dev/null +++ b/test/files/neg/t7622-cyclic-dependency.check @@ -0,0 +1 @@ +error: Cycle in phase dependencies detected at cyclicdependency1, created phase-cycle.dot diff --git a/test/pending/neg/t7494-cyclic-dependency/ThePlugin.scala b/test/files/neg/t7622-cyclic-dependency/ThePlugin.scala index bd94ce60d7..7a905ae32d 100644 --- a/test/pending/neg/t7494-cyclic-dependency/ThePlugin.scala +++ b/test/files/neg/t7622-cyclic-dependency/ThePlugin.scala @@ -20,7 +20,7 @@ class ThePlugin(val global: Global) extends Plugin { val phaseName = ThePlugin.this.name + "1" - def newPhase(prev: Phase) = new ThePhase(prev) + def newPhase(prev: Phase) = new ThePhase(prev, phaseName) } private object thePhase2 extends PluginComponent { @@ -30,11 +30,10 @@ class ThePlugin(val global: Global) extends Plugin { val phaseName = ThePlugin.this.name + "2" - def newPhase(prev: Phase) = new ThePhase(prev) + def newPhase(prev: Phase) = new ThePhase(prev, phaseName) } - private class ThePhase(prev: Phase) extends Phase(prev) { - def name = ThePlugin.this.name + private class ThePhase(prev: Phase, val name: String) extends Phase(prev) { def run {} } } diff --git a/test/pending/neg/t7494-cyclic-dependency/sample_2.flags b/test/files/neg/t7622-cyclic-dependency/sample_2.flags index db25b88a12..db25b88a12 100644 --- a/test/pending/neg/t7494-cyclic-dependency/sample_2.flags +++ b/test/files/neg/t7622-cyclic-dependency/sample_2.flags diff --git a/test/pending/neg/t7494-cyclic-dependency/sample_2.scala b/test/files/neg/t7622-cyclic-dependency/sample_2.scala index 73cdc64e40..73cdc64e40 100644 --- a/test/pending/neg/t7494-cyclic-dependency/sample_2.scala +++ b/test/files/neg/t7622-cyclic-dependency/sample_2.scala diff --git a/test/pending/neg/t7494-cyclic-dependency/scalac-plugin.xml b/test/files/neg/t7622-cyclic-dependency/scalac-plugin.xml index 2558d6fd03..2558d6fd03 100644 --- a/test/pending/neg/t7494-cyclic-dependency/scalac-plugin.xml +++ b/test/files/neg/t7622-cyclic-dependency/scalac-plugin.xml diff --git a/test/files/neg/t7622-missing-dependency.check b/test/files/neg/t7622-missing-dependency.check new file mode 100644 index 0000000000..a0d0e30870 --- /dev/null +++ b/test/files/neg/t7622-missing-dependency.check @@ -0,0 +1,2 @@ +error: Phase 'myplugin' requires: List(missing) +one error found diff --git a/test/files/neg/t7622-missing-dependency/ThePlugin.scala b/test/files/neg/t7622-missing-dependency/ThePlugin.scala new file mode 100644 index 0000000000..a87cbb8e45 --- /dev/null +++ b/test/files/neg/t7622-missing-dependency/ThePlugin.scala @@ -0,0 +1,33 @@ +package scala.test.plugins + +import scala.tools.nsc +import nsc.Global +import nsc.Phase +import nsc.plugins.Plugin +import nsc.plugins.PluginComponent + +class ThePlugin(val global: Global) extends Plugin { + import global._ + + val name = "myplugin" + val description = "Declares one plugin with a missing requirement" + val components = List[PluginComponent](thePhase) + + private object thePhase extends PluginComponent { + val global = ThePlugin.this.global + + val runsAfter = List[String]("typer") + + val phaseName = ThePlugin.this.name + + override val requires = List("missing") + + def newPhase(prev: Phase) = new ThePhase(prev) + } + + private class ThePhase(prev: Phase) extends Phase(prev) { + def name = thePhase.phaseName + def run {} + } +} + diff --git a/test/files/neg/t7622-missing-dependency/sample_2.flags b/test/files/neg/t7622-missing-dependency/sample_2.flags new file mode 100644 index 0000000000..d69035100e --- /dev/null +++ b/test/files/neg/t7622-missing-dependency/sample_2.flags @@ -0,0 +1 @@ +-Xplugin:. -Xplugin-require:myplugin diff --git a/test/files/neg/t7622-missing-dependency/sample_2.scala b/test/files/neg/t7622-missing-dependency/sample_2.scala new file mode 100644 index 0000000000..73cdc64e40 --- /dev/null +++ b/test/files/neg/t7622-missing-dependency/sample_2.scala @@ -0,0 +1,6 @@ + +package sample + +// just a sample that is compiled with the sample plugin enabled +object Sample extends App { +} diff --git a/test/files/neg/t7622-missing-dependency/scalac-plugin.xml b/test/files/neg/t7622-missing-dependency/scalac-plugin.xml new file mode 100644 index 0000000000..3c14061dce --- /dev/null +++ b/test/files/neg/t7622-missing-dependency/scalac-plugin.xml @@ -0,0 +1,5 @@ +<plugin> + <name>myplugin</name> + <classname>scala.test.plugins.ThePlugin</classname> +</plugin> + diff --git a/test/files/neg/t7622-missing-required.check b/test/files/neg/t7622-missing-required.check new file mode 100644 index 0000000000..5982178581 --- /dev/null +++ b/test/files/neg/t7622-missing-required.check @@ -0,0 +1,2 @@ +error: Missing required plugin: special-plugin +one error found diff --git a/test/files/neg/t7622-missing-required.flags b/test/files/neg/t7622-missing-required.flags new file mode 100644 index 0000000000..65deac6feb --- /dev/null +++ b/test/files/neg/t7622-missing-required.flags @@ -0,0 +1 @@ +-Xplugin-require:special-plugin diff --git a/test/files/neg/t7622-missing-required.scala b/test/files/neg/t7622-missing-required.scala new file mode 100644 index 0000000000..a0ba487b24 --- /dev/null +++ b/test/files/neg/t7622-missing-required.scala @@ -0,0 +1,4 @@ + +// the amazing features of this trait +// are unlocked by compiling with a special plugin. +trait Amazing diff --git a/test/files/neg/t7622-multi-followers.check b/test/files/neg/t7622-multi-followers.check new file mode 100644 index 0000000000..d123853a5b --- /dev/null +++ b/test/files/neg/t7622-multi-followers.check @@ -0,0 +1 @@ +error: Multiple phases want to run right after parser; followers: multi1,multi2; created phase-order.dot diff --git a/test/files/neg/t7622-multi-followers/ThePlugin.scala b/test/files/neg/t7622-multi-followers/ThePlugin.scala new file mode 100644 index 0000000000..e7a49a9be6 --- /dev/null +++ b/test/files/neg/t7622-multi-followers/ThePlugin.scala @@ -0,0 +1,44 @@ +package scala.test.plugins + +import scala.tools.nsc +import nsc.Global +import nsc.Phase +import nsc.plugins.Plugin +import nsc.plugins.PluginComponent + +class ThePlugin(val global: Global) extends Plugin { + import global._ + + val name = "multi" + val description = "Declares two phases that both follow parser" + val components = List[PluginComponent](thePhase1,thePhase2) + + private object thePhase1 extends PluginComponent { + val global = ThePlugin.this.global + + val runsAfter = List[String]() + + override val runsRightAfter = Some("parser") + + val phaseName = ThePlugin.this.name + "1" + + def newPhase(prev: Phase) = new ThePhase(prev, phaseName) + } + + private object thePhase2 extends PluginComponent { + val global = ThePlugin.this.global + + val runsAfter = List[String]() + + override val runsRightAfter = Some("parser") + + val phaseName = ThePlugin.this.name + "2" + + def newPhase(prev: Phase) = new ThePhase(prev, phaseName) + } + + private class ThePhase(prev: Phase, val name: String) extends Phase(prev) { + def run {} + } +} + diff --git a/test/files/neg/t7622-multi-followers/sample_2.flags b/test/files/neg/t7622-multi-followers/sample_2.flags new file mode 100644 index 0000000000..d2e83e9723 --- /dev/null +++ b/test/files/neg/t7622-multi-followers/sample_2.flags @@ -0,0 +1 @@ +-Xplugin:. -Xplugin-require:multi diff --git a/test/files/neg/t7622-multi-followers/sample_2.scala b/test/files/neg/t7622-multi-followers/sample_2.scala new file mode 100644 index 0000000000..73cdc64e40 --- /dev/null +++ b/test/files/neg/t7622-multi-followers/sample_2.scala @@ -0,0 +1,6 @@ + +package sample + +// just a sample that is compiled with the sample plugin enabled +object Sample extends App { +} diff --git a/test/files/neg/t7622-multi-followers/scalac-plugin.xml b/test/files/neg/t7622-multi-followers/scalac-plugin.xml new file mode 100644 index 0000000000..2558d6fd03 --- /dev/null +++ b/test/files/neg/t7622-multi-followers/scalac-plugin.xml @@ -0,0 +1,5 @@ +<plugin> + <name>ignored</name> + <classname>scala.test.plugins.ThePlugin</classname> +</plugin> + diff --git a/test/files/run/inline-ex-handlers.scala b/test/files/run/inline-ex-handlers.scala index 33e794b940..964594d258 100644 --- a/test/files/run/inline-ex-handlers.scala +++ b/test/files/run/inline-ex-handlers.scala @@ -1,6 +1,6 @@ -import scala.tools.partest.IcodeTest +import scala.tools.partest.IcodeComparison -object Test extends IcodeTest { +object Test extends IcodeComparison { override def printIcodeAfterPhase = "inlinehandlers" } diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check index 61b947214c..a6e53fbe92 100644 --- a/test/files/run/programmatic-main.check +++ b/test/files/run/programmatic-main.check @@ -22,11 +22,6 @@ superaccessors 6 add super accessors in traits and nested classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls icode 22 generate portable intermediate code - inliner 23 optimization: do inlining -inlinehandlers 24 optimization: inline exception handlers - closelim 25 optimization: eliminate uncalled closures - 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 + jvm 23 generate JVM bytecode + terminal 24 the last phase during a compilation run diff --git a/test/files/run/t5313.scala b/test/files/run/t5313.scala index febfd9c3ed..4cbc29ba5d 100644 --- a/test/files/run/t5313.scala +++ b/test/files/run/t5313.scala @@ -1,6 +1,6 @@ -import scala.tools.partest.IcodeTest +import scala.tools.partest.IcodeComparison -object Test extends IcodeTest { +object Test extends IcodeComparison { override def printIcodeAfterPhase = "dce" override def extraSettings: String = super.extraSettings + " -optimize" @@ -48,7 +48,7 @@ object Test extends IcodeTest { override def show() { val storeLocal = "STORE_LOCAL" - val lines1 = collectIcode("") filter (_ contains storeLocal) map (x => x.drop(x.indexOf(storeLocal))) + val lines1 = collectIcode() filter (_ contains storeLocal) map (x => x.drop(x.indexOf(storeLocal))) println(lines1 mkString "\n") } } diff --git a/test/files/run/t6102.check b/test/files/run/t6102.check index 97e455647b..4e8efa7b6d 100644 --- a/test/files/run/t6102.check +++ b/test/files/run/t6102.check @@ -20,10 +20,12 @@ [running phase mixin on t6102.scala] [running phase cleanup on t6102.scala] [running phase icode on t6102.scala] +#partest -optimise [running phase inliner on t6102.scala] [running phase inlinehandlers on t6102.scala] [running phase closelim on t6102.scala] [running phase constopt on t6102.scala] +#partest [running phase dce on t6102.scala] [running phase jvm on icode] hello diff --git a/test/files/run/t6102.flags b/test/files/run/t6102.flags index 72fe7b1aa0..726e2a997f 100644 --- a/test/files/run/t6102.flags +++ b/test/files/run/t6102.flags @@ -1 +1 @@ - -Ydead-code -Ydebug -Xfatal-warnings +-Ydead-code -Ydebug -Xfatal-warnings diff --git a/test/files/run/t6288b-jump-position.scala b/test/files/run/t6288b-jump-position.scala index e22a1ab120..c5f3bbe788 100644 --- a/test/files/run/t6288b-jump-position.scala +++ b/test/files/run/t6288b-jump-position.scala @@ -1,6 +1,6 @@ -import scala.tools.partest.IcodeTest +import scala.tools.partest.IcodeComparison -object Test extends IcodeTest { +object Test extends IcodeComparison { override def code = """object Case3 { // 01 | def unapply(z: Any): Option[Int] = Some(-1) // 02 @@ -15,8 +15,5 @@ object Test extends IcodeTest { | } |}""".stripMargin - override def show() { - val lines1 = collectIcode("") - println(lines1 mkString "\n") - } + override def show() = showIcode() } diff --git a/test/files/run/t6955.check b/test/files/run/t6955.check deleted file mode 100644 index 0cfbf08886..0000000000 --- a/test/files/run/t6955.check +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/test/files/run/t6955.scala b/test/files/run/t6955.scala index 980aa420cc..329af688e4 100644 --- a/test/files/run/t6955.scala +++ b/test/files/run/t6955.scala @@ -1,4 +1,4 @@ -import scala.tools.partest.IcodeTest +import scala.tools.partest.IcodeComparison // this class should compile to code that uses switches (twice) class Switches { @@ -20,9 +20,15 @@ class Switches { } } -object Test extends IcodeTest { +object Test extends IcodeComparison { // ensure we get two switches out of this -- ignore the rest of the output for robustness // exclude the constant we emit for the "SWITCH ..." string below (we get the icode for all the code you see in this file) - override def show() = println(collectIcode("").filter(x => x.indexOf("SWITCH ...") >= 0 && x.indexOf("CONSTANT(") == -1).size) + override def show() = { + val expected = 2 + val actual = (collectIcode() filter { + x => x.indexOf("SWITCH ...") >= 0 && x.indexOf("CONSTANT(") == -1 + }).size + assert(actual == expected) + } } diff --git a/test/files/run/t6956.check b/test/files/run/t6956.check deleted file mode 100644 index 0cfbf08886..0000000000 --- a/test/files/run/t6956.check +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/test/files/run/t6956.scala b/test/files/run/t6956.scala index 4a6583ca45..3569adf483 100644 --- a/test/files/run/t6956.scala +++ b/test/files/run/t6956.scala @@ -1,4 +1,4 @@ -import scala.tools.partest.IcodeTest +import scala.tools.partest.IcodeComparison class Switches { private[this] final val ONE = 1 @@ -18,9 +18,14 @@ class Switches { } } -object Test extends IcodeTest { +object Test extends IcodeComparison { // ensure we get two switches out of this -- ignore the rest of the output for robustness // exclude the constant we emit for the "SWITCH ..." string below (we get the icode for all the code you see in this file) - override def show() = println(collectIcode("").filter(x => x.indexOf("SWITCH ...") >= 0 && x.indexOf("CONSTANT(") == -1).size) + override def show() = { + val expected = 2 + val actual = (collectIcode() filter { + x => x.indexOf("SWITCH ...") >= 0 && x.indexOf("CONSTANT(") == -1 + }).size + assert(actual == expected) + } } - diff --git a/test/files/run/test-cpp.scala b/test/files/run/test-cpp.scala index f9fa85c4d0..c6eda56776 100644 --- a/test/files/run/test-cpp.scala +++ b/test/files/run/test-cpp.scala @@ -12,9 +12,9 @@ * instead of 'l'. */ -import scala.tools.partest.IcodeTest +import scala.tools.partest.IcodeComparison -object Test extends IcodeTest { +object Test extends IcodeComparison { override def printIcodeAfterPhase = "dce" } |