diff options
Diffstat (limited to 'src/compiler')
34 files changed, 415 insertions, 480 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 8b9ef2178b..f3a2d49697 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 @@ -431,9 +431,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 } @@ -453,9 +455,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" @@ -629,18 +631,17 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // phaseName = "terminal" object terminal extends { val global: Global.this.type = Global.this + } with SubComponent { 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) {} } } @@ -668,7 +669,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( @@ -698,7 +699,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 @@ -716,13 +717,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 */ @@ -740,22 +749,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) { @@ -771,36 +808,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 @@ -1157,7 +1177,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) 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 { /** Have been running into too many init order issues with Run @@ -1196,64 +1216,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/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index d7a32c3be0..5922b4bbbf 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -133,90 +133,6 @@ trait TreeDSL { def ==>(body: Tree): CaseDef = CaseDef(pat, guard, body) } - /** VODD, if it's not obvious, means ValOrDefDef. This is the - * common code between a tree based on a pre-existing symbol and - * one being built from scratch. - */ - trait VODDStart { - def name: Name - def defaultMods: Modifiers - def defaultTpt: Tree - def defaultPos: Position - - type ResultTreeType <: ValOrDefDef - def mkTree(rhs: Tree): ResultTreeType - def ===(rhs: Tree): ResultTreeType - - private var _tpt: Tree = null - private var _pos: Position = null - - def withType(tp: Type): this.type = { - _tpt = TypeTree(tp) - this - } - def withPos(pos: Position): this.type = { - _pos = pos - this - } - - final def mods = defaultMods - final def tpt = if (_tpt == null) defaultTpt else _tpt - final def pos = if (_pos == null) defaultPos else _pos - } - trait SymVODDStart extends VODDStart { - def sym: Symbol - def symType: Type - - def name = sym.name - def defaultMods = Modifiers(sym.flags) - def defaultTpt = TypeTree(symType) setPos sym.pos.focus - def defaultPos = sym.pos - - final def ===(rhs: Tree): ResultTreeType = - atPos(pos)(mkTree(rhs) setSymbol sym) - } - trait ValCreator { - self: VODDStart => - - type ResultTreeType = ValDef - def mkTree(rhs: Tree): ValDef = ValDef(mods, name.toTermName, tpt, rhs) - } - trait DefCreator { - self: VODDStart => - - def tparams: List[TypeDef] - def vparamss: List[List[ValDef]] - - type ResultTreeType = DefDef - def mkTree(rhs: Tree): DefDef = DefDef(mods, name.toTermName, tparams, vparamss, tpt, rhs) - } - - class DefSymStart(val sym: Symbol) extends SymVODDStart with DefCreator { - def symType = sym.tpe.finalResultType - def tparams = sym.typeParams map TypeDef - def vparamss = mapParamss(sym)(ValDef) - } - class ValSymStart(val sym: Symbol) extends SymVODDStart with ValCreator { - def symType = sym.tpe - } - - trait TreeVODDStart extends VODDStart { - def defaultMods = NoMods - def defaultTpt = TypeTree() - def defaultPos = NoPosition - - final def ===(rhs: Tree): ResultTreeType = - if (pos == NoPosition) mkTree(rhs) - else atPos(pos)(mkTree(rhs)) - } - - class ValTreeStart(val name: Name) extends TreeVODDStart with ValCreator { - } - class DefTreeStart(val name: Name) extends TreeVODDStart with DefCreator { - def tparams: List[TypeDef] = Nil - def vparamss: List[List[ValDef]] = ListOfNil - } - class IfStart(cond: Tree, thenp: Tree) { def THEN(x: Tree) = new IfStart(cond, x) def ELSE(elsep: Tree) = If(cond, thenp, elsep) @@ -230,46 +146,23 @@ trait TreeDSL { def CASE(pat: Tree): CaseStart = new CaseStart(pat, EmptyTree) def DEFAULT: CaseStart = new CaseStart(WILD.empty, EmptyTree) - class SymbolMethods(target: Symbol) { - def IS_NULL() = REF(target) OBJ_EQ NULL - def GET() = fn(REF(target), nme.get) - def ARGS = target.paramss.head - } - - /** Top level accessible. */ - def MATCHERROR(arg: Tree) = Throw(MatchErrorClass.tpe, arg) - def THROW(sym: Symbol, msg: Tree): Throw = Throw(sym.tpe, msg.TOSTRING()) - def NEW(tpt: Tree, args: Tree*): Tree = New(tpt, List(args.toList)) - def DEF(sym: Symbol): DefSymStart = new DefSymStart(sym) - def VAL(sym: Symbol): ValSymStart = new ValSymStart(sym) - def AND(guards: Tree*) = - if (guards.isEmpty) EmptyTree - else guards reduceLeft gen.mkAnd + def NOT(tree: Tree) = Select(tree, Boolean_not) + def AND(guards: Tree*) = if (guards.isEmpty) EmptyTree else guards reduceLeft gen.mkAnd def IF(tree: Tree) = new IfStart(tree, EmptyTree) def TRY(tree: Tree) = new TryStart(tree, Nil, EmptyTree) def BLOCK(xs: Tree*) = Block(xs.init.toList, xs.last) - def NOT(tree: Tree) = Select(tree, Boolean_not) - def SOME(xs: Tree*) = Apply(SomeClass.companionSymbol, makeTupleTerm(xs.toList, flattenUnary = true)) + def SOME(xs: Tree*) = Apply(SomeClass.companionSymbol, treeBuilder.makeTupleTerm(xs.toList, flattenUnary = true)) /** Typed trees from symbols. */ - def THIS(sym: Symbol) = gen.mkAttributedThis(sym) - def ID(sym: Symbol) = gen.mkAttributedIdent(sym) - def REF(sym: Symbol) = gen.mkAttributedRef(sym) - def REF(pre: Type, sym: Symbol) = gen.mkAttributedRef(pre, sym) - - def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree = trees match { - case Nil => UNIT - case List(tree) if flattenUnary => tree - case _ => Apply(TupleClass(trees.length).companionModule, trees: _*) - } + def REF(sym: Symbol) = gen.mkAttributedRef(sym) + def REF(pre: Type, sym: Symbol) = gen.mkAttributedRef(pre, sym) /** Implicits - some of these should probably disappear **/ implicit def mkTreeMethods(target: Tree): TreeMethods = new TreeMethods(target) implicit def mkTreeMethodsFromSymbol(target: Symbol): TreeMethods = new TreeMethods(Ident(target)) - implicit def mkSymbolMethodsFromSymbol(target: Symbol): SymbolMethods = new SymbolMethods(target) /** (foo DOT bar) might be simply a Select, but more likely it is to be immediately * followed by an Apply. We don't want to add an actual apply method to arbitrary diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index f3b842b170..07e24900e9 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -703,14 +703,10 @@ self => /* ---------- TREE CONSTRUCTION ------------------------------------------- */ - def atPos[T <: Tree](offset: Int)(t: T): T = - global.atPos(r2p(offset, offset, in.lastOffset max offset))(t) - def atPos[T <: Tree](start: Int, point: Int)(t: T): T = - global.atPos(r2p(start, point, in.lastOffset max start))(t) - def atPos[T <: Tree](start: Int, point: Int, end: Int)(t: T): T = - global.atPos(r2p(start, point, end))(t) - def atPos[T <: Tree](pos: Position)(t: T): T = - global.atPos(pos)(t) + def atPos[T <: Tree](offset: Int)(t: T): T = atPos(r2p(offset, offset, in.lastOffset max offset))(t) + def atPos[T <: Tree](start: Int, point: Int)(t: T): T = atPos(r2p(start, point, in.lastOffset max start))(t) + def atPos[T <: Tree](start: Int, point: Int, end: Int)(t: T): T = atPos(r2p(start, point, end))(t) + def atPos[T <: Tree](pos: Position)(t: T): T = global.atPos(pos)(t) def atInPos[T <: Tree](t: T): T = atPos(o2p(in.offset))(t) def setInPos[T <: Tree](t: T): T = t setPos o2p(in.offset) @@ -946,21 +942,23 @@ self => ts += annotType() } newLineOptWhenFollowedBy(LBRACE) - atPos(t.pos.startOrPoint) { - if (in.token == LBRACE) { - // Warn if they are attempting to refine Unit; we can't be certain it's - // scala.Unit they're refining because at this point all we have is an - // identifier, but at a later stage we lose the ability to tell an empty - // refinement from no refinement at all. See bug #284. - for (Ident(name) <- ts) name.toString match { - case "Unit" | "scala.Unit" => - warning("Detected apparent refinement of Unit; are you missing an '=' sign?") - case _ => - } - CompoundTypeTree(Template(ts.toList, emptyValDef, refinement())) - } - else - makeIntersectionTypeTree(ts.toList) + val types = ts.toList + val braceOffset = in.offset + val hasRefinement = in.token == LBRACE + val refinements = if (hasRefinement) refinement() else Nil + // Warn if they are attempting to refine Unit; we can't be certain it's + // scala.Unit they're refining because at this point all we have is an + // identifier, but at a later stage we lose the ability to tell an empty + // refinement from no refinement at all. See bug #284. + if (hasRefinement) types match { + case Ident(name) :: Nil if name endsWith "Unit" => warning(braceOffset, "Detected apparent refinement of Unit; are you missing an '=' sign?") + case _ => + } + // The second case includes an empty refinement - refinements is empty, but + // it still gets a CompoundTypeTree. + ts.toList match { + case tp :: Nil if !hasRefinement => tp // single type, no refinement, already positioned + case tps => atPos(t.pos.startOrPoint)(CompoundTypeTree(Template(tps, emptyValDef, refinements))) } } @@ -2776,8 +2774,10 @@ self => def readAppliedParent() = { val start = in.offset val parent = startAnnotType() - val argss = if (in.token == LPAREN) multipleArgumentExprs() else Nil - parents += atPos(start)((parent /: argss)(Apply.apply)) + parents += (in.token match { + case LPAREN => atPos(start)((parent /: multipleArgumentExprs())(Apply.apply)) + case _ => parent + }) } readAppliedParent() while (in.token == WITH) { in.nextToken(); readAppliedParent() } @@ -2818,6 +2818,8 @@ self => case tdef @ TypeDef(mods, name, tparams, rhs) => deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.") treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs) + case docdef @ DocDef(comm, rhs) => + treeCopy.DocDef(docdef, comm, rhs) case stat if !stat.isEmpty => syntaxError(stat.pos, "only concrete field definitions allowed in early object initialization section", skipIt = false) EmptyTree diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index d622031e9d..91ff530e05 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -205,11 +205,6 @@ abstract class TreeBuilder { atPos(r2p(start, end, end + op.length)) { new PostfixSelect(od, op.encode) } } - /** A type tree corresponding to (possibly unary) intersection type */ - def makeIntersectionTypeTree(tps: List[Tree]): Tree = - if (tps.tail.isEmpty) tps.head - else CompoundTypeTree(Template(tps, emptyValDef, Nil)) - /** Create tree representing a while loop */ def makeWhile(startPos: Int, cond: Tree, body: Tree): Tree = { val lname = freshTermName(nme.WHILE_PREFIX) 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 32f0571e83..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)) @@ -182,7 +188,6 @@ trait ScalaSettings extends AbsScalaSettings */ val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.") val Yidedebug = BooleanSetting("-Yide-debug", "Generate, validate and output trees using the interactive compiler.") - val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") val Yissuedebug = BooleanSetting("-Yissue-debug", "Print stack traces when a context issues an error.") val YmacrodebugLite = BooleanSetting("-Ymacro-debug-lite", "Trace essential macro-related activities.") val YmacrodebugVerbose = BooleanSetting("-Ymacro-debug-verbose", "Trace all macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.") @@ -193,6 +198,9 @@ trait ScalaSettings extends AbsScalaSettings val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.") val Yquasiquotedebug = BooleanSetting("-Yquasiquote-debug", "Trace quasiquote-related activities.") + // TODO 2.12 Remove + val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") withDeprecationMessage("Use -Ytyper-debug") enabling(List(Ytyperdebug)) + /** Groups of Settings. */ val future = BooleanSetting("-Xfuture", "Turn on future language features.") enabling futureSettings diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index b16ba91916..0135190256 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -94,7 +94,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { if (!isFinal) varSym.addAnnotation(VolatileAttr) - val varDef = typedPos( VAL(varSym) === forInit ) + val varDef = typedPos(ValDef(varSym, forInit)) newStaticMembers append transform(varDef) val varInit = typedPos( REF(varSym) === forInit ) @@ -155,13 +155,13 @@ abstract class CleanUp extends Transform with ast.TreeDSL { val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe BLOCK( - VAL(methodCache) === getPolyCache, + ValDef(methodCache, getPolyCache), IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK( REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)), REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache)) ) ENDIF, - VAL(methodSym) === (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym)), + ValDef(methodSym, (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym))), IF (REF(methodSym) OBJ_NE NULL) . THEN (Return(REF(methodSym))) ELSE { @@ -372,7 +372,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { qual = REF(sym) BLOCK( - VAL(sym) === qual0, + ValDef(sym, qual0), callAsReflective(mparams map (_.tpe), resType) ) } @@ -543,7 +543,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { ) // create field definition and initialization - val stfieldDef = theTyper.typedPos(pos)(VAL(stfieldSym) === rhs) + val stfieldDef = theTyper.typedPos(pos)(ValDef(stfieldSym, rhs)) val stfieldInit = theTyper.typedPos(pos)(REF(stfieldSym) === rhs) // add field definition to new defs diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index c74fc620ca..31855bc1ad 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -268,7 +268,7 @@ abstract class Erasure extends AddInterfaces else abbrvTag(sym).toString } else if (sym.isDerivedValueClass) { - val unboxed = sym.derivedValueClassUnbox.info.finalResultType + val unboxed = sym.derivedValueClassUnbox.tpe_*.finalResultType val unboxedSeen = (tp memberType sym.derivedValueClassUnbox).finalResultType def unboxedMsg = if (unboxed == unboxedSeen) "" else s", seen within ${sym.simpleName} as $unboxedSeen" logResult(s"Erasure of value class $sym (underlying type $unboxed$unboxedMsg) is") { @@ -513,7 +513,7 @@ abstract class Erasure extends AddInterfaces maybeWrap(bridgingCall) } - atPos(bridge.pos)(DefDef(bridge, rhs)) + DefDef(bridge, rhs) } } diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index d6a6e027cb..a2bf5bf9e5 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -90,7 +90,7 @@ abstract class ExplicitOuter extends InfoTransform sym expandName clazz sym.referenced = clazz - sym setInfo MethodType(Nil, restpe) + sym setInfo MethodType(Nil, restpe.widen) } def newOuterField(clazz: Symbol) = { val accFlags = SYNTHETIC | ARTIFACT | PARAMACCESSOR | ( if (clazz.isEffectivelyFinal) PrivateLocal else PROTECTED ) @@ -216,7 +216,7 @@ abstract class ExplicitOuter extends InfoTransform * values for outer parameters of constructors. * The class provides methods for referencing via outer. */ - abstract class OuterPathTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + abstract class OuterPathTransformer(unit: CompilationUnit) extends TypingTransformer(unit) with UnderConstructionTransformer { /** The directly enclosing outer parameter, if we are in a constructor */ protected var outerParam: Symbol = NoSymbol @@ -225,9 +225,10 @@ abstract class ExplicitOuter extends InfoTransform * * Will return `EmptyTree` if there is no outer accessor because of a premature self reference. */ - protected def outerValue: Tree = - if (outerParam != NoSymbol) ID(outerParam) - else outerSelect(THIS(currentClass)) + protected def outerValue: Tree = outerParam match { + case NoSymbol => outerSelect(gen.mkAttributedThis(currentClass)) + case outerParam => gen.mkAttributedIdent(outerParam) + } /** Select and apply outer accessor from 'base' * The result is typed but not positioned. @@ -276,16 +277,6 @@ abstract class ExplicitOuter extends InfoTransform } - /** The stack of class symbols in which a call to this() or to the super - * constructor, or early definition is active - */ - protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls contains clazz - protected val selfOrSuperCalls = mutable.Stack[Symbol]() - @inline protected def inSelfOrSuperCall[A](sym: Symbol)(a: => A) = { - selfOrSuperCalls push sym - try a finally selfOrSuperCalls.pop() - } - override def transform(tree: Tree): Tree = { val savedOuterParam = outerParam try { @@ -299,10 +290,7 @@ abstract class ExplicitOuter extends InfoTransform } case _ => } - if ((treeInfo isSelfOrSuperConstrCall tree) || (treeInfo isEarlyDef tree)) - inSelfOrSuperCall(currentOwner.owner)(super.transform(tree)) - else - super.transform(tree) + super.transform(tree) } finally outerParam = savedOuterParam } @@ -368,8 +356,7 @@ abstract class ExplicitOuter extends InfoTransform /** The definition tree of the outer accessor of current class */ - def outerFieldDef: Tree = - VAL(outerField(currentClass)) === EmptyTree + def outerFieldDef: Tree = ValDef(outerField(currentClass)) /** The definition tree of the outer accessor of current class */ diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 56ec49e962..6a405295cf 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -205,7 +205,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { def makeExtensionMethodSymbol = { val extensionName = extensionNames(origMeth).head.toTermName val extensionMeth = ( - companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) + companion.moduleClass.newMethod(extensionName, tree.pos.focus, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) setAnnotations origMeth.annotations ) origMeth.removeAnnotation(TailrecClass) // it's on the extension method, now. @@ -230,9 +230,14 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { .changeOwner(origMeth -> extensionMeth) new SubstututeRecursion(origMeth, extensionMeth, unit).transform(tree) } + val castBody = + if (extensionBody.tpe <:< extensionMono.finalResultType) + extensionBody + else + gen.mkCastPreservingAnnotations(extensionBody, extensionMono.finalResultType) // SI-7818 e.g. mismatched existential skolems // Record the extension method. Later, in `Extender#transformStats`, these will be added to the companion object. - extensionDefs(companion) += atPos(tree.pos)(DefDef(extensionMeth, extensionBody)) + extensionDefs(companion) += DefDef(extensionMeth, castBody) // These three lines are assembling Foo.bar$extension[T1, T2, ...]($this) // which leaves the actual argument application for extensionCall. @@ -289,7 +294,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { val origThis = extensionMeth.owner.companionClass val baseType = qual.tpe.baseType(origThis) val allTargs = targs.map(_.tpe) ::: baseType.typeArgs - val fun = gen.mkAttributedTypeApply(THIS(extensionMeth.owner), extensionMeth, allTargs) + val fun = gen.mkAttributedTypeApply(gen.mkAttributedThis(extensionMeth.owner), extensionMeth, allTargs) allArgss.foldLeft(fun)(Apply(_, _)) } case _ => super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 515fa66cfa..acef2a50d8 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -436,8 +436,15 @@ abstract class LambdaLift extends InfoTransform { private def liftDef(tree: Tree): Tree = { val sym = tree.symbol val oldOwner = sym.owner - if (sym.owner.isAuxiliaryConstructor && sym.isMethod) // # bug 1909 - sym setFlag STATIC + if (sym.isMethod && isUnderConstruction(sym.owner.owner)) { // # bug 1909 + if (sym.isModule) { // Yes, it can be a module and a method, see comments on `isModuleNotMethod`! + // TODO promote to an implementation restriction if we can reason that this *always* leads to VerifyError. + // See neg/t1909-object.scala + def msg = s"SI-1909 Unable to STATICally lift $sym, which is defined in the self- or super-constructor call of ${sym.owner.owner}. A VerifyError is likely." + devWarning(tree.pos, msg) + } else sym setFlag STATIC + } + sym.owner = sym.owner.enclClass if (sym.isClass) sym.owner = sym.owner.toInterface if (sym.isMethod) sym setFlag LIFTED diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index 15ca916ac1..b71d14a04f 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -199,14 +199,15 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD if (bitmaps.contains(lzyVal)) bitmaps(lzyVal).map(_.owner = defSym) val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) - DEF(defSym).mkTree(addBitmapDefs(lzyVal, BLOCK(rhs, retVal))) setSymbol defSym + + DefDef(defSym, addBitmapDefs(lzyVal, BLOCK(rhs, retVal))) } def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): (Tree, Tree) = { val slowPathDef: Tree = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal) - (If(cond, Apply(ID(slowPathDef.symbol), List()), retVal), slowPathDef) + (If(cond, Apply(Ident(slowPathDef.symbol), Nil), retVal), slowPathDef) } /** return a 'lazified' version of rhs. Rhs should conform to the diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 3ec4d16bf5..7b545be07e 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -472,7 +472,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** The typer */ private var localTyper: erasure.Typer = _ private def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree) - private def localTyped(pos: Position, tree: Tree, pt: Type) = localTyper.typed(atPos(pos)(tree), pt) /** Map lazy values to the fields they should null after initialization. */ private var lazyValNullables: Map[Symbol, Set[Symbol]] = _ @@ -695,10 +694,10 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { */ def completeSuperAccessor(stat: Tree) = stat match { case DefDef(_, _, _, vparams :: Nil, _, EmptyTree) if stat.symbol.isSuperAccessor => - val rhs0 = (Super(clazz, tpnme.EMPTY) DOT stat.symbol.alias)(vparams map (v => Ident(v.symbol)): _*) - val rhs1 = localTyped(stat.pos, rhs0, stat.symbol.tpe.resultType) + val body = atPos(stat.pos)(Apply(Select(Super(clazz, tpnme.EMPTY), stat.symbol.alias), vparams map (v => Ident(v.symbol)))) + val pt = stat.symbol.tpe.resultType - deriveDefDef(stat)(_ => enteringMixin(transform(rhs1))) + copyDefDef(stat)(rhs = enteringMixin(transform(localTyper.typed(body, pt)))) case _ => stat } @@ -724,8 +723,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case _ => } val init = bitmapKind match { - case BooleanClass => VAL(sym) === FALSE - case _ => VAL(sym) === ZERO + case BooleanClass => ValDef(sym, FALSE) + case _ => ValDef(sym, ZERO) } sym setFlag PrivateLocal @@ -775,7 +774,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { defSym setInfoAndEnter MethodType(params, lzyVal.tpe.resultType) val rhs: Tree = (gen.mkSynchronizedCheck(attrThis, cond, syncBody, stats)).changeOwner(currentOwner -> defSym) val strictSubst = new TreeSymSubstituterWithCopying(args.map(_.symbol), params) - addDef(position(defSym), DEF(defSym).mkTree(strictSubst(BLOCK(rhs, retVal))) setSymbol defSym) + addDef(position(defSym), DefDef(defSym, strictSubst(BLOCK(rhs, retVal)))) defSym } diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 4bc4e06fa7..5a440039d6 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1836,12 +1836,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } // ctor - mbrs += atPos(m.pos)(DefDef(m, Modifiers(m.flags), mmap(List(vparams))(ValDef), EmptyTree)) + mbrs += DefDef(m, Modifiers(m.flags), mmap(List(vparams))(ValDef), EmptyTree) } else { - mbrs += atPos(m.pos)(DefDef(m, { paramss => EmptyTree })) + mbrs += DefDef(m, { paramss => EmptyTree }) } } else if (m.isValue) { - mbrs += ValDef(m, EmptyTree).setType(NoType).setPos(m.pos) + mbrs += ValDef(m).setType(NoType) } else if (m.isClass) { // mbrs += // ClassDef(m, Template(m.info.parents map TypeTree, emptyValDef, List()) @@ -1853,9 +1853,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val isSpecializedInstance = sClass :: sClass.parentSymbols exists (_ hasFlag SPECIALIZED) val sym = sClass.newMethod(nme.SPECIALIZED_INSTANCE, sClass.pos) setInfoAndEnter MethodType(Nil, BooleanTpe) - mbrs += atPos(sym.pos) { - DefDef(sym, Literal(Constant(isSpecializedInstance)).setType(BooleanTpe)).setType(NoType) - } + mbrs += DefDef(sym, Literal(Constant(isSpecializedInstance)).setType(BooleanTpe)).setType(NoType) } mbrs.toList } diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 6f422fcc90..b471d16ddd 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -156,7 +156,7 @@ abstract class TailCalls extends Transform { private def mkLabel() = { val label = method.newLabel(newTermName("_" + method.name), method.pos) val thisParam = method.newSyntheticValueParam(currentClass.typeOfThis) - label setInfo MethodType(thisParam :: method.tpe.params, method.tpe.finalResultType) + label setInfo MethodType(thisParam :: method.tpe.params, method.tpe_*.finalResultType) if (isEligible) label substInfo (method.tpe.typeParams, tparams) diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 16c803e2e8..e68f55a09e 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -158,11 +158,12 @@ abstract class UnCurry extends InfoTransform */ private def nonLocalReturnTry(body: Tree, key: Symbol, meth: Symbol) = { localTyper typed { - val extpe = nonLocalReturnExceptionType(meth.tpe.finalResultType) + val restpe = meth.tpe_*.finalResultType + val extpe = nonLocalReturnExceptionType(restpe) val ex = meth.newValue(nme.ex, body.pos) setInfo extpe - val argType = meth.tpe.finalResultType withAnnotation (AnnotationInfo marker UncheckedClass.tpe) + val argType = restpe withAnnotation (AnnotationInfo marker UncheckedClass.tpe) val pat = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(argType)) - val rhs = ( + val rhs = ( IF ((ex DOT nme.key)() OBJ_EQ Ident(key)) THEN ((ex DOT nme.value)()) ELSE (Throw(Ident(ex))) @@ -739,7 +740,7 @@ abstract class UnCurry extends InfoTransform case p if rpsymbols(p.symbol) => toArrayType(p.symbol.tpe) case p => p.symbol.tpe } - val forwresult = dd.symbol.tpe.finalResultType + val forwresult = dd.symbol.tpe_*.finalResultType val forwformsyms = map2(forwformals, flatparams)((tp, oldparam) => currentClass.newValueParameter(oldparam.name, oldparam.symbol.pos).setInfo(tp) ) @@ -751,10 +752,11 @@ abstract class UnCurry extends InfoTransform // create the symbol val forwsym = currentClass.newMethod(dd.name.toTermName, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags) setInfo forwtype + def forwParams = forwsym.info.paramss.flatten // create the tree val forwtree = theTyper.typedPos(dd.pos) { - val locals = map2(forwsym ARGS, flatparams) { + val locals = map2(forwParams, flatparams) { case (_, fp) if !rpsymbols(fp.symbol) => null case (argsym, fp) => Block(Nil, @@ -764,15 +766,13 @@ abstract class UnCurry extends InfoTransform ) ) } - val seqargs = map2(locals, forwsym ARGS) { + val seqargs = map2(locals, forwParams) { case (null, argsym) => Ident(argsym) case (l, _) => l } val end = if (forwsym.isConstructor) List(UNIT) else Nil - DEF(forwsym) === BLOCK( - Apply(gen.mkAttributedRef(flatdd.symbol), seqargs) :: end : _* - ) + DefDef(forwsym, BLOCK(Apply(gen.mkAttributedRef(flatdd.symbol), seqargs) :: end : _*)) } // check if the method with that name and those arguments already exists in the template diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala index cf74f0fb11..c8dbbb02bb 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala @@ -170,7 +170,7 @@ trait MatchCodeGen extends Interface { } toList // at most 1 element // scrutSym == NoSymbol when generating an alternatives matcher - val scrutDef = scrutSym.fold(List[Tree]())(sym => (VAL(sym) === scrut) :: Nil) // for alternatives + val scrutDef = scrutSym.fold(List[Tree]())(ValDef(_, scrut) :: Nil) // for alternatives // the generated block is taken apart in TailCalls under the following assumptions // the assumption is once we encounter a case, the remainder of the block will consist of cases @@ -199,7 +199,7 @@ trait MatchCodeGen extends Interface { def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { val prevSym = freshSym(prev.pos, prev.tpe, "o") BLOCK( - VAL(prevSym) === prev, + ValDef(prevSym, prev), // must be isEmpty and get as we don't control the target of the call (prev is an extractor call) ifThenElseZero( NOT(prevSym DOT vpmName.isEmpty), @@ -214,14 +214,12 @@ trait MatchCodeGen extends Interface { // next == MatchMonad[U] // returns MatchMonad[U] def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = { - val rest = + val rest = ( // only emit a local val for `nextBinder` if it's actually referenced in `next` if (next.exists(_.symbol eq nextBinder)) - BLOCK( - VAL(nextBinder) === res, - next - ) + BLOCK(ValDef(nextBinder, res), next) else next + ) ifThenElseZero(cond, rest) } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index ec45789687..ba78438b66 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -146,7 +146,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { lazy val storedCond = freshSym(pos, BooleanTpe, "rc") setFlag MUTABLE lazy val treesToHoist: List[Tree] = { nextBinder setFlag MUTABLE - List(storedCond, nextBinder) map { b => VAL(b) === codegen.mkZero(b.info) } + List(storedCond, nextBinder) map (b => ValDef(b, codegen.mkZero(b.info))) } // TODO: finer-grained duplication @@ -528,7 +528,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { } def defaultSym: Symbol = scrutSym - def defaultBody: Tree = { import CODE._; matchFailGenOverride map (gen => gen(REF(scrutSym))) getOrElse MATCHERROR(REF(scrutSym)) } + def defaultBody: Tree = { import CODE._; matchFailGenOverride map (gen => gen(REF(scrutSym))) getOrElse Throw(MatchErrorClass.tpe, REF(scrutSym)) } def defaultCase(scrutSym: Symbol = defaultSym, guard: Tree = EmptyTree, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) { (DEFAULT IF guard) ==> body }} @@ -546,7 +546,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { if (scrutSym.tpe =:= IntTpe) REF(scrutSym) else (REF(scrutSym) DOT (nme.toInt)) Some(BLOCK( - VAL(scrutSym) === scrut, + ValDef(scrutSym, scrut), Match(scrutToInt, caseDefsWithDefault) // a switch )) } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index 942aa80c34..cf26ec3398 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -174,7 +174,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { else { // only store binders actually used val (subPatBindersStored, subPatRefsStored) = stored.filter{case (b, _) => usedBinders(b)}.unzip - Block(map2(subPatBindersStored.toList, subPatRefsStored.toList)(VAL(_) === _), in) + Block(map2(subPatBindersStored.toList, subPatRefsStored.toList)(ValDef(_, _)), in) } } } @@ -328,9 +328,9 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def outerTest(testedBinder: Symbol, expectedTp: Type): Tree = { val expectedOuter = expectedTp.prefix match { - case ThisType(clazz) => THIS(clazz) - case pre if pre != NoType => REF(pre.prefix, pre.termSymbol) - case _ => mkTRUE // fallback for SI-6183 + case ThisType(clazz) => This(clazz) + case NoType => mkTRUE // fallback for SI-6183 + case pre => REF(pre.prefix, pre.termSymbol) } // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` @@ -527,8 +527,9 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { // pt is the fully defined type of the cases (either pt or the lub of the types of the cases) def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Tree => Tree]): Tree = - fixerUpper(owner, scrut.pos){ - def matchFailGen = (matchFailGenOverride orElse Some(CODE.MATCHERROR(_: Tree))) + fixerUpper(owner, scrut.pos) { + def matchFailGen = matchFailGenOverride orElse Some(Throw(MatchErrorClass.tpe, _: Tree)) + debug.patmat("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}"))) val (suppression, requireSwitch): (Suppression, Boolean) = diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 263b5ad784..ec2b7d49f5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -21,32 +21,17 @@ trait MethodSynthesis { import definitions._ import CODE._ - object synthesisUtil { - type TT[T] = ru.TypeTag[T] - type CT[T] = ClassTag[T] - - def newValOrDefDef(sym: Symbol, body: Tree) = - if (sym.isLazy) ValDef(sym, body) - else DefDef(sym, body) - - /** The annotations amongst those found on the original symbol which - * should be propagated to this kind of accessor. - */ - def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = { - initial filter { ann => - // There are no meta-annotation arguments attached to `ann` - if (ann.metaAnnotations.isEmpty) { - // A meta-annotation matching `annotKind` exists on `ann`'s definition. - (ann.defaultTargets contains category) || - // `ann`'s definition has no meta-annotations, and `keepClean` is true. - (ann.defaultTargets.isEmpty && keepClean) - } - // There are meta-annotation arguments, and one of them matches `annotKind` - else ann.metaAnnotations exists (_ matches category) - } + /** The annotations amongst those found on the original symbol which + * should be propagated to this kind of accessor. + */ + def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = { + def annotationFilter(ann: AnnotationInfo) = ann.metaAnnotations match { + case Nil if ann.defaultTargets.isEmpty => keepClean // no meta-annotations or default targets + case Nil => ann.defaultTargets contains category // default targets exist for ann + case metas => metas exists (_ matches category) // meta-annotations attached to ann } + initial filter annotationFilter } - import synthesisUtil._ class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { def mkThis = This(clazz) setPos clazz.pos.focus @@ -67,7 +52,10 @@ trait MethodSynthesis { } private def finishMethod(method: Symbol, f: Symbol => Tree): Tree = - localTyper typed newValOrDefDef(method, f(method)) + localTyper typed ( + if (method.isLazy) ValDef(method, f(method)) + else DefDef(method, f(method)) + ) private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = { val name1 = name.toTermName @@ -105,7 +93,7 @@ trait MethodSynthesis { def createSwitchMethod(name: Name, range: Seq[Int], returnType: Type)(f: Int => Tree) = { createMethod(name, List(IntTpe), returnType) { m => val arg0 = Ident(m.firstParam) - val default = DEFAULT ==> THROW(IndexOutOfBoundsExceptionClass, arg0) + val default = DEFAULT ==> Throw(IndexOutOfBoundsExceptionClass.tpe_*, fn(arg0, nme.toString_)) val cases = range.map(num => CASE(LIT(num)) ==> f(num)).toList :+ default Match(arg0, cases) @@ -393,18 +381,9 @@ trait MethodSynthesis { } } case class Getter(tree: ValDef) extends BaseGetter(tree) { - override def derivedSym = ( - if (mods.isDeferred) basisSym - else basisSym.getter(enclClass) - ) - // Range position errors ensue if we don't duplicate this in some - // circumstances (at least: concrete vals with existential types.) - private def tptOriginal = ( - if (mods.isDeferred) tree.tpt // keep type tree of original abstract field - else tree.tpt.duplicate setPos tree.tpt.pos.focus // focused position of original tpt - ) - - override def derivedTree: DefDef = { + override def derivedSym = if (mods.isDeferred) basisSym else basisSym.getter(enclClass) + private def derivedRhs = if (mods.isDeferred) EmptyTree else fieldSelection + private def derivedTpt = { // For existentials, don't specify a type for the getter, even one derived // from the symbol! This leads to incompatible existentials for the field and // the getter. Let the typer do all the work. You might think "why only for @@ -413,24 +392,16 @@ trait MethodSynthesis { // starts compiling (instead of failing like it's supposed to) because the typer // expects to be able to identify escaping locals in typedDefDef, and fails to // spot that brand of them. In other words it's an artifact of the implementation. - val tpt = atPos(derivedSym.pos.focus)(derivedSym.tpe.finalResultType match { - case ExistentialType(_, _) => TypeTree() - case _ if mods.isDeferred => TypeTree() + val tpt = derivedSym.tpe_*.finalResultType.widen match { + // Range position errors ensue if we don't duplicate this in some + // circumstances (at least: concrete vals with existential types.) + case ExistentialType(_, _) => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus) + case _ if mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field case tp => TypeTree(tp) - }) - // TODO - reconcile this with the DefDef creator in Trees (which - // at this writing presented no way to pass a tree in for tpt.) - atPos(derivedSym.pos) { - DefDef( - Modifiers(derivedSym.flags), - derivedSym.name.toTermName, - Nil, - Nil, - tpt setOriginal tptOriginal, - if (mods.isDeferred) EmptyTree else fieldSelection - ) setSymbol derivedSym } + tpt setPos tree.tpt.pos.focus } + override def derivedTree: DefDef = newDefDef(derivedSym, derivedRhs)(tpt = derivedTpt) } /** Implements lazy value accessors: * - for lazy values of type Unit and all lazy fields inside traits, @@ -461,8 +432,8 @@ trait MethodSynthesis { if (tree.symbol.owner.isTrait || hasUnitType(basisSym)) rhs1 else gen.mkAssignAndReturn(basisSym, rhs1) ) - derivedSym.setPos(tree.pos) // cannot set it at createAndEnterSymbol because basisSym can possible stil have NoPosition - val ddefRes = atPos(tree.pos)(DefDef(derivedSym, new ChangeOwnerAndModuleClassTraverser(basisSym, derivedSym)(body))) + derivedSym setPos tree.pos // cannot set it at createAndEnterSymbol because basisSym can possible stil have NoPosition + val ddefRes = DefDef(derivedSym, new ChangeOwnerAndModuleClassTraverser(basisSym, derivedSym)(body)) // ValDef will have its position focused whereas DefDef will have original correct rangepos // ideally positions would be correct at the creation time but lazy vals are really a special case // here so for the sake of keeping api clean we fix positions manually in LazyValGetter diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 454f913412..9ac0b0835a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -135,7 +135,8 @@ trait Namers extends MethodSynthesis { setPrivateWithin(tree, sym, tree.mods) def inConstructorFlag: Long = { - val termOwnedContexts: List[Context] = context.enclosingContextChain.takeWhile(_.owner.isTerm) + val termOwnedContexts: List[Context] = + context.enclosingContextChain.takeWhile(c => c.owner.isTerm && !c.owner.isAnonymousFunction) val constructorNonSuffix = termOwnedContexts exists (c => c.owner.isConstructor && !c.inConstructorSuffix) val earlyInit = termOwnedContexts exists (_.owner.isEarlyInitialized) if (constructorNonSuffix || earlyInit) INCONSTRUCTOR else 0L diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 12d6bb2e6a..b706e1af6b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -296,8 +296,8 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } transformSelect - case DefDef(mods, name, tparams, vparamss, tpt, rhs) if tree.symbol.isMethodWithExtension => - treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, withInvalidOwner(transform(rhs))) + case DefDef(_, _, _, _, _, _) if tree.symbol.isMethodWithExtension => + deriveDefDef(tree)(rhs => withInvalidOwner(transform(rhs))) case TypeApply(sel @ Select(qual, name), args) => mayNeedProtectedAccessor(sel, args, goToSuper = true) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 629513ada3..3ac0b398ee 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -363,7 +363,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper check(owner, scope, pt, tree setType tp1.typeSymbol.classBound) else if (owner == NoSymbol) tree setType packSymbols(hiddenSymbols.reverse, tp1) - else if (!phase.erasedTypes) { // privates + else if (!isPastTyper) { // privates val badSymbol = hiddenSymbols.head SymbolEscapesScopeError(tree, badSymbol) } else tree @@ -2103,7 +2103,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case PolyType(_, restpe) => paramssTypes(restpe) case _ => Nil } - def resultType = meth.tpe.finalResultType + def resultType = meth.tpe_*.finalResultType def nthParamPos(n1: Int, n2: Int) = try ddef.vparamss(n1)(n2).pos catch { case _: IndexOutOfBoundsException => meth.pos } @@ -2598,8 +2598,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper default -> gen.scalaFunctionConstr(List(A1Tpt), B1Tpt) ) } - val rhs = methodBodyTyper.virtualizedMatch(match_, mode, B1.tpe) - val defdef = DefDef(methodSym, Modifiers(methodSym.flags), originals, rhs) + def newParam(param: Symbol): ValDef = { + val vd = ValDef(param, EmptyTree) + val tt @ TypeTree() = vd.tpt + tt setOriginal (originals(param) setPos param.pos.focus) + vd + } + + val rhs = methodBodyTyper.virtualizedMatch(match_, mode, B1.tpe) + val defdef = newDefDef(methodSym, rhs)(vparamss = mapParamss(methodSym)(newParam)) (defdef, matchResTp) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 5049fec65b..af19e3cf80 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -46,11 +46,8 @@ trait Unapplies extends ast.TreeDSL { def copyUntyped[T <: Tree](tree: T): T = returning[T](tree.duplicate)(UnTyper traverse _) - def copyUntypedInvariant(td: TypeDef): TypeDef = { - val copy = treeCopy.TypeDef(td, td.mods &~ (COVARIANT | CONTRAVARIANT), td.name, td.tparams, td.rhs) - - returning[TypeDef](copy.duplicate)(UnTyper traverse _) - } + def copyUntypedInvariant(td: TypeDef): TypeDef = + copyTypeDef(copyUntyped(td))(mods = td.mods &~ (COVARIANT | CONTRAVARIANT)) private def toIdent(x: DefTree) = Ident(x.name) setPos x.pos.focus |