diff options
125 files changed, 1766 insertions, 1139 deletions
@@ -1281,6 +1281,14 @@ TODO: <target name="quick.swing" depends="quick.actors, quick.lib" if="has.java6"> <staged-build with="locker" stage="quick" project="swing"/> </target> + <target name="quick.partest-extras" + depends="quick.comp"> + <!-- compile compiler-specific parts of partest --> + <staged-build with="starr" stage="quick" project="partest-extras" /> + <staged-build with="starr" stage="quick" project="partest-javaagent" /> + </target> + + <target name="quick.plugins" depends="quick.comp"> <staged-uptodate stage="quick" project="plugins"> <check><srcfiles dir="${src.dir}/continuations"/></check> @@ -1308,7 +1316,7 @@ TODO: </staged-uptodate> </target> - <target name="quick.bin" depends="quick.lib, quick.reflect, quick.comp, quick.repl, quick.scalap, quick.interactive, quick.swing, quick.plugins, quick.scaladoc"> + <target name="quick.bin" depends="quick.lib, quick.reflect, quick.comp, quick.repl, quick.scalap, quick.interactive, quick.swing, quick.plugins, quick.scaladoc, quick.partest-extras"> <staged-bin stage="quick" classpathref="quick.bin.tool.path"/> </target> @@ -1325,6 +1333,11 @@ TODO: <target name="pack.actors" depends="quick.lib"> <staged-pack project="actors"/> </target> <target name="pack.swing" if="has.java6" depends="quick.swing"> <staged-pack project="swing"/> </target> <target name="pack.reflect" depends="quick.reflect"> <staged-pack project="reflect"/> </target> + <target name="pack.partest-extras" depends="quick.partest-extras"> + <staged-pack project="partest-extras"/> + <staged-pack project="partest-javaagent" + manifest="${src.dir}/partest-javaagent/scala/tools/partest/javaagent/MANIFEST.MF"/> + </target> <target name="pack.comp" depends="quick.comp, quick.scaladoc, quick.interactive, quick.repl, asm.done"> <staged-pack project="compiler" manifest="${build-pack.dir}/META-INF/MANIFEST.MF"> @@ -1345,11 +1358,10 @@ TODO: <attribute name="Class-Path" value="scala-reflect.jar scala-library.jar"/> </manifest> </pre> - <!-- script api is 2.11-only so far + <!-- JSR-223 support introduced in 2.11 --> <jar-opts> <service type="javax.script.ScriptEngineFactory" provider="scala.tools.nsc.interpreter.IMain$Factory"/> </jar-opts> - --> </staged-pack> </target> @@ -1357,7 +1369,7 @@ TODO: <target name="pack.scalap" depends="quick.scalap"> <staged-pack project="scalap" targetjar="scalap.jar"/> </target> - <target name="pack.bin" depends="pack.comp, pack.lib, pack.actors, pack.plugins, pack.reflect, pack.scalap, pack.swing"> + <target name="pack.bin" depends="pack.comp, pack.lib, pack.actors, pack.plugins, pack.reflect, pack.scalap, pack.swing, pack.partest-extras"> <copy todir="${build-pack.dir}/lib"> <path refid="external-modules-nocore" /> <mapper type="flatten" /> @@ -1606,18 +1618,12 @@ TODO: </target> <!-- See test/build-partest.xml for the macro(s) being used here. --> - <target name="partest.task" depends="init"> + <target name="partest.task" depends="init,pack.done"> <!-- note the classpathref! this is the classpath used to run partest, so it must have the new compiler.... --> <taskdef classpathref="partest.compilation.path" resource="scala/tools/partest/antlib.xml"/> - - <!-- compile compiler-specific parts of partest --> - <staged-build with="starr" stage="quick" project="partest-extras" /> - <staged-build with="starr" stage="quick" project="partest-javaagent" /> - <staged-pack project="partest-extras"/> - <staged-pack project="partest-javaagent" manifest="${src.dir}/partest-javaagent/scala/tools/partest/javaagent/MANIFEST.MF"/> </target> <target name="test.suite.init" depends="pack.done, partest.task"> 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 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/eclipse/scaladoc/.classpath b/src/eclipse/scaladoc/.classpath index 8e03c97657..4b9327796b 100644 --- a/src/eclipse/scaladoc/.classpath +++ b/src/eclipse/scaladoc/.classpath @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="scaladoc"/> - <classpathentry combineaccessrules="false" kind="src" path="/partest"/> + <classpathentry combineaccessrules="false" kind="src" path="/scala-partest"/> <classpathentry kind="var" path="SCALA_BASEDIR/lib/ant/ant.jar"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/> diff --git a/src/intellij/setup.sh b/src/intellij/setup.sh index d0e1abeb96..bd324ba5bd 100755 --- a/src/intellij/setup.sh +++ b/src/intellij/setup.sh @@ -9,7 +9,7 @@ export BASE="$( cd "$( dirname "$0" )"/../.. && pwd )" echo "About to delete .ipr and .iml files and replace with the .SAMPLE files. Press enter to continue or CTRL-C to cancel." read -(rm *.ipr *.iml 2>/dev/null) +(rm -f *.ipr *.iml 2>/dev/null) for f in $(ls "$SCRIPT_DIR"/*.SAMPLE); do NEW_FILE=`echo $f | perl -pe 's/.SAMPLE//'`; diff --git a/src/library/scala/collection/BitSetLike.scala b/src/library/scala/collection/BitSetLike.scala index f11f3757a6..6592e49429 100644 --- a/src/library/scala/collection/BitSetLike.scala +++ b/src/library/scala/collection/BitSetLike.scala @@ -116,14 +116,20 @@ trait BitSetLike[+This <: BitSetLike[This] with SortedSet[Int]] extends SortedSe } override def foreach[B](f: Int => B) { - for (i <- 0 until nwords) { - val w = word(i) - /* NOTE: `until` instead of `to` will not work here because - the maximum value of `(i + 1) * WordLength` could be - `Int.MaxValue + 1` (i.e. `Int.MinValue`). */ - for (j <- i * WordLength to (i + 1) * WordLength - 1) { - if ((w & (1L << j)) != 0L) f(j) + /* NOTE: while loops are significantly faster as of 2.11 and + one major use case of bitsets is performance. Also, there + is nothing to do when all bits are clear, so use that as + the inner loop condition. */ + var i = 0 + while (i < nwords) { + var w = word(i) + var j = i * WordLength + while (w != 0L) { + if ((w&1L) == 1L) f(j) + w = w >>> 1 + j += 1 } + i += 1 } } @@ -218,9 +224,10 @@ trait BitSetLike[+This <: BitSetLike[This] with SortedSet[Int]] extends SortedSe /** Companion object for BitSets. Contains private data only */ object BitSetLike { - private[collection] val LogWL = 6 - private val WordLength = 64 - private[collection] val MaxSize = (Int.MaxValue >> LogWL) + 1 + /* Final vals can sometimes be inlined as constants (faster) */ + private[collection] final val LogWL = 6 + private final val WordLength = 64 + private[collection] final val MaxSize = (Int.MaxValue >> LogWL) + 1 private[collection] def updateArray(elems: Array[Long], idx: Int, w: Long): Array[Long] = { var len = elems.length diff --git a/src/library/scala/collection/mutable/MapLike.scala b/src/library/scala/collection/mutable/MapLike.scala index dbe32fdb79..6230fc23aa 100644 --- a/src/library/scala/collection/mutable/MapLike.scala +++ b/src/library/scala/collection/mutable/MapLike.scala @@ -208,8 +208,8 @@ trait MapLike[A, B, +This <: MapLike[A, B, This] with Map[A, B]] * @param p The test predicate */ def retain(p: (A, B) => Boolean): this.type = { - for ((k, v) <- this.seq ; if !p(k, v)) - this -= k + for ((k, v) <- this.toList) // SI-7269 toList avoids ConcurrentModificationException + if (!p(k, v)) this -= k this } diff --git a/src/library/scala/collection/mutable/SetLike.scala b/src/library/scala/collection/mutable/SetLike.scala index 073bc59f61..c7618fcf3c 100644 --- a/src/library/scala/collection/mutable/SetLike.scala +++ b/src/library/scala/collection/mutable/SetLike.scala @@ -121,7 +121,9 @@ trait SetLike[A, +This <: SetLike[A, This] with Set[A]] * which `p` returns `true` are retained in the set; all others * are removed. */ - def retain(p: A => Boolean): Unit = for (elem <- this.toList) if (!p(elem)) this -= elem + def retain(p: A => Boolean): Unit = + for (elem <- this.toList) // SI-7269 toList avoids ConcurrentModificationException + if (!p(elem)) this -= elem /** Removes all elements from the set. After this operation is completed, * the set will be empty. diff --git a/src/library/scala/io/BufferedSource.scala b/src/library/scala/io/BufferedSource.scala index c170d28127..832c7b23f9 100644 --- a/src/library/scala/io/BufferedSource.scala +++ b/src/library/scala/io/BufferedSource.scala @@ -8,9 +8,11 @@ package scala.io +import java.util.Arrays import java.io.{ InputStream, BufferedReader, InputStreamReader, PushbackReader } import Source.DefaultBufSize import scala.collection.{ Iterator, AbstractIterator } +import scala.collection.mutable.ArrayBuffer /** This object provides convenience methods to create an iterable * representation of a source file. @@ -39,8 +41,8 @@ class BufferedSource(inputStream: InputStream, bufferSize: Int)(implicit val cod takeWhile (_ != -1) map (_.toChar) ) - - class BufferedLineIterator extends AbstractIterator[String] with Iterator[String] { + + private def decachedReader: BufferedReader = { // Don't want to lose a buffered char sitting in iter either. Yes, // this is ridiculous, but if I can't get rid of Source, and all the // Iterator bits are designed into Source, and people create Sources @@ -48,18 +50,21 @@ class BufferedSource(inputStream: InputStream, bufferSize: Int)(implicit val cod // that calls hasNext to find out if they're empty, and that leads // to chars being buffered, and no, I don't work here, they left a // door unlocked. - private val lineReader: BufferedReader = { - // To avoid inflicting this silliness indiscriminately, we can - // skip it if the char reader was never created: and almost always - // it will not have been created, since getLines will be called - // immediately on the source. - if (charReaderCreated && iter.hasNext) { - val pb = new PushbackReader(charReader) - pb unread iter.next().toInt - new BufferedReader(pb, bufferSize) - } - else charReader + // To avoid inflicting this silliness indiscriminately, we can + // skip it if the char reader was never created: and almost always + // it will not have been created, since getLines will be called + // immediately on the source. + if (charReaderCreated && iter.hasNext) { + val pb = new PushbackReader(charReader) + pb unread iter.next().toInt + new BufferedReader(pb, bufferSize) } + else charReader + } + + + class BufferedLineIterator extends AbstractIterator[String] with Iterator[String] { + private val lineReader = decachedReader var nextLine: String = null override def hasNext = { @@ -79,5 +84,18 @@ class BufferedSource(inputStream: InputStream, bufferSize: Int)(implicit val cod } override def getLines(): Iterator[String] = new BufferedLineIterator + + /** Efficiently converts the entire remaining input into a string. */ + override def mkString = { + // Speed up slurping of whole data set in the simplest cases. + val allReader = decachedReader + val sb = new StringBuilder + val buf = new Array[Char](bufferSize) + var n = 0 + while (n != -1) { + n = charReader.read(buf) + if (n>0) sb.appendAll(buf, 0, n) + } + sb.result + } } - diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 315f56bd4e..5fb24f2a36 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -32,21 +32,13 @@ object ScalaRunTime { clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) def isValueClass(clazz: jClass[_]) = clazz.isPrimitive() - def isTuple(x: Any) = x != null && tupleNames(x.getClass.getName) + + // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) + def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") def isAnyVal(x: Any) = x match { case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true case _ => false } - // Avoiding boxing which messes up the specialized tests. Don't ask. - private val tupleNames = { - var i = 22 - var names: List[String] = Nil - while (i >= 1) { - names ::= ("scala.Tuple" + String.valueOf(i)) - i -= 1 - } - names.toSet - } // A helper method to make my life in the pattern matcher a lot easier. def drop[Repr](coll: Repr, num: Int)(implicit traversable: IsTraversableLike[Repr]): Repr = @@ -77,33 +69,37 @@ object ScalaRunTime { classTag[T].runtimeClass.asInstanceOf[jClass[T]] /** Retrieve generic array element */ - def array_apply(xs: AnyRef, idx: Int): Any = xs match { - case x: Array[AnyRef] => x(idx).asInstanceOf[Any] - case x: Array[Int] => x(idx).asInstanceOf[Any] - case x: Array[Double] => x(idx).asInstanceOf[Any] - case x: Array[Long] => x(idx).asInstanceOf[Any] - case x: Array[Float] => x(idx).asInstanceOf[Any] - case x: Array[Char] => x(idx).asInstanceOf[Any] - case x: Array[Byte] => x(idx).asInstanceOf[Any] - case x: Array[Short] => x(idx).asInstanceOf[Any] - case x: Array[Boolean] => x(idx).asInstanceOf[Any] - case x: Array[Unit] => x(idx).asInstanceOf[Any] - case null => throw new NullPointerException + def array_apply(xs: AnyRef, idx: Int): Any = { + xs match { + case x: Array[AnyRef] => x(idx).asInstanceOf[Any] + case x: Array[Int] => x(idx).asInstanceOf[Any] + case x: Array[Double] => x(idx).asInstanceOf[Any] + case x: Array[Long] => x(idx).asInstanceOf[Any] + case x: Array[Float] => x(idx).asInstanceOf[Any] + case x: Array[Char] => x(idx).asInstanceOf[Any] + case x: Array[Byte] => x(idx).asInstanceOf[Any] + case x: Array[Short] => x(idx).asInstanceOf[Any] + case x: Array[Boolean] => x(idx).asInstanceOf[Any] + case x: Array[Unit] => x(idx).asInstanceOf[Any] + case null => throw new NullPointerException + } } /** update generic array element */ - def array_update(xs: AnyRef, idx: Int, value: Any): Unit = xs match { - case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] - case x: Array[Int] => x(idx) = value.asInstanceOf[Int] - case x: Array[Double] => x(idx) = value.asInstanceOf[Double] - case x: Array[Long] => x(idx) = value.asInstanceOf[Long] - case x: Array[Float] => x(idx) = value.asInstanceOf[Float] - case x: Array[Char] => x(idx) = value.asInstanceOf[Char] - case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] - case x: Array[Short] => x(idx) = value.asInstanceOf[Short] - case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] - case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] - case null => throw new NullPointerException + def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { + xs match { + case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] + case x: Array[Int] => x(idx) = value.asInstanceOf[Int] + case x: Array[Double] => x(idx) = value.asInstanceOf[Double] + case x: Array[Long] => x(idx) = value.asInstanceOf[Long] + case x: Array[Float] => x(idx) = value.asInstanceOf[Float] + case x: Array[Char] => x(idx) = value.asInstanceOf[Char] + case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] + case x: Array[Short] => x(idx) = value.asInstanceOf[Short] + case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] + case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] + case null => throw new NullPointerException + } } /** Get generic array length */ 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/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index bc4b8433f5..ed1c3f1044 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -693,6 +693,19 @@ trait Definitions extends api.StandardDefinitions { case NullaryMethodType(restpe) => restpe case _ => tp } + + /** An implementation of finalResultType which does only what + * finalResultType is documented to do. Defining it externally to + * Type helps ensure people can't come to depend on accidental + * aspects of its behavior. This is all of it! + */ + def finalResultType(tp: Type): Type = tp match { + case PolyType(_, restpe) => finalResultType(restpe) + case MethodType(_, restpe) => finalResultType(restpe) + case NullaryMethodType(restpe) => finalResultType(restpe) + case _ => tp + } + def abstractFunctionForFunctionType(tp: Type) = { assert(isFunctionType(tp), tp) abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) 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/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d3a0ffb744..d58ea09386 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1435,6 +1435,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert(isCompilerUniverse) if (infos == null || runId(infos.validFrom) == currentRunId) { infos + } else if (isPackageClass) { + // SI-7801 early phase package scopes are mutated in new runs (Namers#enterPackage), so we have to + // discard transformed infos, rather than just marking them as from this run. + val oldest = infos.oldest + oldest.validFrom = validTo + this.infos = oldest + oldest } else { val prev1 = adaptInfos(infos.prev) if (prev1 ne infos.prev) prev1 @@ -1444,10 +1451,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => _validTo = period(currentRunId, pid) phase = phaseWithId(pid) - val info1 = ( - if (isPackageClass) infos.info - else adaptToNewRunMap(infos.info) - ) + val info1 = adaptToNewRunMap(infos.info) if (info1 eq infos.info) { infos.validFrom = validTo infos @@ -3473,6 +3477,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => "TypeHistory(" + phaseOf(validFrom)+":"+runId(validFrom) + "," + info + "," + prev + ")" def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) + + def oldest: TypeHistory = if (prev == null) this else prev.oldest } // ----- Hoisted closures and convenience methods, for compile time reductions ------- diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 84818a6f42..38f1f0f725 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -589,6 +589,10 @@ trait Trees extends api.Trees { self: SymbolTable => object TypeTree extends TypeTreeExtractor def TypeTree(tp: Type): TypeTree = TypeTree() setType tp + def TypeTree(sym: Symbol): TypeTree = atPos(sym.pos.focus)(TypeTree(sym.tpe_*.finalResultType)) + + def TypeBoundsTree(bounds: TypeBounds): TypeBoundsTree = TypeBoundsTree(TypeTree(bounds.lo), TypeTree(bounds.hi)) + def TypeBoundsTree(sym: Symbol): TypeBoundsTree = atPos(sym.pos)(TypeBoundsTree(sym.info.bounds)) override type TreeCopier <: InternalTreeCopierOps abstract class InternalTreeCopierOps extends TreeCopierOps { @@ -1013,15 +1017,6 @@ trait Trees extends api.Trees { self: SymbolTable => ModuleDef(Modifiers(sym.flags), sym.name.toTermName, impl) setSymbol sym } - def ValDef(sym: Symbol, rhs: Tree): ValDef = - atPos(sym.pos) { - ValDef(Modifiers(sym.flags), sym.name.toTermName, - TypeTree(sym.tpe) setPos sym.pos.focus, - rhs) setSymbol sym - } - - def ValDef(sym: Symbol): ValDef = ValDef(sym, EmptyTree) - trait CannotHaveAttrs extends Tree { override def canHaveAttrs = false @@ -1041,50 +1036,44 @@ trait Trees extends api.Trees { self: SymbolTable => object emptyValDef extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) with CannotHaveAttrs object pendingSuperCall extends Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List()) with CannotHaveAttrs - def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = - atPos(sym.pos) { - assert(sym != NoSymbol) - DefDef(mods, - sym.name.toTermName, - sym.typeParams map TypeDef, - vparamss, - TypeTree(sym.tpe.finalResultType) setPos sym.pos.focus, - rhs) setSymbol sym - } - - def DefDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef = - DefDef(sym, Modifiers(sym.flags), vparamss, rhs) - - def DefDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef = - DefDef(sym, mods, mapParamss(sym)(ValDef), rhs) - - /** A DefDef with original trees attached to the TypeTree of each parameter */ - def DefDef(sym: Symbol, mods: Modifiers, originalParamTpts: Symbol => Tree, rhs: Tree): DefDef = { - val paramms = mapParamss(sym){ sym => - val vd = ValDef(sym, EmptyTree) - (vd.tpt : @unchecked) match { - case tt: TypeTree => tt setOriginal (originalParamTpts(sym) setPos sym.pos.focus) - } - vd - } - DefDef(sym, mods, paramms, rhs) - } - - def DefDef(sym: Symbol, rhs: Tree): DefDef = - DefDef(sym, Modifiers(sym.flags), rhs) - - def DefDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef = - DefDef(sym, rhs(sym.info.paramss)) - - /** A TypeDef node which defines given `sym` with given tight hand side `rhs`. */ - def TypeDef(sym: Symbol, rhs: Tree): TypeDef = - atPos(sym.pos) { - TypeDef(Modifiers(sym.flags), sym.name.toTypeName, sym.typeParams map TypeDef, rhs) setSymbol sym - } + def newValDef(sym: Symbol, rhs: Tree)( + mods: Modifiers = Modifiers(sym.flags), + name: TermName = sym.name.toTermName, + tpt: Tree = TypeTree(sym) + ): ValDef = ( + atPos(sym.pos)(ValDef(mods, name, tpt, rhs)) setSymbol sym + ) + + def newDefDef(sym: Symbol, rhs: Tree)( + mods: Modifiers = Modifiers(sym.flags), + name: TermName = sym.name.toTermName, + tparams: List[TypeDef] = sym.typeParams map TypeDef, + vparamss: List[List[ValDef]] = mapParamss(sym)(ValDef), + tpt: Tree = TypeTree(sym) + ): DefDef = ( + atPos(sym.pos)(DefDef(mods, name, tparams, vparamss, tpt, rhs)) setSymbol sym + ) + + def newTypeDef(sym: Symbol, rhs: Tree)( + mods: Modifiers = Modifiers(sym.flags), + name: TypeName = sym.name.toTypeName, + tparams: List[TypeDef] = sym.typeParams map TypeDef + ): TypeDef = ( + atPos(sym.pos)(TypeDef(mods, name, tparams, rhs)) setSymbol sym + ) + + def DefDef(sym: Symbol, rhs: Tree): DefDef = newDefDef(sym, rhs)() + def DefDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef = newDefDef(sym, rhs)(vparamss = vparamss) + def DefDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef = newDefDef(sym, rhs)(mods = mods) + def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = newDefDef(sym, rhs)(mods = mods, vparamss = vparamss) + def DefDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef = newDefDef(sym, rhs(sym.info.paramss))() + + def ValDef(sym: Symbol): ValDef = newValDef(sym, EmptyTree)() + def ValDef(sym: Symbol, rhs: Tree): ValDef = newValDef(sym, rhs)() /** A TypeDef node which defines abstract type or type parameter for given `sym` */ - def TypeDef(sym: Symbol): TypeDef = - TypeDef(sym, TypeBoundsTree(TypeTree(sym.info.bounds.lo), TypeTree(sym.info.bounds.hi))) + def TypeDef(sym: Symbol): TypeDef = newTypeDef(sym, TypeBoundsTree(sym))() + def TypeDef(sym: Symbol, rhs: Tree): TypeDef = newTypeDef(sym, rhs)() def LabelDef(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef = atPos(sym.pos) { @@ -1617,6 +1606,25 @@ trait Trees extends api.Trees { self: SymbolTable => } } + /** Tracks the classes currently under construction during a transform */ + trait UnderConstructionTransformer extends Transformer { + import collection.mutable + + protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls contains clazz + + /** The stack of class symbols in which a call to this() or to the super + * constructor, or early definition is active */ + private val selfOrSuperCalls = mutable.Stack[Symbol]() + + abstract override def transform(tree: Tree) = { + if ((treeInfo isSelfOrSuperConstrCall tree) || (treeInfo isEarlyDef tree)) { + selfOrSuperCalls push currentOwner.owner + try super.transform(tree) + finally selfOrSuperCalls.pop() + } else super.transform(tree) + } + } + def duplicateAndKeepPositions(tree: Tree) = new Duplicator(focusPositions = false) transform tree // ------ copiers ------------------------------------------- diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index ca01a4b8e3..3adbbf13fc 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -144,7 +144,6 @@ trait Types override def isErroneous = underlying.isErroneous override def isStable: Boolean = underlying.isStable override def isVolatile = underlying.isVolatile - override def finalResultType = underlying.finalResultType override def paramSectionCount = underlying.paramSectionCount override def paramss = underlying.paramss override def params = underlying.params @@ -189,7 +188,6 @@ trait Types override def deconst = maybeRewrap(underlying.deconst) override def resultType = maybeRewrap(underlying.resultType) override def resultType(actuals: List[Type]) = maybeRewrap(underlying.resultType(actuals)) - override def finalResultType = maybeRewrap(underlying.finalResultType) override def paramSectionCount = 0 override def paramss: List[List[Symbol]] = List() override def params: List[Symbol] = List() @@ -440,7 +438,7 @@ trait Types /** For a curried/nullary method or poly type its non-method result type, * the type itself for all other types */ - def finalResultType: Type = this + final def finalResultType: Type = definitions finalResultType this /** For a method type, the number of its value parameter sections, * 0 for all other types */ @@ -1240,7 +1238,6 @@ trait Types if (pre.isOmittablePrefix) pre.fullName + ".type" else prefixString + "type" } - /* override def typeOfThis: Type = typeSymbol.typeOfThis override def bounds: TypeBounds = TypeBounds(this, this) @@ -2564,8 +2561,6 @@ trait Types //TODO this may be generalised so that the only constraint is dependencies are acyclic def approximate: MethodType = MethodType(params, resultApprox) - override def finalResultType: Type = resultType.finalResultType - override def safeToString = paramString(this) + resultType override def cloneInfo(owner: Symbol) = { @@ -2592,7 +2587,6 @@ trait Types override def isTrivial = resultType.isTrivial && (resultType eq resultType.withoutAnnotations) override def prefix: Type = resultType.prefix override def narrow: Type = resultType.narrow - override def finalResultType: Type = resultType.finalResultType override def termSymbol: Symbol = resultType.termSymbol override def typeSymbol: Symbol = resultType.typeSymbol override def parents: List[Type] = resultType.parents @@ -2642,7 +2636,6 @@ trait Types override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) override def narrow: Type = resultType.narrow override def isVolatile = resultType.isVolatile - override def finalResultType: Type = resultType.finalResultType /** @M: typeDefSig wraps a TypeBounds in a PolyType * to represent a higher-kinded type parameter @@ -4013,15 +4006,11 @@ trait Types * type selections with the same name of equal (as determined by `=:=`) prefixes are * considered equal in regard to `=:=`. */ - def beginsWithTypeVarOrIsRefined(tp: Type): Boolean = tp match { - case SingleType(pre, sym) => - !(sym hasFlag PACKAGE) && beginsWithTypeVarOrIsRefined(pre) - case tv@TypeVar(_, constr) => - !tv.instValid || beginsWithTypeVarOrIsRefined(constr.inst) - case RefinedType(_, _) => - true - case _ => - false + def isEligibleForPrefixUnification(tp: Type): Boolean = tp match { + case SingleType(pre, sym) => !(sym hasFlag PACKAGE) && isEligibleForPrefixUnification(pre) + case tv@TypeVar(_, constr) => !tv.instValid || isEligibleForPrefixUnification(constr.inst) + case RefinedType(_, _) => true + case _ => false } def isErrorOrWildcard(tp: Type) = (tp eq ErrorType) || (tp eq WildcardType) diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index d8b3b04d0e..152c689c56 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -35,8 +35,10 @@ trait TypeComparers { private var subsametypeRecursions: Int = 0 - private def isUnifiable(pre1: Type, pre2: Type) = - (beginsWithTypeVarOrIsRefined(pre1) || beginsWithTypeVarOrIsRefined(pre2)) && (pre1 =:= pre2) + private def isUnifiable(pre1: Type, pre2: Type) = ( + (isEligibleForPrefixUnification(pre1) || isEligibleForPrefixUnification(pre2)) + && (pre1 =:= pre2) + ) /** Returns true iff we are past phase specialize, * sym1 and sym2 are two existential skolems with equal names and bounds, @@ -64,7 +66,6 @@ trait TypeComparers { (sym1.name == sym2.name) && isUnifiable(pre1, pre2) ) - def isDifferentType(tp1: Type, tp2: Type): Boolean = try { subsametypeRecursions += 1 undoLog undo { // undo type constraints that arise from operations in this block @@ -207,6 +208,7 @@ trait TypeComparers { } case _ => false } + /* Those false cases certainly are ugly. There's a proposed SIP to deuglify it. * https://docs.google.com/a/improving.org/document/d/1onPrzSqyDpHScc9PS_hpxJwa3FlPtthxw-bAuuEe8uA */ @@ -334,6 +336,14 @@ trait TypeComparers { (tparams1 corresponds tparams2)(cmp) && (sub1(res1) <:< sub2(res2)) } } + // This is looking for situations such as B.this.x.type <:< B.super.x.type. + // If it's a ThisType on the lhs and a SuperType on the right, and they originate + // in the same class, and the 'x' in the ThisType has in its override chain + // the 'x' in the SuperType, then the types conform. + private def isThisAndSuperSubtype(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { + case (SingleType(ThisType(lpre), v1), SingleType(SuperType(ThisType(rpre), _), v2)) => (lpre == rpre) && (v1.overrideChain contains v2) + case _ => false + } // @assume tp1.isHigherKinded || tp2.isHigherKinded def isHKSubType(tp1: Type, tp2: Type, depth: Depth): Boolean = { @@ -359,7 +369,7 @@ trait TypeComparers { def retry(lhs: Type, rhs: Type) = ((lhs ne tp1) || (rhs ne tp2)) && isSubType(lhs, rhs, depth) if (isSingleType(tp1) && isSingleType(tp2) || isConstantType(tp1) && isConstantType(tp2)) - return (tp1 =:= tp2) || retry(tp1.underlying, tp2) + return (tp1 =:= tp2) || isThisAndSuperSubtype(tp1, tp2) || retry(tp1.underlying, tp2) if (tp1.isHigherKinded || tp2.isHigherKinded) return isHKSubType(tp1, tp2, depth) diff --git a/src/reflect/scala/reflect/internal/util/Position.scala b/src/reflect/scala/reflect/internal/util/Position.scala index ddd0a64675..8e7df28167 100644 --- a/src/reflect/scala/reflect/internal/util/Position.scala +++ b/src/reflect/scala/reflect/internal/util/Position.scala @@ -1,273 +1,213 @@ /* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL * @author Martin Odersky - * */ package scala -package reflect.internal.util - -import scala.reflect.ClassTag -import scala.reflect.internal.FatalError -import scala.reflect.macros.Attachments - -object Position { - val tabInc = 8 - - /** Prints the message with the given position indication. */ - def formatMessage(posIn: Position, msg: String, shortenFile: Boolean): String = { - val pos = ( - if (posIn eq null) NoPosition - else if (posIn.isDefined) posIn.inUltimateSource(posIn.source) - else posIn - ) - val prefix = if (shortenFile) pos.sourceName else pos.sourcePath - - pos match { - case FakePos(fmsg) => fmsg+" "+msg - case NoPosition => msg - case _ => "%s:%s: %s\n%s\n%s".format(prefix, pos.line, msg, pos.lineContent, pos.lineCarat) - } - } -} +package reflect +package internal +package util /** The Position class and its subclasses represent positions of ASTs and symbols. - * Except for NoPosition and FakePos, every position refers to a SourceFile - * and to an offset in the sourcefile (its `point`). For batch compilation, - * that's all. For interactive IDE's there are also RangePositions - * and TransparentPositions. A RangePosition indicates a start and an end - * in addition to its point. TransparentPositions are a subclass of RangePositions. - * Range positions that are not transparent are called opaque. - * Trees with RangePositions need to satisfy the following invariants. + * Every subclass of DefinedPosition refers to a SourceFile and three character + * offsets within it: start, end, and point. The point is where the ^ belongs when + * issuing an error message, usually a Name. A range position can be designated + * as transparent, which excuses it from maintaining the invariants to follow. If + * a transparent position has opaque children, those are considered as if they were + * the direct children of the transparent position's parent. + * + * Note: some of these invariants actually apply to the trees which carry + * the positions, but they are phrased as if the positions themselves were + * the parent/children for conciseness. * - * INV1: A tree with an offset position never contains a child - * with a range position - * INV2: If the child of a tree with a range position also has a range position, - * then the child's range is contained in the parent's range. - * INV3: Opaque range positions of children of the same node are non-overlapping - * (this means their overlap is at most a single point). + * Invariant 1: in a focused/offset position, start == point == end + * Invariant 2: in a range position, start <= point < end + * Invariant 3: an offset position never has a child with a range position + * Invariant 4: every range position child of a range position parent is contained within its parent + * Invariant 5: opaque range position siblings overlap at most at a single point * * The following tests are useful on positions: * - * pos.isDefined true if position is not a NoPosition nor a FakePosition - * pos.isRange true if position is a range + * pos.isDefined true if position is not an UndefinedPosition (those being NoPosition and FakePos) + * pos.isRange true if position is a range (opaque or transparent) which implies start < end * pos.isOpaqueRange true if position is an opaque range * - * The following accessor methods are provided: + * The following accessor methods are provided - an exception will be thrown if + * point/start/end are attempted on an UndefinedPosition. * - * pos.source The source file of the position, which must be defined - * pos.point The offset of the position's point, which must be defined - * pos.start The start of the position, which must be a range - * pos.end The end of the position, which must be a range - * - * There are also convenience methods, such as - * - * pos.startOrPoint - * pos.endOrPoint - * pos.pointOrElse(default) - * - * These are less strict about the kind of position on which they can be applied. + * pos.source The source file of the position, or NoSourceFile if unavailable + * pos.point The offset of the point + * pos.start The (inclusive) start offset, or the point of an offset position + * pos.end The (exclusive) end offset, or the point of an offset position * * The following conversion methods are often used: * - * pos.focus converts a range position to an offset position, keeping its point; - * returns all other positions unchanged. - * pos.makeTransparent converts an opaque range position into a transparent one. - * returns all other positions unchanged. + * pos.focus Converts a range position to an offset position focused on the point + * pos.makeTransparent Convert an opaque range into a transparent range */ -abstract class Position extends scala.reflect.api.Position { self => - +class Position extends scala.reflect.api.Position with InternalPositionImpl with DeprecatedPosition { type Pos = Position - def pos: Position = this + def withPos(newPos: Position): macros.Attachments { type Pos = Position.this.Pos } = newPos + + protected def fail(what: String) = throw new UnsupportedOperationException(s"Position.$what on $this") + + // If scala-refactoring extends Position directly it seems I have no + // choice but to offer all the concrete methods. + def isDefined = false + def isRange = false + def source: SourceFile = NoSourceFile + def start: Int = fail("start") + def point: Int = fail("point") + def end: Int = fail("end") +} - def withPos(newPos: Position): Attachments { type Pos = self.Pos } = newPos - - /** An optional value containing the source file referred to by this position, or - * None if not defined. - */ - def source: SourceFile = throw new UnsupportedOperationException(s"Position.source on ${this.getClass}") - - /** Is this position neither a NoPosition nor a FakePosition? - * If isDefined is true, offset and source are both defined. - */ - def isDefined: Boolean = false - - /** Is this position a transparent position? */ - def isTransparent: Boolean = false - - /** Is this position a range position? */ - def isRange: Boolean = false - - /** Is this position a non-transparent range position? */ - def isOpaqueRange: Boolean = false - - /** if opaque range, make this position transparent */ - def makeTransparent: Position = this - - /** The start of the position's range, error if not a range position */ - def start: Int = throw new UnsupportedOperationException(s"Position.start on ${this.getClass}") - - /** The start of the position's range, or point if not a range position */ - def startOrPoint: Int = point - - /** The point (where the ^ is) of the position */ - def point: Int = throw new UnsupportedOperationException(s"Position.point on ${this.getClass}") - - /** The point (where the ^ is) of the position, or else `default` if undefined */ - def pointOrElse(default: Int): Int = default - - /** The end of the position's range, error if not a range position */ - def end: Int = throw new UnsupportedOperationException(s"Position.end on ${this.getClass}") - - /** The end of the position's range, or point if not a range position */ - def endOrPoint: Int = point - - @deprecated("use point instead", "2.9.0") - def offset: Option[Int] = if (isDefined) Some(point) else None // used by sbt - - /** The same position with a different start value (if a range) */ - def withStart(off: Int): Position = this - - /** The same position with a different end value (if a range) */ - def withEnd(off: Int): Position = this - - /** The same position with a different point value (if a range or offset) */ - def withPoint(off: Int): Position = this - - /** The same position with a different source value, and its values shifted by given offset */ - def withSource(source: SourceFile, shift: Int): Position = this - - /** If this is a range, the union with the other range, with the point of this position. - * Otherwise, this position - */ - def union(pos: Position): Position = this - - /** If this is a range position, the offset position of its start. - * Otherwise the position itself - */ - def focusStart: Position = this - - /** If this is a range position, the offset position of its point. - * Otherwise the position itself - */ - def focus: Position = this - - /** If this is a range position, the offset position of its end. - * Otherwise the position itself - */ - def focusEnd: Position = this - - /** Does this position include the given position `pos`. - * This holds if `this` is a range position and its range [start..end] - * is the same or covers the range of the given position, which may or may not be a range position. - */ - def includes(pos: Position): Boolean = false - - /** Does this position properly include the given position `pos` ("properly" meaning their - * ranges are not the same)? - */ - def properlyIncludes(pos: Position): Boolean = - includes(pos) && (start < pos.startOrPoint || pos.endOrPoint < end) - - /** Does this position precede that position? - * This holds if both positions are defined and the end point of this position - * is not larger than the start point of the given position. - */ - def precedes(pos: Position): Boolean = - isDefined && pos.isDefined && endOrPoint <= pos.startOrPoint - - /** Does this position properly precede the given position `pos` ("properly" meaning their ranges - * do not share a common point). - */ - def properlyPrecedes(pos: Position): Boolean = - isDefined && pos.isDefined && endOrPoint < pos.startOrPoint - - /** Does this position overlap with that position? - * This holds if both positions are ranges and there is an interval of - * non-zero length that is shared by both position ranges. - */ - def overlaps(pos: Position): Boolean = - isRange && pos.isRange && - ((pos.start < end && start < pos.end) || (start < pos.end && pos.start < end)) - - /** Does this position cover the same range as that position? - * Holds only if both position are ranges - */ - def sameRange(pos: Position): Boolean = - isRange && pos.isRange && start == pos.start && end == pos.end - - def line: Int = throw new UnsupportedOperationException("Position.line") +object Position { + val tabInc = 8 - def column: Int = throw new UnsupportedOperationException("Position.column") + private def validate[T <: Position](pos: T): T = { + if (pos.isRange) + assert(pos.start <= pos.end, s"bad position: ${pos.show}") - /** A line with a ^ padded with the right number of spaces. - */ - def lineCarat: String = " " * (column - 1) + "^" + pos + } - /** The line of code and the corresponding carat pointing line, trimmed - * to the maximum specified width, with the trimmed versions oriented - * around the point to give maximum context. - */ - def lineWithCarat(maxWidth: Int): (String, String) = { - val radius = maxWidth / 2 - var start = scala.math.max(column - radius, 0) - var result = lineContent drop start take maxWidth - - if (result.length < maxWidth) { - result = lineContent takeRight maxWidth - start = lineContent.length - result.length + /** Prints the message with the given position indication. */ + def formatMessage(posIn: Position, msg: String, shortenFile: Boolean): String = { + val pos = if (posIn eq null) NoPosition else posIn + val prefix = pos.source match { + case NoSourceFile => "" + case s if shortenFile => s.file.name + ":" + case s => s.file.path + ":" } - - (result, lineCarat drop start take maxWidth) + prefix + (pos showError msg) } - /** Convert this to a position around `point` that spans a single source line */ - def toSingleLine: Position = this - - /** The source code corresponding to the range, if this is a range position. - * Otherwise the empty string. - */ - def sourceCode = "" - def sourceName = "<none>" - def sourcePath = "<none>" - def lineContent = "<none>" - def lengthInChars = 0 - def lengthInLines = 0 - - /** Map this position to a position in an original source - * file. If the SourceFile is a normal SourceFile, simply - * return this. - */ - def inUltimateSource(source : SourceFile): Position = - if (source == null) this else source.positionInUltimateSource(this) - - def dbgString: String = toString - def safeLine: Int = try line catch { case _: UnsupportedOperationException => -1 } - - def show: String = "["+toString+"]" + def offset(source: SourceFile, point: Int): Position = validate(new OffsetPosition(source, point)) + def range(source: SourceFile, start: Int, point: Int, end: Int): Position = validate(new RangePosition(source, start, point, end)) + def transparent(source: SourceFile, start: Int, point: Int, end: Int): Position = validate(new TransparentPosition(source, start, point, end)) } -case object NoPosition extends Position { - override def dbgString = toString +class OffsetPosition(sourceIn: SourceFile, pointIn: Int) extends DefinedPosition { + override def isRange = false + override def source = sourceIn + override def point = pointIn + override def start = point + override def end = point } - -case class FakePos(msg: String) extends Position { +class RangePosition(sourceIn: SourceFile, startIn: Int, pointIn: Int, endIn: Int) extends OffsetPosition(sourceIn, pointIn) { + override def isRange = true + override def start = startIn + override def end = endIn +} +class TransparentPosition(sourceIn: SourceFile, startIn: Int, pointIn: Int, endIn: Int) extends RangePosition(sourceIn, startIn, pointIn, endIn) { + override def isTransparent = true +} +case object NoPosition extends UndefinedPosition +case class FakePos(msg: String) extends UndefinedPosition { override def toString = msg } -class OffsetPosition(override val source: SourceFile, override val point: Int) extends Position { - override def isDefined = true - override def pointOrElse(default: Int): Int = point - override def withPoint(off: Int) = new OffsetPosition(source, off) - override def withSource(source: SourceFile, shift: Int) = new OffsetPosition(source, point + shift) +sealed abstract class DefinedPosition extends Position { + final override def isDefined = true + override def equals(that: Any) = that match { + case that: DefinedPosition => source.file == that.source.file && start == that.start && point == that.point && end == that.end + case _ => false + } + override def hashCode = Seq[Any](source.file, start, point, end).## + override def toString = ( + if (isRange) s"RangePosition($canonicalPath, $start, $point, $end)" + else s"source-$canonicalPath,line-$line,$pointMessage$point" + ) + private def pointMessage = if (point > source.length) "out-of-bounds-" else "offset=" + private def canonicalPath = source.file.canonicalPath +} - override def line = source.offsetToLine(point) + 1 - override def sourceName = source.file.name - override def sourcePath = source.file.path - override def lineContent = source.lineToString(line - 1) +sealed abstract class UndefinedPosition extends Position { + final override def isDefined = false + override def isRange = false + override def source = NoSourceFile + override def start = fail("start") + override def point = fail("point") + override def end = fail("end") +} - override def column: Int = { +private[util] trait InternalPositionImpl { + self: Position => + + // The methods which would be abstract in Position if it were + // possible to change Position. + def isDefined: Boolean + def isRange: Boolean + def source: SourceFile + def start: Int + def point: Int + def end: Int + + /** Map this position to its position in the original source file + * (which may be this position unchanged.) + */ + def finalPosition: Pos = source positionInUltimateSource this + + def isTransparent = false + def isOffset = isDefined && !isRange + def isOpaqueRange = isRange && !isTransparent + def pointOrElse(alt: Int): Int = if (isDefined) point else alt + def makeTransparent: Position = if (isOpaqueRange) Position.transparent(source, start, point, end) else this + + /** Copy a range position with a changed value. + */ + def withStart(start: Int): Position = copyRange(start = start) + def withPoint(point: Int): Position = if (isRange) copyRange(point = point) else Position.offset(source, point) + def withEnd(end: Int): Position = copyRange(end = end) + def withSource(source: SourceFile): Position = copyRange(source = source) + def withShift(shift: Int): Position = Position.range(source, start + shift, point + shift, end + shift) + + /** Convert a range position to a simple offset. + */ + def focusStart: Position = if (this.isRange) asOffset(start) else this + def focus: Position = if (this.isRange) asOffset(point) else this + def focusEnd: Position = if (this.isRange) asOffset(end) else this + + def union(pos: Position): Position = ( + if (!pos.isRange) this + else if (this.isRange) copyRange(start = start min pos.start, end = end max pos.end) + else pos + ) + + def includes(pos: Position): Boolean = isRange && pos.isDefined && start <= pos.start && pos.end <= end + def properlyIncludes(pos: Position): Boolean = includes(pos) && (start < pos.start || pos.end < end) + def precedes(pos: Position): Boolean = bothDefined(pos) && end <= pos.start + def properlyPrecedes(pos: Position): Boolean = bothDefined(pos) && end < pos.start + def sameRange(pos: Position): Boolean = bothRanges(pos) && start == pos.start && end == pos.end + // This works because it's a range position invariant that S1 < E1 and S2 < E2. + // So if S1 < E2 and S2 < E1, then both starts precede both ends, which is the + // necessary condition to establish that there is overlap. + def overlaps(pos: Position): Boolean = bothRanges(pos) && start < pos.end && pos.start < end + + def line: Int = if (hasSource) source.offsetToLine(point) + 1 else 0 + def column: Int = if (hasSource) calculateColumn() else 0 + def lineContent: String = if (hasSource) source.lineToString(line - 1) else "" + def lineCarat: String = if (hasSource) " " * (column - 1) + "^" else "" + + def showError(msg: String): String = finalPosition match { + case FakePos(fmsg) => s"$fmsg $msg" + case NoPosition => msg + case pos => s"${pos.line}: $msg\n${pos.lineContent}\n${pos.lineCarat}" + } + def showDebug: String = toString + def show = ( + if (isOpaqueRange) s"[$start:$end]" + else if (isTransparent) s"<$start:$end>" + else if (isDefined) s"[$point]" + else "[NoPosition]" + ) + + private def asOffset(point: Int): Position = Position.offset(source, point) + private def copyRange(source: SourceFile = source, start: Int = start, point: Int = point, end: Int = end): Position = + Position.range(source, start, point, end) + + private def calculateColumn(): Int = { var idx = source.lineToOffset(source.offsetToLine(point)) var col = 0 while (idx != point) { @@ -276,18 +216,39 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e } col + 1 } + private def hasSource = source ne NoSourceFile + private def bothRanges(that: Position) = isRange && that.isRange + private def bothDefined(that: Position) = isDefined && that.isDefined +} - override def union(pos: Position) = if (pos.isRange) pos else this +/** Holding cell for methods unused and/or unnecessary. */ +private[util] trait DeprecatedPosition { + self: Position => - override def equals(that : Any) = that match { - case that : OffsetPosition => point == that.point && source.file == that.source.file - case that => false - } - override def hashCode = point * 37 + source.file.hashCode + @deprecated("use `point`", "2.9.0") + def offset: Option[Int] = if (isDefined) Some(point) else None // used by sbt - override def toString = { - val pointmsg = if (point > source.length) "out-of-bounds-" else "offset=" - "source-%s,line-%s,%s%s".format(source.file.canonicalPath, line, pointmsg, point) - } - override def show = "["+point+"]" + @deprecated("use `focus`", "2.11.0") + def toSingleLine: Position = this + + @deprecated("use `line`", "2.11.0") + def safeLine: Int = line + + @deprecated("use `showDebug`", "2.11.0") + def dbgString: String = showDebug + + @deprecated("use `finalPosition`", "2.11.0") + def inUltimateSource(source: SourceFile): Position = source positionInUltimateSource this + + @deprecated("use `lineCarat`", "2.11.0") + def lineWithCarat(maxWidth: Int): (String, String) = ("", "") + + @deprecated("Use `withSource(source)` and `withShift`", "2.11.0") + def withSource(source: SourceFile, shift: Int): Position = this withSource source withShift shift + + @deprecated("Use `start`", "2.11.0") + def startOrPoint: Int = if (isRange) start else point + + @deprecated("Use `end`", "2.11.0") + def endOrPoint: Int = if (isRange) end else point } diff --git a/src/reflect/scala/reflect/internal/util/RangePosition.scala b/src/reflect/scala/reflect/internal/util/RangePosition.scala deleted file mode 100644 index 0d09a53cd9..0000000000 --- a/src/reflect/scala/reflect/internal/util/RangePosition.scala +++ /dev/null @@ -1,50 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala -package reflect.internal.util - -/** new for position ranges */ -class RangePosition(source: SourceFile, override val start: Int, point: Int, override val end: Int) -extends OffsetPosition(source, point) { - if (start > end) scala.sys.error("bad position: "+show) - override def isRange: Boolean = true - override def isOpaqueRange: Boolean = true - override def startOrPoint: Int = start - override def endOrPoint: Int = end - override def withStart(off: Int) = new RangePosition(source, off, point, end) - override def withEnd(off: Int) = new RangePosition(source, start, point, off) - override def withPoint(off: Int) = new RangePosition(source, start, off, end) - override def withSource(source: SourceFile, shift: Int) = new RangePosition(source, start + shift, point + shift, end + shift) - override def focusStart = new OffsetPosition(source, start) - override def focus = { - if (focusCache eq NoPosition) focusCache = new OffsetPosition(source, point) - focusCache - } - override def focusEnd = new OffsetPosition(source, end) - override def makeTransparent = new TransparentPosition(source, start, point, end) - override def includes(pos: Position) = pos.isDefined && start <= pos.startOrPoint && pos.endOrPoint <= end - override def union(pos: Position): Position = - if (pos.isRange) new RangePosition(source, start min pos.start, point, end max pos.end) else this - - override def toSingleLine: Position = source match { - case bs: BatchSourceFile - if end > 0 && bs.offsetToLine(start) < bs.offsetToLine(end - 1) => - val pointLine = bs.offsetToLine(point) - new RangePosition(source, bs.lineToOffset(pointLine), point, bs.lineToOffset(pointLine + 1)) - case _ => this - } - - override def toString = "RangePosition("+source.file.canonicalPath+", "+start+", "+point+", "+end+")" - override def show = "["+start+":"+end+"]" - private var focusCache: Position = NoPosition -} - -class TransparentPosition(source: SourceFile, start: Int, point: Int, end: Int) extends RangePosition(source, start, point, end) { - override def isOpaqueRange: Boolean = false - override def isTransparent = true - override def makeTransparent = this - override def show = "<"+start+":"+end+">" -} diff --git a/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala index 6406dacc24..bb5fe07fc9 100644 --- a/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala @@ -28,7 +28,7 @@ trait ExprTyper { case IR.Success => val sym0 = symbolOfTerm(name) // drop NullaryMethodType - sym0.cloneSymbol setInfo exitingTyper(sym0.info.finalResultType) + sym0.cloneSymbol setInfo exitingTyper(sym0.tpe_*.finalResultType) case _ => NoSymbol } } diff --git a/test/disabled/run/t7843-jsr223-service.check b/test/disabled/run/t7843-jsr223-service.check new file mode 100644 index 0000000000..a668df3567 --- /dev/null +++ b/test/disabled/run/t7843-jsr223-service.check @@ -0,0 +1,2 @@ +n: Object = 10 +12345678910 diff --git a/test/disabled/run/t7843-jsr223-service.scala b/test/disabled/run/t7843-jsr223-service.scala new file mode 100644 index 0000000000..e2ea850698 --- /dev/null +++ b/test/disabled/run/t7843-jsr223-service.scala @@ -0,0 +1,18 @@ +/*DISABLED: + For Paul, it steals focus when it runs. + + For me, it fails with some platform specific extra output: + + -ScriptEngineManager providers.next(): javax.script.ScriptEngineFactory: Provider apple.applescript.AppleScriptEngin + n: Object = 10 + 12345678910 +*/ +import javax.script._ +import scala.tools.nsc.interpreter.IMain + +object Test extends App { + val engine = new ScriptEngineManager getEngineByName "scala" + engine.asInstanceOf[IMain].settings.usejavacp.value = true + engine put ("n", 10) + engine eval "1 to n.asInstanceOf[Int] foreach print" +} diff --git a/test/files/neg/t1909-object.check b/test/files/neg/t1909-object.check new file mode 100644 index 0000000000..401c1f7ebf --- /dev/null +++ b/test/files/neg/t1909-object.check @@ -0,0 +1,4 @@ +t1909-object.scala:4: error: !!! SI-1909 Unable to STATICally lift object InnerTrouble$1, which is defined in the self- or super-constructor call of class Kaboom. A VerifyError is likely. + object InnerTrouble + ^ +one error found diff --git a/test/files/neg/t1909-object.flags b/test/files/neg/t1909-object.flags new file mode 100644 index 0000000000..eb8b40661b --- /dev/null +++ b/test/files/neg/t1909-object.flags @@ -0,0 +1 @@ +-Xdev -Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t1909-object.scala b/test/files/neg/t1909-object.scala new file mode 100644 index 0000000000..d6011ba4a5 --- /dev/null +++ b/test/files/neg/t1909-object.scala @@ -0,0 +1,12 @@ +class Kaboom(a: Any) { + def this() = { + this({ + object InnerTrouble + InnerTrouble + }) + } +} + +object Test extends App { + new Kaboom() +}
\ No newline at end of file diff --git a/test/files/neg/t284.check b/test/files/neg/t284.check index 37801af1b5..7c2e9be03e 100644 --- a/test/files/neg/t284.check +++ b/test/files/neg/t284.check @@ -1,8 +1,6 @@ t284.scala:2: warning: Detected apparent refinement of Unit; are you missing an '=' sign? - def f1(a: T): Unit { } - ^ -t284.scala:5: error: Unmatched closing brace '}' ignored here - } - ^ + def f1(a: T): Unit { } + ^ +error: No warnings can be incurred under -Xfatal-warnings. one warning found one error found diff --git a/test/files/neg/t284.flags b/test/files/neg/t284.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/t284.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t284.scala b/test/files/neg/t284.scala index a210130102..f75bc3d4ba 100644 --- a/test/files/neg/t284.scala +++ b/test/files/neg/t284.scala @@ -1,6 +1,5 @@ trait B[T] { - def f1(a: T): Unit { } - def f2(a: T): Unit - def f3(a: T) { } - } + def f1(a: T): Unit { } + def f2(a: T): Unit + def f3(a: T) { } } 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/t7007.check b/test/files/neg/t7007.check new file mode 100644 index 0000000000..e22ecb9e4e --- /dev/null +++ b/test/files/neg/t7007.check @@ -0,0 +1,7 @@ +t7007.scala:5: error: Implementation restriction: <$anon: A => B> requires premature access to class Crash. + def this(a: Seq[A]) = this(a.collect{ case b: B => b}, a.collect{ case b: B => b}) + ^ +t7007.scala:5: error: Implementation restriction: <$anon: A => B> requires premature access to class Crash. + def this(a: Seq[A]) = this(a.collect{ case b: B => b}, a.collect{ case b: B => b}) + ^ +two errors found diff --git a/test/files/neg/t7007.scala b/test/files/neg/t7007.scala new file mode 100644 index 0000000000..e41dccf550 --- /dev/null +++ b/test/files/neg/t7007.scala @@ -0,0 +1,14 @@ +class A +class B extends A + +class Crash(b1: Seq[B], b2: Seq[B]) { + def this(a: Seq[A]) = this(a.collect{ case b: B => b}, a.collect{ case b: B => b}) +} + +object Main extends App { + + // runtime exception with either constructor + val c1 = new Crash(Seq(new B, new B)) + val c2 = new Crash(Seq(new B), Seq(new B)) + +} 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/neg/t7834neg.check b/test/files/neg/t7834neg.check new file mode 100644 index 0000000000..569df4b8ce --- /dev/null +++ b/test/files/neg/t7834neg.check @@ -0,0 +1,41 @@ +t7834neg.scala:48: error: type mismatch; + found : C.super.q.type (with underlying type M2) + required: C.super.q.type + x1 = x2 // fail + ^ +t7834neg.scala:50: error: type mismatch; + found : C.super.q.type (with underlying type M1) + required: C.super.q.type + x2 = x1 // fail + ^ +t7834neg.scala:53: error: type mismatch; + found : C.super.q.type (with underlying type M1) + required: C.this.q.type + x3 = x1 // fail + ^ +t7834neg.scala:54: error: type mismatch; + found : C.super.q.type (with underlying type M2) + required: C.this.q.type + x3 = x2 // fail + ^ +t7834neg.scala:69: error: type mismatch; + found : C.super.q.type (with underlying type M2) + required: C.super.q.type + x1 = super[S2].q // fail + ^ +t7834neg.scala:71: error: type mismatch; + found : C.super.q.type (with underlying type M1) + required: C.super.q.type + x2 = super[S1].q // fail + ^ +t7834neg.scala:74: error: type mismatch; + found : C.super.q.type (with underlying type M1) + required: C.this.q.type + x3 = super[S1].q // fail + ^ +t7834neg.scala:75: error: type mismatch; + found : C.super.q.type (with underlying type M2) + required: C.this.q.type + x3 = super[S2].q // fail + ^ +8 errors found diff --git a/test/files/neg/t7834neg.scala b/test/files/neg/t7834neg.scala new file mode 100644 index 0000000000..d35a84eadd --- /dev/null +++ b/test/files/neg/t7834neg.scala @@ -0,0 +1,76 @@ +class M1 +class M2 extends M1 +class M3 extends M2 + +trait S1 { val q = new M1 ; val q1: q.type = q } +trait S2 { val q = new M2 ; val q2: q.type = q } + +class B extends S1 with S2 { + override val q = new M3 + val q3: q.type = q + + var x1: B.super[S1].q1.type = null + var x2: B.super[S2].q2.type = null + var x3: B.this.q3.type = null + + x1 = x1 + x1 = x2 + x1 = x3 + x2 = x1 + x2 = x2 + x2 = x3 + x3 = x1 + x3 = x2 + x3 = x3 + + x1 = q1 + x1 = q2 + x1 = q3 + x2 = q1 + x2 = q2 + x2 = q3 + x3 = q1 + x3 = q2 + x3 = x3 +} + +class C extends S1 with S2 { + override val q = new M3 + val q3: q.type = q + + // x1's type and x2's type are incompatible + // x3's is assignable to x1 or x2, but not vice versa + var x1: C.super[S1].q.type = null + var x2: C.super[S2].q.type = null + var x3: C.this.q.type = null + + x1 = x1 + x1 = x2 // fail + x1 = x3 + x2 = x1 // fail + x2 = x2 + x2 = x3 + x3 = x1 // fail + x3 = x2 // fail + x3 = x3 + + x1 = q1 + x1 = q2 + x1 = q3 + x2 = q1 + x2 = q2 + x2 = q3 + x3 = q1 + x3 = q2 + x3 = x3 + + x1 = q + x1 = super[S1].q + x1 = super[S2].q // fail + x2 = q + x2 = super[S1].q // fail + x2 = super[S2].q + x3 = q + x3 = super[S1].q // fail + x3 = super[S2].q // fail +} diff --git a/test/files/pos/infersingle.scala b/test/files/pos/infersingle.scala index 6830fcd799..60f4ff07e6 100644 --- a/test/files/pos/infersingle.scala +++ b/test/files/pos/infersingle.scala @@ -1,5 +1,52 @@ -object Test { +object Test1 { def one[T](x: T): Option[T] = Some(x) val x = "one" val y: Option[x.type] = one(x) -}
\ No newline at end of file +} + +object Test2 { + // Has never worked, but seems desirable given the recent changes to + // pattern type inference. + val a = "" + object Id { + def unapply(xxxx: Any): Some[a.type] = Some[a.type](a) + } + val b: a.type = (a: a.type) match { + case Id(x) => x + } +} + +object Test3 { + val a = "" + object Id { + def unapply(xxxx: Any): Some[Test3.type] = Some[Test3.type](Test3) + } + val b: Test3.type = a match { + case Id(x) => x + } +} + +class Test4 { + val a = "" + object Id { + def unapply(xxxx: Any): Some[Test4.this.type] = Some[Test4.this.type](Test4.this) + } + val b: Test4.this.type = a match { + case Id(x) => x + } +} + +class Super5 { + final val q = "" + def q1: q.type = q +} + +class Test5 extends Super5 { + val a = "" + object Id { + def unapply(xxxx: Any): Some[Test5.super.q.type] = Some[Test5.super.q.type](q1) + } + val b: Test5.super.q.type = a match { + case Id(x) => x + } +} diff --git a/test/files/pos/private-types-after-typer.scala b/test/files/pos/private-types-after-typer.scala new file mode 100644 index 0000000000..79ef934063 --- /dev/null +++ b/test/files/pos/private-types-after-typer.scala @@ -0,0 +1,9 @@ +// Testing that the type of the outer accessor in O2 +// doesn't crash the compiler over private type escaping scope. +trait T { + class C { + private object O1 { + object O2 + } + } +}
\ No newline at end of file diff --git a/test/files/pos/t7818.scala b/test/files/pos/t7818.scala new file mode 100644 index 0000000000..77b99e7d5d --- /dev/null +++ b/test/files/pos/t7818.scala @@ -0,0 +1,10 @@ +class Observable1[+T](val asJava: JObservable[_ <: T]) extends AnyVal { + private def foo[X](a: JObservable[X]): JObservable[X] = ??? + // was generating a type error as the type of the RHS included an existential + // skolem based on the class type parameter `T`, which did not conform + // to the typer parameter of the extension method into which the RHS is + // transplanted. + def synchronize: Observable1[T] = new Observable1(foo(asJava)) +} + +class JObservable[T] diff --git a/test/files/pos/t7834.scala b/test/files/pos/t7834.scala new file mode 100644 index 0000000000..fc9a0aa09d --- /dev/null +++ b/test/files/pos/t7834.scala @@ -0,0 +1,6 @@ +class S { val q = "" } + +class B extends S { + val x1: B.super.q.type = q + val x2: B.this.q.type = q +} diff --git a/test/files/run/compiler-asSeenFrom.check b/test/files/run/compiler-asSeenFrom.check index 47d40b0331..a1826c2784 100644 --- a/test/files/run/compiler-asSeenFrom.check +++ b/test/files/run/compiler-asSeenFrom.check @@ -1,6 +1,54 @@ class C { type seen from prefix is ---- ---------------- -- + C.this.I[Int] C[List[T3]] C[List[T3]]#I[Int] + C.this.I[Int] C[T1] C[T1]#I[Int] + C.this.I[Int] D[A1] D[A1]#I[Int] + C.this.I[Int] D[T3] D[T3]#I[Int] + C.this.I[List[Int]] C[List[T3]] C[List[T3]]#I[List[Int]] + C.this.I[List[Int]] C[T1] C[T1]#I[List[Int]] + C.this.I[List[Int]] D[A1] D[A1]#I[List[Int]] + C.this.I[List[Int]] D[T3] D[T3]#I[List[Int]] + C.this.I[T1] C[List[T3]] C[List[T3]]#I[List[T3]] + C.this.I[T1] C[T1] C[T1]#I[T1] + C.this.I[T1] D[A1] D[A1]#I[A1] + C.this.I[T1] D[T3] D[T3]#I[T3] + C.this.I[T2] C[List[T3]] C[List[T3]]#I[T2] + C.this.I[T2] C[T1] C[T1]#I[T2] + C.this.I[T2] D[A1] D[A1]#I[T2] + C.this.I[T2] D[T3] D[T3]#I[T2] + C.this.I[T3] C[List[T3]] C[List[T3]]#I[T3] + C.this.I[T3] C[T1] C[T1]#I[T3] + C.this.I[T3] D[A1] D[A1]#I[T3] + C.this.I[T3] D[T3] D[T3]#I[T3] + C.this.I[T4] C[List[T3]] C[List[T3]]#I[T4] + C.this.I[T4] C[T1] C[T1]#I[T4] + C.this.I[T4] D[A1] D[A1]#I[T4] + C.this.I[T4] D[T3] D[T3]#I[T4] + C.this.J[Int] C[List[T3]] C[List[T3]]#J[Int] + C.this.J[Int] C[T1] C[T1]#J[Int] + C.this.J[Int] D[A1] D[A1]#J[Int] + C.this.J[Int] D[T3] D[T3]#J[Int] + C.this.J[List[Int]] C[List[T3]] C[List[T3]]#J[List[Int]] + C.this.J[List[Int]] C[T1] C[T1]#J[List[Int]] + C.this.J[List[Int]] D[A1] D[A1]#J[List[Int]] + C.this.J[List[Int]] D[T3] D[T3]#J[List[Int]] + C.this.J[T1] C[List[T3]] C[List[T3]]#J[List[T3]] + C.this.J[T1] C[T1] C[T1]#J[T1] + C.this.J[T1] D[A1] D[A1]#J[A1] + C.this.J[T1] D[T3] D[T3]#J[T3] + C.this.J[T2] C[List[T3]] C[List[T3]]#J[T2] + C.this.J[T2] C[T1] C[T1]#J[T2] + C.this.J[T2] D[A1] D[A1]#J[T2] + C.this.J[T2] D[T3] D[T3]#J[T2] + C.this.J[T3] C[List[T3]] C[List[T3]]#J[T3] + C.this.J[T3] C[T1] C[T1]#J[T3] + C.this.J[T3] D[A1] D[A1]#J[T3] + C.this.J[T3] D[T3] D[T3]#J[T3] + C.this.J[T4] C[List[T3]] C[List[T3]]#J[T4] + C.this.J[T4] C[T1] C[T1]#J[T4] + C.this.J[T4] D[A1] D[A1]#J[T4] + C.this.J[T4] D[T3] D[T3]#J[T4] C[List[T3]]#I[T1] D[A1] C[List[T3]]#I[A1] C[List[T3]]#I[T1] D[T3] C[List[T3]]#I[T3] C[List[T3]]#J[T1] D[A1] C[List[T3]]#J[A1] @@ -49,6 +97,8 @@ class C { class D { type seen from prefix is ---- ---------------- -- + C.this.I[T3] D[A1] C.this.I[A1] + C.this.J[T3] D[A1] C.this.J[A1] C[List[T3]]#I[Int] D[A1] C[List[A1]]#I[Int] C[List[T3]]#I[List[Int]] D[A1] C[List[A1]]#I[List[Int]] C[List[T3]]#I[T1] D[A1] C[List[A1]]#I[T1] @@ -73,6 +123,42 @@ class D { class I { type seen from prefix is ---- ---------------- -- + C.this.I[Int] D.this.J[T4] D.this.cD.I[Int] + C.this.I[Int] Z.dZ.J[A2] Z.dZ.cD.I[Int] + C.this.I[Int] Z.dZ.J[P] Z.dZ.cD.I[Int] + C.this.I[List[Int]] D.this.J[T4] D.this.cD.I[List[Int]] + C.this.I[List[Int]] Z.dZ.J[A2] Z.dZ.cD.I[List[Int]] + C.this.I[List[Int]] Z.dZ.J[P] Z.dZ.cD.I[List[Int]] + C.this.I[T1] D.this.J[T4] D.this.cD.I[List[T3]] + C.this.I[T1] Z.dZ.J[A2] Z.dZ.cD.I[List[A1]] + C.this.I[T1] Z.dZ.J[P] Z.dZ.cD.I[List[A1]] + C.this.I[T2] D.this.J[T4] D.this.cD.I[T4] + C.this.I[T2] Z.dZ.J[A2] Z.dZ.cD.I[A2] + C.this.I[T2] Z.dZ.J[P] Z.dZ.cD.I[P] + C.this.I[T3] D.this.J[T4] D.this.cD.I[T3] + C.this.I[T3] Z.dZ.J[A2] Z.dZ.cD.I[T3] + C.this.I[T3] Z.dZ.J[P] Z.dZ.cD.I[T3] + C.this.I[T4] D.this.J[T4] D.this.cD.I[T4] + C.this.I[T4] Z.dZ.J[A2] Z.dZ.cD.I[T4] + C.this.I[T4] Z.dZ.J[P] Z.dZ.cD.I[T4] + C.this.J[Int] D.this.J[T4] D.this.cD.J[Int] + C.this.J[Int] Z.dZ.J[A2] Z.dZ.cD.J[Int] + C.this.J[Int] Z.dZ.J[P] Z.dZ.cD.J[Int] + C.this.J[List[Int]] D.this.J[T4] D.this.cD.J[List[Int]] + C.this.J[List[Int]] Z.dZ.J[A2] Z.dZ.cD.J[List[Int]] + C.this.J[List[Int]] Z.dZ.J[P] Z.dZ.cD.J[List[Int]] + C.this.J[T1] D.this.J[T4] D.this.cD.J[List[T3]] + C.this.J[T1] Z.dZ.J[A2] Z.dZ.cD.J[List[A1]] + C.this.J[T1] Z.dZ.J[P] Z.dZ.cD.J[List[A1]] + C.this.J[T2] D.this.J[T4] D.this.cD.J[T4] + C.this.J[T2] Z.dZ.J[A2] Z.dZ.cD.J[A2] + C.this.J[T2] Z.dZ.J[P] Z.dZ.cD.J[P] + C.this.J[T3] D.this.J[T4] D.this.cD.J[T3] + C.this.J[T3] Z.dZ.J[A2] Z.dZ.cD.J[T3] + C.this.J[T3] Z.dZ.J[P] Z.dZ.cD.J[T3] + C.this.J[T4] D.this.J[T4] D.this.cD.J[T4] + C.this.J[T4] Z.dZ.J[A2] Z.dZ.cD.J[T4] + C.this.J[T4] Z.dZ.J[P] Z.dZ.cD.J[T4] C[List[T3]]#I[T1] D.this.J[T4] C[List[T3]]#I[List[T3]] C[List[T3]]#I[T1] Z.dZ.J[A2] C[List[T3]]#I[List[A1]] C[List[T3]]#I[T1] Z.dZ.J[P] C[List[T3]]#I[List[A1]] @@ -137,6 +223,14 @@ class I { class J { type seen from prefix is ---- ---------------- -- + C.this.I[T3] Z.dZ.J[A2] C.this.I[A1] + C.this.I[T3] Z.dZ.J[P] C.this.I[A1] + C.this.I[T4] Z.dZ.J[A2] C.this.I[A2] + C.this.I[T4] Z.dZ.J[P] C.this.I[P] + C.this.J[T3] Z.dZ.J[A2] C.this.J[A1] + C.this.J[T3] Z.dZ.J[P] C.this.J[A1] + C.this.J[T4] Z.dZ.J[A2] C.this.J[A2] + C.this.J[T4] Z.dZ.J[P] C.this.J[P] C[List[T3]]#I[Int] Z.dZ.J[A2] C[List[A1]]#I[Int] C[List[T3]]#I[Int] Z.dZ.J[P] C[List[A1]]#I[Int] C[List[T3]]#I[List[Int]] Z.dZ.J[A2] C[List[A1]]#I[List[Int]] @@ -269,8 +363,8 @@ value jZ { // after parser value jZ { // after explicitouter protected val $outer: D.this.type - val $outer(): D.this.type - val $outer(): C.this.type + val $outer(): ll.D[T3] + val $outer(): ll.C[T1] def thisI(): I.this.type def thisC(): C.this.type def t2(): T2 diff --git a/test/files/run/idempotency-case-classes.check b/test/files/run/idempotency-case-classes.check index e0453883ff..5a8d0ad9d3 100644 --- a/test/files/run/idempotency-case-classes.check +++ b/test/files/run/idempotency-case-classes.check @@ -47,7 +47,7 @@ C(2,3) case <synthetic> def unapply(x$0: C): Option[(Int, Int)] = if (x$0.==(null)) scala.this.None else - Some.apply[(Int, Int)](Tuple2.apply[Int, Int](x$0.x, x$0.y)); + Some.apply[(Int, Int)](scala.Tuple2.apply[Int, Int](x$0.x, x$0.y)); <synthetic> private def readResolve(): Object = C }; Predef.println(C.apply(2, 3)) 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/predef-cycle.scala b/test/files/run/predef-cycle.scala new file mode 100644 index 0000000000..ab147688bc --- /dev/null +++ b/test/files/run/predef-cycle.scala @@ -0,0 +1,71 @@ +class Force { + val t1 = new Thread { + override def run() { + scala.`package` + } + } + val t2 = new Thread { + override def run() { + scala.Predef + } + } + t1.start() + t2.start() + t1.join() + t2.join() +} + +object Test { + def main(args: Array[String]) { + new Force() + } +} + +/* Was deadlocking: +"Thread-2" prio=5 tid=7f9637268000 nid=0x119601000 in Object.wait() [119600000] + java.lang.Thread.State: RUNNABLE + at scala.Predef$.<init>(Predef.scala:90) + at scala.Predef$.<clinit>(Predef.scala) + at Force$$anon$2.run(predef-cycle.scala:10) + +"Thread-1" prio=5 tid=7f9637267800 nid=0x1194fe000 in Object.wait() [1194fb000] + java.lang.Thread.State: RUNNABLE + at scala.collection.immutable.Set$Set4.$plus(Set.scala:127) + at scala.collection.immutable.Set$Set4.$plus(Set.scala:121) + at scala.collection.mutable.SetBuilder.$plus$eq(SetBuilder.scala:24) + at scala.collection.mutable.SetBuilder.$plus$eq(SetBuilder.scala:22) + at scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1.apply(Growable.scala:48) + at scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1.apply(Growable.scala:48) + at scala.collection.immutable.List.foreach(List.scala:318) + at scala.collection.generic.Growable$class.$plus$plus$eq(Growable.scala:48) + at scala.collection.mutable.SetBuilder.$plus$plus$eq(SetBuilder.scala:22) + at scala.collection.TraversableLike$class.to(TraversableLike.scala:629) + at scala.collection.AbstractTraversable.to(Traversable.scala:105) + at scala.collection.TraversableOnce$class.toSet(TraversableOnce.scala:267) + at scala.collection.AbstractTraversable.toSet(Traversable.scala:105) + at scala.runtime.ScalaRunTime$.<init>(ScalaRunTime.scala:50) + at scala.runtime.ScalaRunTime$.<clinit>(ScalaRunTime.scala) + at scala.collection.mutable.HashTable$HashUtils$class.elemHashCode(HashTable.scala) + at scala.collection.mutable.HashMap.elemHashCode(HashMap.scala:39) + at scala.collection.mutable.HashTable$class.findOrAddEntry(HashTable.scala:161) + at scala.collection.mutable.HashMap.findOrAddEntry(HashMap.scala:39) + at scala.collection.mutable.HashMap.put(HashMap.scala:75) + at scala.collection.mutable.HashMap.update(HashMap.scala:80) + at scala.sys.SystemProperties$.addHelp(SystemProperties.scala:64) + at scala.sys.SystemProperties$.bool(SystemProperties.scala:68) + at scala.sys.SystemProperties$.noTraceSupression$lzycompute(SystemProperties.scala:80) + - locked <7b8b0e228> (a scala.sys.SystemProperties$) + at scala.sys.SystemProperties$.noTraceSupression(SystemProperties.scala:80) + at scala.util.control.NoStackTrace$.<init>(NoStackTrace.scala:31) + at scala.util.control.NoStackTrace$.<clinit>(NoStackTrace.scala) + at scala.util.control.NoStackTrace$class.fillInStackTrace(NoStackTrace.scala:22) + at scala.util.control.BreakControl.fillInStackTrace(Breaks.scala:93) + at java.lang.Throwable.<init>(Throwable.java:181) + at scala.util.control.BreakControl.<init>(Breaks.scala:93) + at scala.util.control.Breaks.<init>(Breaks.scala:28) + at scala.collection.Traversable$.<init>(Traversable.scala:96) + at scala.collection.Traversable$.<clinit>(Traversable.scala) + at scala.package$.<init>(package.scala:46) + at scala.package$.<clinit>(package.scala) + at Force$$anon$1.run(predef-cycle.scala:4) + */
\ No newline at end of file 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/t1909.check b/test/files/run/t1909.check new file mode 100644 index 0000000000..7d25be60fd --- /dev/null +++ b/test/files/run/t1909.check @@ -0,0 +1,3 @@ +t1909.scala:7: warning: A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled. + def this(p: String) = this(try 0) + ^ diff --git a/test/files/pos/t1909.scala b/test/files/run/t1909.scala index 01213f62a3..8ead7bacf2 100644 --- a/test/files/pos/t1909.scala +++ b/test/files/run/t1909.scala @@ -6,3 +6,7 @@ class Ticket1909 { def this(value: Int) = this() def this(p: String) = this(try 0) } + +object Test extends App { + new Ticket1909("") +} diff --git a/test/files/pos/t1909b-pos.scala b/test/files/run/t1909b.scala index b914bee366..89b2af57dc 100644 --- a/test/files/pos/t1909b-pos.scala +++ b/test/files/run/t1909b.scala @@ -3,4 +3,7 @@ class Ticket1909 (x: Int) { def bar() = 5 bar }) -}
\ No newline at end of file +} +object Test extends App { + new Ticket1909() +} diff --git a/test/files/run/t1909c.scala b/test/files/run/t1909c.scala new file mode 100644 index 0000000000..87c0eb08b5 --- /dev/null +++ b/test/files/run/t1909c.scala @@ -0,0 +1,9 @@ +class Base(a: Any) + +// java.lang.VerifyError: (class: Sub, method: <init> signature: ()V) Expecting to find object/array on stack +// at Test$.<init>(t1909c.scala) +class Sub() extends Base({ def bippy = 5; bippy }) + +object Test extends App { + new Sub() +} diff --git a/test/files/run/t3832.scala b/test/files/run/t3832.scala new file mode 100644 index 0000000000..ac44358bc7 --- /dev/null +++ b/test/files/run/t3832.scala @@ -0,0 +1,17 @@ +class t3832 { + def this(un: Int) = { + this() + def bippy = this + () + } + def this(un: Boolean) = { + this() + def boppy = () => this + () + } +} + +object Test extends App { + new t3832(0) + new t3832(true) +} 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/t7223.check b/test/files/run/t7223.check new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/test/files/run/t7223.check @@ -0,0 +1 @@ +0 diff --git a/test/files/run/t7223.scala b/test/files/run/t7223.scala new file mode 100644 index 0000000000..a707e957df --- /dev/null +++ b/test/files/run/t7223.scala @@ -0,0 +1,11 @@ +class D(val a: () => Int => () => Any) { + a()(0)() +} + +object Crash extends D(() => { + (x: Int) => {() => { new { println(x.toString) } }} +}) + +object Test extends App { + Crash +} diff --git a/test/files/run/t7269.scala b/test/files/run/t7269.scala new file mode 100644 index 0000000000..d22e57dfee --- /dev/null +++ b/test/files/run/t7269.scala @@ -0,0 +1,32 @@ +import scala.collection.JavaConversions._ +import scala.collection.mutable + +object Test extends App { + + def testMap(): Unit = { + val mapJ = new java.util.HashMap[Int, String] + val mapS: mutable.Map[Int, String] = mapJ + + (10 to 20).foreach(i => mapS += ((i, i.toString))) + assert(11 == mapS.size) + + // ConcurrentModificationException thrown in the following line + mapS.retain((i, str) => i % 2 == 0) + assert(6 == mapS.size) + } + + def testSet(): Unit = { + val mapJ = new java.util.HashSet[Int] + val mapS: mutable.Set[Int] = mapJ + + (10 to 20).foreach(i => mapS += i) + assert(11 == mapS.size) + + // ConcurrentModificationException thrown in the following line + mapS.retain((i) => i % 2 == 0) + assert(6 == mapS.size) + } + + testSet() + testMap() +} diff --git a/test/files/run/t7801.check b/test/files/run/t7801.check new file mode 100644 index 0000000000..d72060c684 --- /dev/null +++ b/test/files/run/t7801.check @@ -0,0 +1,11 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> val g: scala.reflect.internal.SymbolTable = null; import g.abort +g: scala.reflect.internal.SymbolTable = null +import g.abort + +scala> class C(val a: Any) extends AnyVal +defined class C + +scala> diff --git a/test/files/run/t7801.scala b/test/files/run/t7801.scala new file mode 100644 index 0000000000..3a3cc97a51 --- /dev/null +++ b/test/files/run/t7801.scala @@ -0,0 +1,12 @@ +import scala.tools.partest.ReplTest + +// was crashing due to a subtle interaction of the Namer entering packages into +// enclosing packages by mutating the scope in place without invalidating later +// entries in the enclosing package class symbols type history. +// +// Sadly, I couldn't whittle the test case down further. +object Test extends ReplTest { + override def code = """val g: scala.reflect.internal.SymbolTable = null; import g.abort + |class C(val a: Any) extends AnyVal""".stripMargin + +} 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" } diff --git a/test/instrumented/boxes.patch b/test/instrumented/boxes.patch index 6c5ff23f9f..2bb3243221 100644 --- a/test/instrumented/boxes.patch +++ b/test/instrumented/boxes.patch @@ -1,5 +1,5 @@ 9c9 -< +< --- > /* INSTRUMENTED VERSION */ 51a52,59 diff --git a/test/instrumented/library/scala/runtime/BoxesRunTime.java b/test/instrumented/library/scala/runtime/BoxesRunTime.java index 673c047dfe..57799bd9b1 100644 --- a/test/instrumented/library/scala/runtime/BoxesRunTime.java +++ b/test/instrumented/library/scala/runtime/BoxesRunTime.java @@ -244,7 +244,7 @@ public final class BoxesRunTime * as yet have not. * * Note: Among primitives, Float.NaN != Float.NaN, but the boxed - * verisons are equal. This still needs reconciliation. + * versions are equal. This still needs reconciliation. */ public static int hashFromLong(java.lang.Long n) { int iv = n.intValue(); @@ -258,6 +258,9 @@ public final class BoxesRunTime long lv = n.longValue(); if (lv == dv) return java.lang.Long.valueOf(lv).hashCode(); + + float fv = n.floatValue(); + if (fv == dv) return java.lang.Float.valueOf(fv).hashCode(); else return n.hashCode(); } public static int hashFromFloat(java.lang.Float n) { diff --git a/test/instrumented/library/scala/runtime/ScalaRunTime.scala b/test/instrumented/library/scala/runtime/ScalaRunTime.scala index d8d093e93b..e474ae737c 100644 --- a/test/instrumented/library/scala/runtime/ScalaRunTime.scala +++ b/test/instrumented/library/scala/runtime/ScalaRunTime.scala @@ -8,7 +8,8 @@ /* INSTRUMENTED VERSION */ -package scala.runtime +package scala +package runtime import scala.collection.{ Seq, IndexedSeq, TraversableView, AbstractIterator } import scala.collection.mutable.WrappedArray @@ -17,6 +18,7 @@ import scala.collection.generic.{ Sorted } import scala.reflect.{ ClassTag, classTag } import scala.util.control.ControlThrowable import scala.xml.{ Node, MetaData } +import java.lang.{ Class => jClass } import java.lang.Double.doubleToLongBits import java.lang.reflect.{ Modifier, Method => JMethod } @@ -30,29 +32,21 @@ object ScalaRunTime { def isArray(x: Any, atLevel: Int): Boolean = x != null && isArrayClass(x.getClass, atLevel) - private def isArrayClass(clazz: Class[_], atLevel: Int): Boolean = + private def isArrayClass(clazz: jClass[_], atLevel: Int): Boolean = clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) - def isValueClass(clazz: Class[_]) = clazz.isPrimitive() - def isTuple(x: Any) = x != null && tupleNames(x.getClass.getName) + def isValueClass(clazz: jClass[_]) = clazz.isPrimitive() + + // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) + def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") def isAnyVal(x: Any) = x match { case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true case _ => false } - // Avoiding boxing which messes up the specialized tests. Don't ask. - private val tupleNames = { - var i = 22 - var names: List[String] = Nil - while (i >= 1) { - names ::= ("scala.Tuple" + String.valueOf(i)) - i -= 1 - } - names.toSet - } /** Return the class object representing an array with element class `clazz`. */ - def arrayClass(clazz: Class[_]): Class[_] = { + def arrayClass(clazz: jClass[_]): jClass[_] = { // newInstance throws an exception if the erasure is Void.TYPE. see SI-5680 if (clazz == java.lang.Void.TYPE) classOf[Array[Unit]] else java.lang.reflect.Array.newInstance(clazz, 0).getClass @@ -60,18 +54,19 @@ object ScalaRunTime { /** Return the class object representing elements in arrays described by a given schematic. */ - def arrayElementClass(schematic: Any): Class[_] = schematic match { - case cls: Class[_] => cls.getComponentType + def arrayElementClass(schematic: Any): jClass[_] = schematic match { + case cls: jClass[_] => cls.getComponentType case tag: ClassTag[_] => tag.runtimeClass - case _ => throw new UnsupportedOperationException("unsupported schematic %s (%s)".format(schematic, if (schematic == null) "null" else schematic.getClass)) + case _ => + throw new UnsupportedOperationException(s"unsupported schematic $schematic (${schematic.getClass})") } /** Return the class object representing an unboxed value type, * e.g. classOf[int], not classOf[java.lang.Integer]. The compiler * rewrites expressions like 5.getClass to come here. */ - def anyValClass[T <: AnyVal : ClassTag](value: T): Class[T] = - classTag[T].runtimeClass.asInstanceOf[Class[T]] + def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] = + classTag[T].runtimeClass.asInstanceOf[jClass[T]] var arrayApplyCount = 0 @@ -93,11 +88,9 @@ object ScalaRunTime { } } - var arrayUpdateCount = 0 - /** update generic array element */ def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { - arrayUpdateCount += 1 + arrayApplyCount += 1 xs match { case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] case x: Array[Int] => x(idx) = value.asInstanceOf[Int] @@ -156,7 +149,7 @@ object ScalaRunTime { dest } - def toArray[T](xs: collection.Seq[T]) = { + def toArray[T](xs: scala.collection.Seq[T]) = { val arr = new Array[AnyRef](xs.length) var i = 0 for (x <- xs) { @@ -179,39 +172,10 @@ object ScalaRunTime { def checkInitialized[T <: AnyRef](x: T): T = if (x == null) throw new UninitializedError else x - abstract class Try[+A] { - def Catch[B >: A](handler: PartialFunction[Throwable, B]): B - def Finally(fin: => Unit): A - } - - def Try[A](block: => A): Try[A] = new Try[A] with Runnable { - private var result: A = _ - private var exception: Throwable = - try { run() ; null } - catch { - case e: ControlThrowable => throw e // don't catch non-local returns etc - case e: Throwable => e - } - - def run() { result = block } - - def Catch[B >: A](handler: PartialFunction[Throwable, B]): B = - if (exception == null) result - else if (handler isDefinedAt exception) handler(exception) - else throw exception - - def Finally(fin: => Unit): A = { - fin - - if (exception == null) result - else throw exception - } - } - def _toString(x: Product): String = x.productIterator.mkString(x.productPrefix + "(", ",", ")") - def _hashCode(x: Product): Int = scala.util.MurmurHash3.productHash(x) + def _hashCode(x: Product): Int = scala.util.hashing.MurmurHash3.productHash(x) /** A helper for case classes. */ def typedProductIterator[T](x: Product): Iterator[T] = { @@ -246,12 +210,12 @@ object ScalaRunTime { // Note that these are the implementations called by ##, so they // must not call ## themselves. - @inline def hash(x: Any): Int = + def hash(x: Any): Int = if (x == null) 0 else if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.hashFromNumber(x.asInstanceOf[java.lang.Number]) else x.hashCode - @inline def hash(dv: Double): Int = { + def hash(dv: Double): Int = { val iv = dv.toInt if (iv == dv) return iv @@ -261,7 +225,7 @@ object ScalaRunTime { val fv = dv.toFloat if (fv == dv) fv.hashCode else dv.hashCode } - @inline def hash(fv: Float): Int = { + def hash(fv: Float): Int = { val iv = fv.toInt if (iv == fv) return iv @@ -269,29 +233,29 @@ object ScalaRunTime { if (lv == fv) return hash(lv) else fv.hashCode } - @inline def hash(lv: Long): Int = { + def hash(lv: Long): Int = { val low = lv.toInt val lowSign = low >>> 31 val high = (lv >>> 32).toInt low ^ (high + lowSign) } - @inline def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x) + def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x) // The remaining overloads are here for completeness, but the compiler // inlines these definitions directly so they're not generally used. - @inline def hash(x: Int): Int = x - @inline def hash(x: Short): Int = x.toInt - @inline def hash(x: Byte): Int = x.toInt - @inline def hash(x: Char): Int = x.toInt - @inline def hash(x: Boolean): Int = if (x) true.hashCode else false.hashCode - @inline def hash(x: Unit): Int = 0 + def hash(x: Int): Int = x + def hash(x: Short): Int = x.toInt + def hash(x: Byte): Int = x.toInt + def hash(x: Char): Int = x.toInt + def hash(x: Boolean): Int = if (x) true.hashCode else false.hashCode + def hash(x: Unit): Int = 0 /** A helper method for constructing case class equality methods, * because existential types get in the way of a clean outcome and * it's performing a series of Any/Any equals comparisons anyway. * See ticket #2867 for specifics. */ - def sameElements(xs1: collection.Seq[Any], xs2: collection.Seq[Any]) = xs1 sameElements xs2 + def sameElements(xs1: scala.collection.Seq[Any], xs2: scala.collection.Seq[Any]) = xs1 sameElements xs2 /** Given any Scala value, convert it to a String. * @@ -358,7 +322,7 @@ object ScalaRunTime { case x: String => if (x.head.isWhitespace || x.last.isWhitespace) "\"" + x + "\"" else x case x if useOwnToString(x) => x.toString case x: AnyRef if isArray(x) => arrayToString(x) - case x: collection.Map[_, _] => x.iterator take maxElements map mapInner mkString (x.stringPrefix + "(", ", ", ")") + case x: scala.collection.Map[_, _] => x.iterator take maxElements map mapInner mkString (x.stringPrefix + "(", ", ", ")") case x: Iterable[_] => x.iterator take maxElements map inner mkString (x.stringPrefix + "(", ", ", ")") case x: Traversable[_] => x take maxElements map inner mkString (x.stringPrefix + "(", ", ", ")") case x: Product1[_] if isTuple(x) => "(" + inner(x._1) + ",)" // that special trailing comma diff --git a/test/instrumented/srt.patch b/test/instrumented/srt.patch index 47dcfa2197..ee619b2ecb 100644 --- a/test/instrumented/srt.patch +++ b/test/instrumented/srt.patch @@ -1,67 +1,10 @@ 8a9,10 > /* INSTRUMENTED VERSION */ > -73a76,77 +68a71,72 > var arrayApplyCount = 0 > -75,86c79,93 -< def array_apply(xs: AnyRef, idx: Int): Any = xs match { -< case x: Array[AnyRef] => x(idx).asInstanceOf[Any] -< case x: Array[Int] => x(idx).asInstanceOf[Any] -< case x: Array[Double] => x(idx).asInstanceOf[Any] -< case x: Array[Long] => x(idx).asInstanceOf[Any] -< case x: Array[Float] => x(idx).asInstanceOf[Any] -< case x: Array[Char] => x(idx).asInstanceOf[Any] -< case x: Array[Byte] => x(idx).asInstanceOf[Any] -< case x: Array[Short] => x(idx).asInstanceOf[Any] -< case x: Array[Boolean] => x(idx).asInstanceOf[Any] -< case x: Array[Unit] => x(idx).asInstanceOf[Any] -< case null => throw new NullPointerException ---- -> def array_apply(xs: AnyRef, idx: Int): Any = { +70a75 +> arrayApplyCount += 1 +87a93 > arrayApplyCount += 1 -> xs match { -> case x: Array[AnyRef] => x(idx).asInstanceOf[Any] -> case x: Array[Int] => x(idx).asInstanceOf[Any] -> case x: Array[Double] => x(idx).asInstanceOf[Any] -> case x: Array[Long] => x(idx).asInstanceOf[Any] -> case x: Array[Float] => x(idx).asInstanceOf[Any] -> case x: Array[Char] => x(idx).asInstanceOf[Any] -> case x: Array[Byte] => x(idx).asInstanceOf[Any] -> case x: Array[Short] => x(idx).asInstanceOf[Any] -> case x: Array[Boolean] => x(idx).asInstanceOf[Any] -> case x: Array[Unit] => x(idx).asInstanceOf[Any] -> case null => throw new NullPointerException -> } -88a96,97 -> var arrayUpdateCount = 0 -> -90,101c99,113 -< def array_update(xs: AnyRef, idx: Int, value: Any): Unit = xs match { -< case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] -< case x: Array[Int] => x(idx) = value.asInstanceOf[Int] -< case x: Array[Double] => x(idx) = value.asInstanceOf[Double] -< case x: Array[Long] => x(idx) = value.asInstanceOf[Long] -< case x: Array[Float] => x(idx) = value.asInstanceOf[Float] -< case x: Array[Char] => x(idx) = value.asInstanceOf[Char] -< case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] -< case x: Array[Short] => x(idx) = value.asInstanceOf[Short] -< case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] -< case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] -< case null => throw new NullPointerException ---- -> def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { -> arrayUpdateCount += 1 -> xs match { -> case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] -> case x: Array[Int] => x(idx) = value.asInstanceOf[Int] -> case x: Array[Double] => x(idx) = value.asInstanceOf[Double] -> case x: Array[Long] => x(idx) = value.asInstanceOf[Long] -> case x: Array[Float] => x(idx) = value.asInstanceOf[Float] -> case x: Array[Char] => x(idx) = value.asInstanceOf[Char] -> case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] -> case x: Array[Short] => x(idx) = value.asInstanceOf[Short] -> case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] -> case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] -> case null => throw new NullPointerException -> } diff --git a/test/junit/scala/runtime/ScalaRunTimeTest.scala b/test/junit/scala/runtime/ScalaRunTimeTest.scala new file mode 100644 index 0000000000..9da197c71a --- /dev/null +++ b/test/junit/scala/runtime/ScalaRunTimeTest.scala @@ -0,0 +1,70 @@ +package scala.runtime + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** Tests for the private class DefaultPromise */ +@RunWith(classOf[JUnit4]) +class ScalaRunTimeTest { + @Test + def testIsTuple() { + import ScalaRunTime.isTuple + def check(v: Any) = { + assertTrue(v.toString, isTuple(v)) + } + + val s = "" + check(Tuple1(s)) + check((s, s)) + check((s, s, s)) + check((s, s, s, s)) + check((s, s, s, s, s)) + check((s, s, s, s, s, s)) + check((s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + + // some specialized variants will have mangled classnames + check(Tuple1(0)) + check((0, 0)) + check((0, 0, 0)) + check((0, 0, 0, 0)) + check((0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + + case class C() + val c = new C() + assertFalse(c.toString, isTuple(c)) + } +} diff --git a/test/scaladoc/run/t7767.check b/test/scaladoc/run/t7767.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/t7767.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/t7767.scala b/test/scaladoc/run/t7767.scala new file mode 100644 index 0000000000..6c9ceb511d --- /dev/null +++ b/test/scaladoc/run/t7767.scala @@ -0,0 +1,18 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + class Docable extends { /**Doc*/ val foo = 0 } with AnyRef + """ + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + import access._ + val comment = rootPackage._class("Docable")._value("foo").comment.map(_.body.toString.trim).getOrElse("") + assert(comment.contains("Doc"), comment) + } +} |