summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/cmd/CommandLine.scala50
-rw-r--r--src/compiler/scala/tools/cmd/Demo.scala7
-rw-r--r--src/compiler/scala/tools/cmd/Instance.scala3
-rw-r--r--src/compiler/scala/tools/cmd/Interpolation.scala6
-rw-r--r--src/compiler/scala/tools/cmd/Meta.scala21
-rw-r--r--src/compiler/scala/tools/cmd/Opt.scala8
-rw-r--r--src/compiler/scala/tools/cmd/Reference.scala3
-rw-r--r--src/compiler/scala/tools/cmd/Spec.scala11
-rw-r--r--src/compiler/scala/tools/cmd/program/Scmp.scala59
-rw-r--r--src/compiler/scala/tools/cmd/program/Simple.scala81
-rw-r--r--src/compiler/scala/tools/cmd/program/Tokens.scala100
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala37
-rw-r--r--src/compiler/scala/tools/nsc/Interpreter.scala221
-rw-r--r--src/compiler/scala/tools/nsc/InterpreterLoop.scala61
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeInfo.scala44
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala5
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala3
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala38
-rw-r--r--src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala46
-rw-r--r--src/compiler/scala/tools/nsc/doc/DocFactory.scala2
-rw-r--r--src/compiler/scala/tools/nsc/doc/Settings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala6
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/Index.scala45
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/Template.scala7
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css11
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css9
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala8
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ByteCode.scala7
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Completion.scala334
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala33
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala88
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala25
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/LiteralCompletion.scala50
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala187
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Parsed.scala14
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala18
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala1
-rw-r--r--src/compiler/scala/tools/nsc/io/Process.scala22
-rw-r--r--src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala8
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala18
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala8
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala21
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala15
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala13
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala107
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala223
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala144
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Analyzer.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala41
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala16
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala49
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala137
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala58
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala53
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala21
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala268
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala305
-rwxr-xr-xsrc/compiler/scala/tools/nsc/util/Chars.scala11
-rw-r--r--src/compiler/scala/tools/nsc/util/ShowPickled.scala17
-rw-r--r--src/compiler/scala/tools/nsc/util/SourceFile.scala2
68 files changed, 1896 insertions, 1337 deletions
diff --git a/src/compiler/scala/tools/cmd/CommandLine.scala b/src/compiler/scala/tools/cmd/CommandLine.scala
index 8cb4c00b14..9b8bef4a9a 100644
--- a/src/compiler/scala/tools/cmd/CommandLine.scala
+++ b/src/compiler/scala/tools/cmd/CommandLine.scala
@@ -8,18 +8,19 @@ package cmd
import scala.collection.mutable.ListBuffer
+trait CommandLineConfig {
+ def enforceArity: Boolean = true
+ def onlyKnownOptions: Boolean = true
+}
+
/** An instance of a command line, parsed according to a Spec.
*/
-class CommandLine(val spec: Reference, val originalArgs: List[String]) {
+class CommandLine(val spec: Reference, val originalArgs: List[String]) extends CommandLineConfig {
def this(spec: Reference, line: String) = this(spec, Parser tokenize line)
def this(spec: Reference, args: Array[String]) = this(spec, args.toList)
import spec.{ isAnyOption, isUnaryOption, isBinaryOption, isExpandOption }
- def assumeBinary = true
- def enforceArity = true
- def onlyKnownOptions = false
-
val Terminator = "--"
val ValueForUnaryOption = "true" // so if --opt is given, x(--opt) = true
@@ -32,13 +33,6 @@ class CommandLine(val spec: Reference, val originalArgs: List[String]) {
lazy val (argMap, residualArgs) = {
val residualBuffer = new ListBuffer[String]
- def isOption(s: String) = isAnyOption(s) || ((s startsWith "-") && !onlyKnownOptions)
-
- def unknownOption(opt: String) =
- errorFn("Option '%s' not recognized.".format(opt))
- def missingArg(opt: String, what: String) =
- errorFn("Option '%s' requires argument, found %s instead.".format(opt, what))
-
def loop(args: List[String]): Map[String, String] = {
def residual(xs: List[String]) = { residualBuffer ++= xs ; Map[String, String]() }
@@ -54,22 +48,32 @@ class CommandLine(val spec: Reference, val originalArgs: List[String]) {
else None
}
+ /** Assumes known options have all been ruled out already. */
+ def isUnknown(opt: String) =
+ onlyKnownOptions && (opt startsWith "-") && {
+ errorFn("Option '%s' not recognized.".format(opt))
+ true
+ }
+
args match {
case Nil => Map()
case Terminator :: xs => residual(xs)
case x :: Nil =>
expand(x) foreach (exp => return loop(exp))
if (isBinaryOption(x) && enforceArity)
- missingArg(x, "EOF")
+ errorFn("Option '%s' requires argument, found EOF instead.".format(x))
if (isUnaryOption(x)) mapForUnary(x)
+ else if (isUnknown(x)) Map()
else residual(args)
+
case x1 :: x2 :: xs =>
expand(x1) foreach (exp => return loop(exp ++ args.tail))
if (x2 == Terminator) mapForUnary(x1) ++ residual(xs)
else if (isUnaryOption(x1)) mapForUnary(x1) ++ loop(args.tail)
else if (isBinaryOption(x1)) Map(x1 -> x2) ++ loop(xs)
+ else if (isUnknown(x1)) loop(args.tail)
else residual(List(x1)) ++ loop(args.tail)
}
}
@@ -85,23 +89,3 @@ class CommandLine(val spec: Reference, val originalArgs: List[String]) {
override def toString() = argMap.toString + " " + residualArgs.toString
}
-
-object CommandLine {
- def apply(args: List[String], unary: List[String], binary: List[String]) = {
- /** XXX Temporarily assembling a fake spec so we can continue to
- * do ad-hoc parsing based on a list of unary and binary args.
- * Should either clean this up or do it differently.
- */
- object NoSpec extends Reference {
- unary foreach (_ --? )
- binary foreach (_ --| )
-
- protected def creator(args: List[String]) = error("No Spec")
- def programInfo = Spec.Names("", "")
- lazy val referenceSpec = this
- }
-
- new CommandLine(NoSpec, args)
- }
-}
-
diff --git a/src/compiler/scala/tools/cmd/Demo.scala b/src/compiler/scala/tools/cmd/Demo.scala
index 7014e6b4d7..22cf50bd58 100644
--- a/src/compiler/scala/tools/cmd/Demo.scala
+++ b/src/compiler/scala/tools/cmd/Demo.scala
@@ -10,7 +10,7 @@ package cmd
* First take advantage of the meta-options:
*
* // this command creates an executable runner script "demo"
- * % scala scala.tools.cmd.Demo --generate-runner demo
+ * % scala scala.tools.cmd.Demo --self-update demo
*
* // this one creates and sources a completion file - note backticks
* % `./demo --bash`
@@ -19,13 +19,13 @@ package cmd
* % ./demo --<tab>
* --action --defint --int
* --bash --defstr --str
- * --defenv --generate-runner --unary
+ * --defenv --self-update --unary
*
* The normal option configuration is plausibly self-explanatory.
*/
trait DemoSpec extends Spec with Meta.StdOpts with Interpolation {
lazy val referenceSpec = DemoSpec
- lazy val programInfo = Spec.Names("demo", "scala.tools.cmd.Demo")
+ lazy val programInfo = Spec.Info("demo", "Usage: demo [<options>]", "scala.tools.cmd.Demo")
help("""Usage: demo [<options>]""")
heading("Unary options:")
@@ -48,7 +48,6 @@ object DemoSpec extends DemoSpec with Property {
type ThisCommandLine = SpecCommandLine
def creator(args: List[String]) =
new SpecCommandLine(args) {
- override def onlyKnownOptions = true
override def errorFn(msg: String) = { println("Error: " + msg) ; System.exit(0) }
}
}
diff --git a/src/compiler/scala/tools/cmd/Instance.scala b/src/compiler/scala/tools/cmd/Instance.scala
index 4d319b98cc..3c0dbbaa1f 100644
--- a/src/compiler/scala/tools/cmd/Instance.scala
+++ b/src/compiler/scala/tools/cmd/Instance.scala
@@ -16,7 +16,8 @@ trait Instance extends Spec {
protected def help(str: => String): Unit = ()
def isSet(s: String) = parsed isSet toOpt(s)
- def originalArgs = parsed.originalArgs
+ def originalArgs = parsed.originalArgs // the full original list
+ def residualArgs = parsed.residualArgs // only args which were not options or args to options
type OptionMagic = Opt.Instance
protected implicit def optionMagicAdditions(name: String) = new Opt.Instance(programInfo, parsed, name)
diff --git a/src/compiler/scala/tools/cmd/Interpolation.scala b/src/compiler/scala/tools/cmd/Interpolation.scala
index 6b86a35bb9..a326d48f64 100644
--- a/src/compiler/scala/tools/cmd/Interpolation.scala
+++ b/src/compiler/scala/tools/cmd/Interpolation.scala
@@ -52,8 +52,6 @@ object Interpolation {
|#!/bin/sh
|#
|
- |scala @@MAINCLASS@@ $*
- |
- """.stripMargin
+ |scala @@MAINCLASS@@ "$@"
+ |""".stripMargin.trim + "\n"
}
-
diff --git a/src/compiler/scala/tools/cmd/Meta.scala b/src/compiler/scala/tools/cmd/Meta.scala
index 5a09766b13..8609db3d50 100644
--- a/src/compiler/scala/tools/cmd/Meta.scala
+++ b/src/compiler/scala/tools/cmd/Meta.scala
@@ -22,11 +22,11 @@ object Meta {
trait StdOpts {
self: Spec with Interpolation =>
- Bash.name --> runAndExit(Bash.action())
- val runnerFileName = Runner.name --| ;
+ Bash.name --> runAndExit(Bash.action())
+ val selfUpdateName = SelfUpdate.name --| ;
- if (runnerFileName.isDefined)
- runAndExit(Runner.action())
+ if (selfUpdateName.isDefined)
+ runAndExit(SelfUpdate.action())
/** I think we're as close as we can get to bundling completion with
* the program given the constraints imposed by bash. This outputs
@@ -47,12 +47,17 @@ object Meta {
}
}
- /** A very basic runner script.
+ /** Generates a very basic runner script. It's called SelfUpdate
+ * because once it exists you can do something like
+ *
+ * tools/scmp --self-update tools/scmp
+ *
+ * and it will overwrite itself with the current version.
*/
- object Runner extends Opt {
- val name = "generate-runner"
+ object SelfUpdate extends Opt {
+ val name = "self-update"
val action = () => {
- val file = File(runnerFileName.get)
+ val file = File(selfUpdateName.get)
file writeAll interpolate(runnerTemplate)
file setExecutable true
()
diff --git a/src/compiler/scala/tools/cmd/Opt.scala b/src/compiler/scala/tools/cmd/Opt.scala
index 9e3c324deb..beea590492 100644
--- a/src/compiler/scala/tools/cmd/Opt.scala
+++ b/src/compiler/scala/tools/cmd/Opt.scala
@@ -7,7 +7,7 @@ package scala.tools
package cmd
import nsc.Properties.envOrElse
-import Spec.Names
+import Spec.Info
/** Machinery for what amounts to a command line specification DSL.
* It is designed so the same specification trait can be used for
@@ -25,7 +25,7 @@ object Opt {
trait Implicit {
def name: String
- def programInfo: Names
+ def programInfo: Info
protected def opt = toOpt(name)
def --? : Boolean // --opt is set
@@ -47,7 +47,7 @@ object Opt {
def /(descr: String): String // --opt has help description 'descr'
}
- class Reference(val programInfo: Names, val options: Reference.Accumulators, val name: String) extends Implicit {
+ class Reference(val programInfo: Info, val options: Reference.Accumulators, val name: String) extends Implicit {
import options._
def --? = { addUnary(opt) ; false }
@@ -63,7 +63,7 @@ object Opt {
def /(descr: String) = returning(name)(_ => addHelp(() => helpFormatStr.format(opt, descr)))
}
- class Instance(val programInfo: Names, val parsed: CommandLine, val name: String) extends Implicit with Error {
+ class Instance(val programInfo: Info, val parsed: CommandLine, val name: String) extends Implicit with Error {
def --? = parsed isSet opt
def --> (body: => Unit) = if (parsed isSet opt) body
def --| = parsed get opt
diff --git a/src/compiler/scala/tools/cmd/Reference.scala b/src/compiler/scala/tools/cmd/Reference.scala
index 695868191b..3f3712766b 100644
--- a/src/compiler/scala/tools/cmd/Reference.scala
+++ b/src/compiler/scala/tools/cmd/Reference.scala
@@ -32,7 +32,8 @@ trait Reference extends Spec {
protected def help(str: => String) = addHelp(() => str)
- type ThisCommandLine <: SpecCommandLine
+ type ThisCommandLine <: CommandLine
+
class SpecCommandLine(args: List[String]) extends CommandLine(Reference.this, args) { }
protected def creator(args: List[String]): ThisCommandLine
final def apply(args: String*): ThisCommandLine = creator(propertyArgs ++ args flatMap expandArg)
diff --git a/src/compiler/scala/tools/cmd/Spec.scala b/src/compiler/scala/tools/cmd/Spec.scala
index c8283165d9..794bb3303f 100644
--- a/src/compiler/scala/tools/cmd/Spec.scala
+++ b/src/compiler/scala/tools/cmd/Spec.scala
@@ -12,7 +12,7 @@ package cmd
*/
trait Spec {
def referenceSpec: Reference
- def programInfo: Spec.Names
+ def programInfo: Spec.Info
protected def help(str: => String): Unit
protected def heading(str: => String): Unit = help("\n " + str)
@@ -22,7 +22,14 @@ trait Spec {
}
object Spec {
- case class Names(runner: String, mainClass: String) { }
+ class Info(
+ val runner: String,
+ val usage: String,
+ val mainClass: String
+ )
+ object Info {
+ def apply(runner: String, help: String, mainClass: String): Info = new Info(runner, help, mainClass)
+ }
class Accumulator[T: FromString]() {
private var _buf: List[T] = Nil
diff --git a/src/compiler/scala/tools/cmd/program/Scmp.scala b/src/compiler/scala/tools/cmd/program/Scmp.scala
new file mode 100644
index 0000000000..ff4fa11eaf
--- /dev/null
+++ b/src/compiler/scala/tools/cmd/program/Scmp.scala
@@ -0,0 +1,59 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package cmd
+package program
+
+import nsc.io._
+
+object Scmp {
+ private val scmpUsage = """
+ |Usage: scmp [options] <cmd line>
+ |Example: scmp --p1 '-no-specialization -Ydebug' scalac src/library/scala/Function1.scala
+ |
+ |Note: the command line must start with a path to scalac.
+ |""".stripMargin
+ private val scmpOptions = List(
+ "p1" -> "options for the first run only",
+ "p2" -> "options for the second run only"
+ )
+ private val scmpInfo = Simple.scalaProgramInfo("scmp", scmpUsage)
+ lazy val ScmpSpec = Simple(scmpInfo, Nil, scmpOptions, x => returning(x)(_.onlyKnownOptions = false))
+
+ def main(args0: Array[String]): Unit = {
+ if (args0.isEmpty)
+ return println(scmpUsage)
+
+ val runner = ScmpSpec instance args0
+ import runner._
+
+ val p1args = parsed.getOrElse("--p1", "")
+ val p2args = parsed.getOrElse("--p2", "")
+
+ if (p1args.isEmpty && p2args.isEmpty)
+ return println("At least one of --p1 and --p2 must be given.")
+ if (residualArgs.isEmpty)
+ return println("There is no command to run.")
+
+ def createCmd(extras: String) =
+ fromArgs(residualArgs.patch(1, toArgs(extras), 0))
+
+ def runCmd(cmd: String) = {
+ val output = Process(cmd, redirect = true).slurp()
+
+ returning(File.makeTemp())(_ writeAll output)
+ }
+
+ val cmds = List(p1args, p2args) map createCmd
+ println(cmds.mkString("Running command lines:\n ", "\n ", ""))
+
+ val files = cmds map runCmd map (_.path)
+ val diff = Process("diff %s %s".format(files: _*)).slurp()
+
+ if (diff.isEmpty) println("No differences.")
+ else println(diff)
+ }
+}
diff --git a/src/compiler/scala/tools/cmd/program/Simple.scala b/src/compiler/scala/tools/cmd/program/Simple.scala
new file mode 100644
index 0000000000..641be31c9e
--- /dev/null
+++ b/src/compiler/scala/tools/cmd/program/Simple.scala
@@ -0,0 +1,81 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package cmd
+package program
+
+import Spec.Info
+
+/** A boilerplate reducer for commands with simple requirements. For examples,
+ * see Scmp and Tokens in this package.
+ */
+object Simple {
+ type CommandLineTransform = SimpleCommandLine => SimpleCommandLine
+
+ abstract class SimpleSpec(val programInfo: Info) extends Spec with Meta.StdOpts with Interpolation
+
+ trait SimpleInstance extends SimpleSpec with Instance {
+ val parsed: CommandLine
+ }
+
+ class SimpleReference(
+ programInfo: Info,
+ unary: List[(String, String)] = Nil,
+ binary: List[(String, String)] = Nil,
+ postCreation: CommandLineTransform = null
+ ) extends SimpleSpec(programInfo) with Reference {
+
+ spec =>
+
+ if (programInfo.usage != "") help(programInfo.usage)
+ unary foreach { case (option, help) => option / help --? }
+ binary foreach { case (option, help) => option / help --| }
+
+ type ThisCommandLine = SimpleCommandLine
+
+ def creator(args: List[String]) = new SimpleCommandLine(spec, args)
+ def instance(args: Array[String]): SimpleInstance = instance(args.toList)
+ def instance(args: List[String]): SimpleInstance =
+ new {
+ val parsed = spec(args: _*)
+ } with SimpleSpec(programInfo) with SimpleInstance {
+ lazy val referenceSpec = spec
+ }
+
+ lazy val referenceSpec = spec
+ }
+
+ def apply(info: Info, unary: List[(String, String)], binary: List[(String, String)], postCreation: CommandLineTransform): SimpleReference = {
+ new SimpleReference(info, unary, binary, postCreation) {
+ override def creator(args: List[String]) = {
+ val obj = super.creator(args)
+ if (postCreation == null) obj
+ else postCreation(obj)
+ }
+ }
+ }
+
+ def scalaProgramInfo(name: String, help: String) =
+ Spec.Info(name, help, "scala.tools.cmd.program." + name.capitalize)
+
+ /** You can't override a def with a var unless a setter exists. We cleverly
+ * sidestep this by mixing in a trait with dummy setters which will be
+ * inaccessible due to the overriding var.
+ */
+ trait Ticket2338WontFixWorkaround {
+ def enforceArity_=(x: Boolean): Unit = error("unreachable")
+ def onlyKnownOptions_=(x: Boolean): Unit = error("unreachable")
+ }
+
+ /** Configurability simplicity achieved by turning defs into vars and letting
+ * the spec creator apply a transformation. This way there's no need to create
+ * custom subclasses of CommandLine.
+ */
+ class SimpleCommandLine(spec: Reference, args: List[String]) extends CommandLine(spec, args) with Ticket2338WontFixWorkaround {
+ override var enforceArity: Boolean = true
+ override var onlyKnownOptions: Boolean = true
+ }
+}
diff --git a/src/compiler/scala/tools/cmd/program/Tokens.scala b/src/compiler/scala/tools/cmd/program/Tokens.scala
new file mode 100644
index 0000000000..36786aa2b7
--- /dev/null
+++ b/src/compiler/scala/tools/cmd/program/Tokens.scala
@@ -0,0 +1,100 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package cmd
+package program
+
+import nsc._
+import util.Chars.char2uescape
+import io._
+import ast.parser.Tokens._
+
+/** Given paths on the command line, tokenizes any scala files found
+ * and prints one token per line.
+ */
+object Tokens {
+ private val tokensUsage = "Usage: tokens [options] <path1 path2 ...>\n\nOptions:"
+ private val tokensUnary = List(
+ "verbose" -> "be more verbose",
+ "freq" -> "combine token lists and sort by frequency",
+ "stats" -> "output some stats"
+ )
+ private val tokensBinary = List(
+ "sliding" -> "print tokens in groups of given size"
+ )
+ private val tokensInfo = Simple.scalaProgramInfo("tokens", tokensUsage)
+ private lazy val TokensSpec = Simple(tokensInfo, tokensUnary, tokensBinary, null)
+
+ def sanitize(x: Any): String = sanitize(x.toString)
+ def sanitize(str: String): String = str flatMap (x => if (x.isControl) char2uescape(x) else x.toString)
+
+ def main(args0: Array[String]): Unit = {
+ if (args0.isEmpty)
+ return println(TokensSpec.helpMsg)
+
+ val runner = TokensSpec instance args0
+ import runner._
+
+ val files = (residualArgs flatMap walk).distinct
+ if (parsed isSet "--verbose")
+ println("Tokenizing: " + (files map (_.name) mkString " "))
+
+ if (parsed isSet "--stats")
+ println("Stats not yet implemented.")
+
+ def raw = files flatMap fromScalaSource
+ def tokens: List[Any] =
+ if (parsed isSet "--sliding") raw sliding parsed("--sliding").toInt map (_ map sanitize mkString " ") toList
+ else raw
+
+ def output =
+ if (parsed isSet "--freq")
+ (tokens groupBy (x => x) mapValues (_.length)).toList sortBy (-_._2) map (x => x._2 + " " + x._1)
+ else
+ tokens
+
+ output foreach println
+ }
+
+ def fromPaths(paths: String*): List[Any] =
+ (paths.toList flatMap walk).distinct flatMap fromScalaSource
+
+ /** Given a path, returns all .scala files underneath it.
+ */
+ private def walk(arg: String): List[File] = {
+ def traverse = Path(arg) ifDirectory (_.deepList()) getOrElse Iterator(File(arg))
+
+ Path onlyFiles traverse filter (_ hasExtension "scala") toList
+ }
+
+ /** Tokenizes a single scala file.
+ */
+ def fromScalaSource(file: Path): List[Any] = fromScalaSource(file.path)
+ def fromScalaSource(file: String): List[Any] = {
+ val global = new Global(new Settings())
+ import global._
+ import syntaxAnalyzer.{ UnitScanner, token2string }
+
+ val in = new UnitScanner(new CompilationUnit(getSourceFile(file)))
+ in.init()
+
+ Iterator continually {
+ val token = in.token match {
+ case IDENTIFIER | BACKQUOTED_IDENT => in.name
+ case CHARLIT | INTLIT | LONGLIT => in.intVal
+ case DOUBLELIT | FLOATLIT => in.floatVal
+ case STRINGLIT => "\"" + in.strVal + "\""
+ case SEMI | NEWLINE => ";"
+ case NEWLINES => ";;"
+ case COMMA => ","
+ case EOF => null
+ case x => token2string(x)
+ }
+ in.nextToken()
+ token
+ } takeWhile (_ != null) toList
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 16bff5d399..0ec36cf9ab 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -137,7 +137,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
def error(msg: String) = reporter.error(NoPosition, msg)
def warning(msg: String) =
- if (settings.Ywarnfatal.value) reporter.error(NoPosition, msg)
+ if (settings.Xwarnfatal.value) reporter.error(NoPosition, msg)
else reporter.warning(NoPosition, msg)
def inform(msg: String) = reporter.info(NoPosition, msg, true)
def inform[T](msg: String, value: T): T = { inform(msg+value); value }
@@ -714,7 +714,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
/** Compile list of source files */
def compileSources(_sources: List[SourceFile]) {
val depSources = dependencyAnalysis.filter(_sources.distinct) // bug #1268, scalac confused by duplicated filenames
- val sources = scalaObjectFirst(depSources)
+ val sources = coreClassesFirst(depSources)
if (reporter.hasErrors)
return // there is a problem already, e.g. a
// plugin was passed a bad option
@@ -871,14 +871,43 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
if (!pclazz.isRoot) resetPackageClass(pclazz.owner)
}
- private def scalaObjectFirst(files: List[SourceFile]) = {
+ /**
+ * Re-orders the source files to
+ * 1. ScalaObject
+ * 2. LowPriorityImplicits / StandardEmbeddings (i.e. parents of Predef)
+ * 3. the rest
+ *
+ * 1 is to avoid cyclic reference errors.
+ * 2 is due to the following. When completing "Predef" (*), typedIdent is called
+ * for its parents (e.g. "LowPriorityImplicits"). typedIdent checks wethter
+ * the symbol reallyExists, which tests if the type of the symbol after running
+ * its completer is != NoType.
+ * If the "namer" phase has not yet run for "LowPriorityImplicits", the symbol
+ * has a SourcefileLoader as type. Calling "doComplete" on it does nothing at
+ * all, because the source file is part of the files to be compiled anyway.
+ * So the "reallyExists" test will return "false".
+ * Only after the namer, the symbol has a lazy type which actually computes
+ * the info, and "reallyExists" behaves as expected.
+ * So we need to make sure that the "namer" phase is run on predef's parents
+ * before running it on predef.
+ *
+ * (*) Predef is completed early when calling "mkAttributedRef" during the
+ * addition of "import Predef._" to sourcefiles. So this situation can't
+ * happen for user classes.
+ *
+ */
+ private def coreClassesFirst(files: List[SourceFile]) = {
def inScalaFolder(f: SourceFile) =
f.file.container.name == "scala"
+ var scalaObject: Option[SourceFile] = None
val res = new ListBuffer[SourceFile]
for (file <- files) file.file.name match {
- case "ScalaObject.scala" if inScalaFolder(file) => file +=: res
+ case "ScalaObject.scala" if inScalaFolder(file) => scalaObject = Some(file)
+ case "LowPriorityImplicits.scala" if inScalaFolder(file) => file +=: res
+ case "StandardEmbeddings.scala" if inScalaFolder(file) => file +=: res
case _ => res += file
}
+ for (so <- scalaObject) so +=: res
res.toList
}
} // class Run
diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala
index 0cc0f65640..556a3107cd 100644
--- a/src/compiler/scala/tools/nsc/Interpreter.scala
+++ b/src/compiler/scala/tools/nsc/Interpreter.scala
@@ -27,7 +27,7 @@ import scala.util.control.Exception.{ Catcher, catching, ultimately, unwrapping
import io.{ PlainFile, VirtualDirectory }
import reporters.{ ConsoleReporter, Reporter }
import symtab.{ Flags, Names }
-import util.{ SourceFile, BatchSourceFile, ClassPath }
+import util.{ SourceFile, BatchSourceFile, ClassPath, Chars }
import scala.reflect.NameTransformer
import scala.tools.nsc.{ InterpreterResults => IR }
import interpreter._
@@ -74,6 +74,8 @@ import Interpreter._
* @author Lex Spoon
*/
class Interpreter(val settings: Settings, out: PrintWriter) {
+ repl =>
+
/** construct an interpreter that reports to Console */
def this(settings: Settings) = this(settings, new NewLinePrintWriter(new ConsoleWriter, true))
def this() = this(new Settings())
@@ -236,6 +238,12 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
private val boundNameMap = new HashMap[Name, Request]()
private def allHandlers = prevRequests.toList flatMap (_.handlers)
+ def printAllTypeOf = {
+ prevRequests foreach { req =>
+ req.typeOf foreach { case (k, v) => Console.println(k + " => " + v) }
+ }
+ }
+
/** Most recent tree handled which wasn't wholly synthetic. */
private def mostRecentlyHandledTree: Option[Tree] = {
for {
@@ -487,42 +495,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
}
- /** For :power - create trees and type aliases from code snippets. */
- def mkContext(code: String = "") = compiler.analyzer.rootContext(mkUnit(code))
- def mkAlias(name: String, what: String) = interpret("type %s = %s".format(name, what))
- def mkSourceFile(code: String) = new BatchSourceFile("<console>", code)
- def mkUnit(code: String) = new CompilationUnit(mkSourceFile(code))
-
- def mkTree(code: String): Tree = mkTrees(code).headOption getOrElse EmptyTree
- def mkTrees(code: String): List[Tree] = parse(code) getOrElse Nil
- def mkTypedTrees(code: String*): List[compiler.Tree] = {
- class TyperRun extends compiler.Run {
- override def stopPhase(name: String) = name == "superaccessors"
- }
-
- reporter.reset
- val run = new TyperRun
- run compileSources (code.toList.zipWithIndex map {
- case (s, i) => new BatchSourceFile("<console %d>".format(i), s)
- })
- run.units.toList map (_.body)
- }
- def mkTypedTree(code: String) = mkTypedTrees(code).head
-
- def mkType(id: String): compiler.Type = {
- // if it's a recognized identifier, the type of that; otherwise treat the
- // String like it is itself a type (e.g. scala.collection.Map) .
- val typeName = typeForIdent(id) getOrElse id
-
- try definitions.getClass(newTermName(typeName)).tpe
- catch { case _: Throwable => NoType }
- }
-
- private[nsc] val powerMkImports = List(
- "mkContext", "mkTree", "mkTrees", "mkAlias", "mkSourceFile", "mkUnit", "mkType", "mkTypedTree", "mkTypedTrees"
- // , "treeWrapper"
- )
-
/** Compile an nsc SourceFile. Returns true if there are
* no compilation errors, or false otherwise.
*/
@@ -796,16 +768,32 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
private class ImportHandler(imp: Import) extends MemberHandler(imp) {
+ lazy val Import(expr, selectors) = imp
+ def targetType = stringToCompilerType(expr.toString) match {
+ case NoType => None
+ case x => Some(x)
+ }
+
+ private def selectorWild = selectors filter (_.name == USCOREkw) // wildcard imports, e.g. import foo._
+ private def selectorMasked = selectors filter (_.rename == USCOREkw) // masking imports, e.g. import foo.{ bar => _ }
+ private def selectorNames = selectors map (_.name)
+ private def selectorRenames = selectors map (_.rename) filterNot (_ == null)
+
/** Whether this import includes a wildcard import */
- val importsWildcard = imp.selectors map (_.name) contains USCOREkw
+ val importsWildcard = selectorWild.nonEmpty
+
+ /** Complete list of names imported by a wildcard */
+ def wildcardImportedNames: List[Name] = (
+ for (tpe <- targetType ; if importsWildcard) yield
+ tpe.nonPrivateMembers filter (x => x.isMethod && x.isPublic) map (_.name) distinct
+ ).toList.flatten
/** The individual names imported by this statement */
- val importedNames: List[Name] = (
- imp.selectors
- . map (x => x.rename)
- . filter (x => x != null && x != USCOREkw)
- . flatMap (x => List(x.toTypeName, x.toTermName))
- )
+ /** XXX come back to this and see what can be done with wildcards now that
+ * we know how to enumerate the identifiers.
+ */
+ val importedNames: List[Name] =
+ selectorRenames filterNot (_ == USCOREkw) flatMap (x => List(x.toTypeName, x.toTermName))
override def resultExtractionCode(req: Request, code: PrintWriter) =
code println codegenln(imp.toString)
@@ -830,9 +818,10 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** def and val names */
def defNames = partialFlatMap(handlers) { case x: DefHandler => x.boundNames }
- def valAndVarNames = partialFlatMap(handlers) {
+ def valueNames = partialFlatMap(handlers) {
case x: AssignHandler => List(x.helperName)
case x: ValHandler => boundNames
+ case x: ModuleHandler => List(x.name)
}
/** Code to import bound names from previous lines - accessPath is code to
@@ -940,7 +929,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
!reporter.hasErrors
}
-
def atNextPhase[T](op: => T): T = compiler.atPhase(objRun.typerPhase.next)(op)
/** The outermost wrapper object */
@@ -972,7 +960,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
}
- getTypes(valAndVarNames, nme.getterToLocal(_)) ++ getTypes(defNames, identity)
+ getTypes(valueNames, nme.getterToLocal(_)) ++ getTypes(defNames, identity)
}
/** load and run the code using reflection */
@@ -999,43 +987,64 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
}
}
- /** These methods are exposed so REPL commands can access them.
- * The command infrastructure is in InterpreterLoop.
+ /** A container class for methods to be injected into the repl
+ * in power mode.
*/
- def dumpState(xs: List[String]): String = """
- | Names used: %s
- |
- | Identifiers: %s
- |
- | synthvars: %d
- """.stripMargin.format(
- allUsedNames mkString " ",
- unqualifiedIds mkString " ",
- allBoundNames filter isSynthVarName size
- )
-
- // def dumpTrees(xs: List[String]): String = {
- // val treestrs = (xs map requestForIdent).flatten flatMap (_.trees)
- //
- // if (treestrs.isEmpty) "No trees found."
- // else treestrs.map(t => t.toString + " (" + t.getClass.getSimpleName + ")\n").mkString
- // }
+ object power {
+ lazy val compiler: repl.compiler.type = repl.compiler
+ import compiler.{ phaseNames, atPhase, currentRun }
+
+ def mkContext(code: String = "") = compiler.analyzer.rootContext(mkUnit(code))
+ def mkAlias(name: String, what: String) = interpret("type %s = %s".format(name, what))
+ def mkSourceFile(code: String) = new BatchSourceFile("<console>", code)
+ def mkUnit(code: String) = new CompilationUnit(mkSourceFile(code))
+
+ def mkTree(code: String): Tree = mkTrees(code).headOption getOrElse EmptyTree
+ def mkTrees(code: String): List[Tree] = parse(code) getOrElse Nil
+ def mkTypedTrees(code: String*): List[compiler.Tree] = {
+ class TyperRun extends compiler.Run {
+ override def stopPhase(name: String) = name == "superaccessors"
+ }
- def powerUser(): String = {
- beQuietDuring {
- this.bind("repl", "scala.tools.nsc.Interpreter", this)
- this.bind("global", "scala.tools.nsc.Global", compiler)
- interpret("import repl.{ %s, eval }".format(powerMkImports mkString ", "), false)
+ reporter.reset
+ val run = new TyperRun
+ run compileSources (code.toList.zipWithIndex map {
+ case (s, i) => new BatchSourceFile("<console %d>".format(i), s)
+ })
+ run.units.toList map (_.body)
}
+ def mkTypedTree(code: String) = mkTypedTrees(code).head
+ def mkType(id: String): compiler.Type = stringToCompilerType(id)
+
+ def dump(): String = (
+ ("Names used: " :: allUsedNames) ++
+ ("\nIdentifiers: " :: unqualifiedIds)
+ ) mkString " "
+
+ lazy val allPhases: List[Phase] = phaseNames map (currentRun phaseNamed _)
+ def atAllPhases[T](op: => T): List[(String, T)] = allPhases map (ph => (ph.name, atPhase(ph)(op)))
+ def showAtAllPhases(op: => Any): Unit =
+ atAllPhases(op.toString) foreach { case (ph, op) => Console.println("%15s -> %s".format(ph, op take 240)) }
+ }
- """** Power User mode enabled - BEEP BOOP **
- |** New vals! Try repl, global **
- |** New cmds! :help to discover them **
- |** New defs! Give these a whirl: **
- |** mkAlias("Fn", "(String, Int) => Int") **
- |** mkTree("def f(x: Int, y: Int) = x+y") **""".stripMargin
+ def unleash(): Unit = beQuietDuring {
+ interpret("import scala.tools.nsc._")
+ repl.bind("repl", "scala.tools.nsc.Interpreter", this)
+ interpret("val global: repl.compiler.type = repl.compiler")
+ interpret("val power: repl.power.type = repl.power")
+ // interpret("val replVars = repl.replVars")
}
+ /** Artificial object demonstrating completion */
+ // lazy val replVars = CompletionAware(
+ // Map[String, CompletionAware](
+ // "ids" -> CompletionAware(() => unqualifiedIds, completionAware _),
+ // "synthVars" -> CompletionAware(() => allBoundNames filter isSynthVarName map (_.toString)),
+ // "types" -> CompletionAware(() => allSeenTypes map (_.toString)),
+ // "implicits" -> CompletionAware(() => allImplicits map (_.toString))
+ // )
+ // )
+
/** Returns the name of the most recent interpreter result.
* Mostly this exists so you can conveniently invoke methods on
* the previous result.
@@ -1052,11 +1061,21 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
private def requestForName(name: Name): Option[Request] =
prevRequests.reverse find (_.boundNames contains name)
- private def requestForIdent(line: String): Option[Request] =
- requestForName(newTermName(line))
+ private def requestForIdent(line: String): Option[Request] = requestForName(newTermName(line))
+
+ def stringToCompilerType(id: String): compiler.Type = {
+ // if it's a recognized identifier, the type of that; otherwise treat the
+ // String like a value (e.g. scala.collection.Map) .
+ def findType = typeForIdent(id) match {
+ case Some(x) => definitions.getClass(newTermName(x)).tpe
+ case _ => definitions.getModule(newTermName(id)).tpe
+ }
+
+ try findType catch { case _: MissingRequirementError => NoType }
+ }
def typeForIdent(id: String): Option[String] =
- requestForIdent(id) map (_ typeOf newTermName(id))
+ requestForIdent(id) flatMap (x => x.typeOf get newTermName(id))
def methodsOf(name: String) =
evalExpr[List[String]](methodsCode(name)) map (x => NameTransformer.decode(getOriginalName(x)))
@@ -1160,6 +1179,22 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
case x: ImportHandler => x.importedNames
} filterNot isSynthVarName
+ /** Types which have been wildcard imported, such as:
+ * val x = "abc" ; import x._ // type java.lang.String
+ * import java.lang.String._ // object java.lang.String
+ *
+ * Used by tab completion.
+ *
+ * XXX right now this gets import x._ and import java.lang.String._,
+ * but doesn't figure out import String._. There's a lot of ad hoc
+ * scope twiddling which should be swept away in favor of digging
+ * into the compiler scopes.
+ */
+ def wildcardImportedTypes(): List[Type] = {
+ val xs = allHandlers collect { case x: ImportHandler if x.importsWildcard => x.targetType }
+ xs.flatten.reverse.distinct
+ }
+
/** Another entry point for tab-completion, ids in scope */
def unqualifiedIds() = (unqualifiedIdNames() map (_.toString)).distinct.sorted
@@ -1169,16 +1204,6 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
/** Parse the ScalaSig to find type aliases */
def aliasForType(path: String) = ByteCode.aliasForType(path)
- /** Artificial object demonstrating completion */
- def replVarsObject() = CompletionAware(
- Map[String, CompletionAware](
- "ids" -> CompletionAware(() => unqualifiedIds, completionAware _),
- "synthVars" -> CompletionAware(() => allBoundNames filter isSynthVarName map (_.toString)),
- "types" -> CompletionAware(() => allSeenTypes map (_.toString)),
- "implicits" -> CompletionAware(() => allImplicits map (_.toString))
- )
- )
-
// Coming soon
// implicit def string2liftedcode(s: String): LiftedCode = new LiftedCode(s)
// case class LiftedCode(code: String) {
@@ -1191,6 +1216,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) {
// debugging
def isReplDebug = settings.Yrepldebug.value
+ def isCompletionDebug = settings.Ycompletion.value
def DBG(s: String) = if (isReplDebug) out println s else ()
}
@@ -1275,21 +1301,10 @@ object Interpreter {
* This requires replacing all special characters by escape
* codes. It does not add the surrounding " marks. */
def string2code(str: String): String = {
- /** Convert a character to a backslash-u escape */
- def char2uescape(c: Char): String = {
- var rest = c.toInt
- val buf = new StringBuilder
- for (i <- 1 to 4) {
- buf ++= (rest % 16).toHexString
- rest = rest / 16
- }
- "\\u" + buf.toString.reverse
- }
-
val res = new StringBuilder
for (c <- str) c match {
case '"' | '\'' | '\\' => res += '\\' ; res += c
- case _ if c.isControl => res ++= char2uescape(c)
+ case _ if c.isControl => res ++= Chars.char2uescape(c)
case _ => res += c
}
res.toString
diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
index 9d568418f8..4e8a04de44 100644
--- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala
+++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala
@@ -211,15 +211,12 @@ class InterpreterLoop(in0: Option[BufferedReader], protected val out: PrintWrite
}
/** Power user commands */
- // XXX - why does a third argument like "interpreter dumpState(_)" throw an NPE
- // while the version below works?
var powerUserOn = false
val powerCommands: List[Command] = {
import CommandImplicits._
List(
- VarArgs("dump", "displays a view of the interpreter's internal state",
- (xs: List[String]) => interpreter dumpState xs),
- OneArg("search", "search the classpath for classes matching regex", search)
+ OneArg("completions", "generate list of completions for a given String", completions),
+ NoArgs("dump", "displays a view of the interpreter's internal state", () => interpreter.power.dump())
// VarArgs("tree", "displays ASTs for specified identifiers",
// (xs: List[String]) => interpreter dumpTrees xs)
@@ -331,45 +328,26 @@ class InterpreterLoop(in0: Option[BufferedReader], protected val out: PrintWrite
else out.println("The path '" + f + "' doesn't seem to exist.")
}
- /** This isn't going to win any efficiency awards, but it's only
- * available in power mode so I'm unconcerned for the moment.
- */
- def search(arg: String) {
- val MAX_RESULTS = 40
- if (in.completion.isEmpty) return println("No classpath data available")
- val comp = in.completion.get
-
- import java.util.regex.PatternSyntaxException
- import comp.pkgs.agent._
- import scala.collection.JavaConversions._
-
- try {
- val regex = arg.r
- val matches = (
- for ((k, vs) <- dottedPaths) yield {
- val pkgs = if (regex findFirstMatchIn k isDefined) List("package " + k) else Nil
- val classes = vs filter (regex findFirstMatchIn _.visibleName isDefined) map (" class " + k + "." + _.visibleName)
-
- pkgs ::: classes
- }
- ).flatten
+ def completions(arg: String): Unit = {
+ val comp = in.completion getOrElse { return println("Completion unavailable.") }
+ val xs = comp completions arg
- matches take MAX_RESULTS foreach println
- }
- catch {
- case _: PatternSyntaxException =>
- return println("Invalid regular expression: you must use java.util.regex.Pattern syntax.")
- }
+ injectAndName(xs)
}
def power() {
- powerUserOn = true
- out println interpreter.powerUser()
- if (in.history.isDefined)
- interpreter.quietBind("history", "scala.collection.immutable.List[String]", in.historyList)
+ val powerUserBanner =
+ """** Power User mode enabled - BEEP BOOP **
+ |** scala.tools.nsc._ has been imported **
+ |** New vals! Try repl, global, power **
+ |** New cmds! :help to discover them **
+ |** New defs! Type power.<tab> to reveal **""".stripMargin
- if (in.completion.isDefined)
- interpreter.quietBind("replHelper", "scala.tools.nsc.interpreter.CompletionAware", interpreter.replVarsObject())
+ powerUserOn = true
+ interpreter.unleash()
+ injectOne("history", in.historyList)
+ in.completion foreach (x => injectOne("completion", x))
+ out println powerUserBanner
}
def verbosity() = {
@@ -496,6 +474,9 @@ class InterpreterLoop(in0: Option[BufferedReader], protected val out: PrintWrite
* to be recorded for replay, if any.
*/
def interpretStartingWith(code: String): Option[String] = {
+ // signal completion non-completion input has been received
+ in.completion foreach (_.resetVerbosity())
+
def reallyInterpret = {
interpreter.interpret(code) match {
case IR.Error => None
@@ -527,7 +508,7 @@ class InterpreterLoop(in0: Option[BufferedReader], protected val out: PrintWrite
interpretAsPastedTranscript(List(code))
None
}
- else if (Completion.looksLikeInvocation(code)) {
+ else if (Completion.looksLikeInvocation(code) && interpreter.mostRecentVar != "") {
interpretStartingWith(interpreter.mostRecentVar + code)
}
else {
diff --git a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala
index 2e543a0960..430967298c 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeInfo.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeInfo.scala
@@ -102,17 +102,20 @@ abstract class TreeInfo {
case _ => false
}
- def isVariableOrGetter(tree: Tree) = tree match {
- case Ident(_) =>
- tree.symbol.isVariable
- case Select(qual, _) =>
- tree.symbol.isVariable ||
- (mayBeVarGetter(tree.symbol) &&
- tree.symbol.owner.info.member(nme.getterToSetter(tree.symbol.name)) != NoSymbol)
- case Apply(Select(qual, nme.apply), _) =>
- qual.tpe.member(nme.update) != NoSymbol
- case _ =>
- false
+ def isVariableOrGetter(tree: Tree) = {
+ def sym = tree.symbol
+ def isVar = sym.isVariable
+ def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(nme.getterToSetter(sym.name)) != NoSymbol
+
+ tree match {
+ case Ident(_) => isVar
+ case Select(_, _) => isVar || isGetter
+ case _ =>
+ methPart(tree) match {
+ case Select(qual, nme.apply) => qual.tpe.member(nme.update) != NoSymbol
+ case _ => false
+ }
+ }
}
/** Is tree a self constructor call?
@@ -295,10 +298,10 @@ abstract class TreeInfo {
/** The method part of an application node
*/
def methPart(tree: Tree): Tree = tree match {
- case Apply(fn, _) => methPart(fn)
- case TypeApply(fn, _) => methPart(fn)
+ case Apply(fn, _) => methPart(fn)
+ case TypeApply(fn, _) => methPart(fn)
case AppliedTypeTree(fn, _) => methPart(fn)
- case _ => tree
+ case _ => tree
}
def firstArgument(tree: Tree): Tree = tree match {
@@ -325,18 +328,19 @@ abstract class TreeInfo {
false
}
- /** Compilation unit is the predef object
+ /** Compilation unit is class or object 'name' in package 'scala'
*/
def isUnitInScala(tree: Tree, name: Name) = tree match {
- case PackageDef(Ident(nme.scala_), defs) => isObject(defs, name)
+ case PackageDef(Ident(nme.scala_), defs) => isImplDef(defs, name)
case _ => false
}
- private def isObject(trees: List[Tree], name: Name): Boolean = trees match {
- case Import(_, _) :: xs => isObject(xs, name)
- case DocDef(_, tree1) :: Nil => isObject(List(tree1), name)
- case Annotated(_, tree1) :: Nil => isObject(List(tree1), name)
+ private def isImplDef(trees: List[Tree], name: Name): Boolean = trees match {
+ case Import(_, _) :: xs => isImplDef(xs, name)
+ case DocDef(_, tree1) :: Nil => isImplDef(List(tree1), name)
+ case Annotated(_, tree1) :: Nil => isImplDef(List(tree1), name)
case ModuleDef(_, `name`, _) :: Nil => true
+ case ClassDef(_, `name`, _, _) :: Nil => true
case _ => false
}
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 3640b6825b..b40f286680 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -221,9 +221,8 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
var vparamss1 =
vparamss map (vps => vps.map { vd =>
atPos(vd.pos.focus) {
- val pa = if (vd.hasFlag(PRIVATE | LOCAL)) 0L else PARAMACCESSOR
ValDef(
- Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM | BYNAMEPARAM) | PARAM | pa) withAnnotations vd.mods.annotations,
+ Modifiers(vd.mods.flags & (IMPLICIT | DEFAULTPARAM | BYNAMEPARAM) | PARAM | PARAMACCESSOR) withAnnotations vd.mods.annotations,
vd.name, vd.tpt.duplicate, vd.rhs.duplicate)
}})
val (edefs, rest) = body span treeInfo.isEarlyDef
@@ -260,7 +259,7 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable =>
}
// println("typed template, gvdefs = "+gvdefs+", parents = "+parents+", constrs = "+constrs)
constrs foreach (ensureNonOverlapping(_, parents ::: gvdefs))
- // remove defaults
+ // vparamss2 are used as field definitions for the class. remove defaults
val vparamss2 = vparamss map (vps => vps map { vd =>
treeCopy.ValDef(vd, vd.mods &~ DEFAULTPARAM, vd.name, vd.tpt, EmptyTree)
})
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 1cfee481bc..497cfc398b 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -2630,7 +2630,8 @@ self =>
} else if (isStatSep) {
in.nextToken()
} else {
- syntaxErrorOrIncomplete("illegal start of statement", true)
+ val addendum = if (isModifier) " (no modifiers allowed here)" else ""
+ syntaxErrorOrIncomplete("illegal start of statement" + addendum, true)
}
}
stats.toList
diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
index 87f2641f4f..fb97587ec4 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
@@ -19,6 +19,8 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse
class ParserPhase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) {
override val checkable = false
+ override val keepsTypeParams = false
+
def apply(unit: global.CompilationUnit) {
global.informProgress("parsing " + unit)
unit.body =
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 5318c76311..dc193b03db 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -851,7 +851,7 @@ abstract class GenJVM extends SubComponent {
def addForwarders(jclass: JClass, module: Symbol) { addForwarders(jclass, module, _ => true) }
def addForwarders(jclass: JClass, module: Symbol, cond: (Symbol) => Boolean) {
def conflictsIn(cls: Symbol, name: Name) =
- cls.info.nonPrivateMembers.exists(_.name == name)
+ cls.info.members exists (_.name == name)
/** List of parents shared by both class and module, so we don't add forwarders
* for methods defined there - bug #1804 */
@@ -874,7 +874,7 @@ abstract class GenJVM extends SubComponent {
atPhase(currentRun.picklerPhase) (
m.owner != definitions.ObjectClass
&& m.isMethod
- && !m.hasFlag(Flags.CASE | Flags.PROTECTED | Flags.DEFERRED | Flags.SPECIALIZED)
+ && !m.hasFlag(Flags.CASE | Flags.PRIVATE | Flags.PROTECTED | Flags.DEFERRED | Flags.SPECIALIZED)
&& !m.isConstructor
&& !m.isStaticMember
&& !(m.owner == definitions.AnyClass)
diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
index 285e09295d..554dcd4e6d 100644
--- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
+++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
@@ -1053,7 +1053,7 @@ abstract class GenMSIL extends SubComponent {
}
var doEmit = true
- types.get(msym.owner) match {
+ getTypeOpt(msym.owner) match {
case Some(typ) if (typ.IsEnum) => {
def negBool = {
mcode.Emit(OpCodes.Ldc_I4_0)
@@ -1577,9 +1577,9 @@ abstract class GenMSIL extends SubComponent {
mf = mf | FieldAttributes.Static
else {
mf = mf | MethodAttributes.Virtual
- if (sym.isFinal && !types(sym.owner).IsInterface)
+ if (sym.isFinal && !getType(sym.owner).IsInterface)
mf = mf | MethodAttributes.Final
- if (sym.hasFlag(Flags.DEFERRED) || types(sym.owner).IsInterface)
+ if (sym.hasFlag(Flags.DEFERRED) || getType(sym.owner).IsInterface)
mf = mf | MethodAttributes.Abstract
}
}
@@ -1679,8 +1679,14 @@ abstract class GenMSIL extends SubComponent {
sym.tpe.paramTypes.map(msilType).toArray
}
- def getType(sym: Symbol): MsilType = types.get(sym) match {
- case Some(typ) => typ
+ def getType(sym: Symbol) = getTypeOpt(sym).getOrElse(abort(showsym(sym)))
+
+ /**
+ * Get an MSIL type form a symbol. First look in the clrTypes.types map, then
+ * lookup the name using clrTypes.getType
+ */
+ def getTypeOpt(sym: Symbol): Option[MsilType] = types.get(sym) match {
+ case typ @ Some(_) => typ
case None =>
def typeString(sym: Symbol): String = {
val s = if (sym.isNestedClass) typeString(sym.owner) +"+"+ sym.simpleName
@@ -1690,10 +1696,10 @@ abstract class GenMSIL extends SubComponent {
val name = typeString(sym)
val typ = clrTypes.getType(name)
if (typ == null)
- abort(showsym(sym) + " with name " + name)
+ None
else {
- clrTypes.types(sym) = typ
- typ
+ types(sym) = typ
+ Some(typ)
}
}
@@ -1703,10 +1709,20 @@ abstract class GenMSIL extends SubComponent {
}
def createTypeBuilder(iclass: IClass) {
+ /**
+ * First look in the clrTypes.types map, then see if it's a class we're
+ * currently compiling by looking at the icodes.classes map, then finally
+ * lookup the name using clrTypes.getType (by calling getType).
+ */
def msilTypeFromSym(sym: Symbol): MsilType = {
- types.get(sym) match {
- case Some(mtype) => mtype
- case None => createTypeBuilder(classes(sym)); types(sym)
+ types.get(sym).getOrElse {
+ classes.get(sym) match {
+ case Some(iclass) =>
+ createTypeBuilder(iclass)
+ types (sym)
+ case None =>
+ getType(sym)
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala
index 4c7b1977bb..aa784e9c87 100644
--- a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala
@@ -1,6 +1,6 @@
package scala.tools.nsc
-package dependencies;
-import util.SourceFile;
+package dependencies
+import util.SourceFile
import io.AbstractFile
import collection._
import symtab.Flags
@@ -8,11 +8,11 @@ import symtab.Flags
trait DependencyAnalysis extends SubComponent with Files {
import global._
- val phaseName = "dependencyAnalysis";
+ val phaseName = "dependencyAnalysis"
def off = settings.make.value == "all"
- def newPhase(prev : Phase) = new AnalysisPhase(prev)
+ def newPhase(prev: Phase) = new AnalysisPhase(prev)
lazy val maxDepth = settings.make.value match {
case "changed" => 0
@@ -25,7 +25,7 @@ trait DependencyAnalysis extends SubComponent with Files {
// todo: order insensible checking and, also checking timestamp?
def validateClasspath(cp1: String, cp2: String): Boolean = cp1 == cp2
- def nameToFile(src: AbstractFile, name : String) =
+ def nameToFile(src: AbstractFile, name: String) =
settings.outputDirs.outputDirFor(src)
.lookupPathUnchecked(name.toString.replace(".", java.io.File.separator) + ".class", false)
@@ -39,7 +39,7 @@ trait DependencyAnalysis extends SubComponent with Files {
def dependenciesFile: Option[AbstractFile] = depFile
def classpath = settings.classpath.value
- def newDeps = new FileDependencies(classpath);
+ def newDeps = new FileDependencies(classpath)
var dependencies = newDeps
@@ -48,19 +48,19 @@ trait DependencyAnalysis extends SubComponent with Files {
/** Top level definitions per source file. */
val definitions: mutable.Map[AbstractFile, List[Symbol]] =
new mutable.HashMap[AbstractFile, List[Symbol]] {
- override def default(f : AbstractFile) = Nil
+ override def default(f: AbstractFile) = Nil
}
/** External references used by source file. */
val references: mutable.Map[AbstractFile, immutable.Set[String]] =
new mutable.HashMap[AbstractFile, immutable.Set[String]] {
- override def default(f : AbstractFile) = immutable.Set()
+ override def default(f: AbstractFile) = immutable.Set()
}
/** External references for inherited members used in the source file */
val inherited: mutable.Map[AbstractFile, immutable.Set[Inherited]] =
new mutable.HashMap[AbstractFile, immutable.Set[Inherited]] {
- override def default(f : AbstractFile) = immutable.Set()
+ override def default(f: AbstractFile) = immutable.Set()
}
/** Write dependencies to the current file. */
@@ -71,15 +71,14 @@ trait DependencyAnalysis extends SubComponent with Files {
/** Load dependencies from the given file and save the file reference for
* future saves.
*/
- def loadFrom(f: AbstractFile, toFile: String => AbstractFile) : Boolean = {
+ def loadFrom(f: AbstractFile, toFile: String => AbstractFile): Boolean = {
dependenciesFile = f
FileDependencies.readFrom(f, toFile) match {
case Some(fd) =>
val success = if (shouldCheckClasspath) validateClasspath(fd.classpath, classpath) else true
dependencies = if (success) fd else {
- if (settings.debug.value) {
- println("Classpath has changed. Nuking dependencies");
- }
+ if (settings.debug.value)
+ println("Classpath has changed. Nuking dependencies")
newDeps
}
@@ -88,15 +87,13 @@ trait DependencyAnalysis extends SubComponent with Files {
}
}
- def filter(files : List[SourceFile]) : List[SourceFile] =
+ def filter(files: List[SourceFile]): List[SourceFile] =
if (off) files
- else if (dependencies.isEmpty){
- if(settings.debug.value){
- println("No known dependencies. Compiling everything");
- }
+ else if (dependencies.isEmpty) {
+ println("No known dependencies. Compiling " +
+ (if (settings.debug.value) files.mkString(", ") else "everything"))
files
- }
- else {
+ } else {
val (direct, indirect) = dependencies.invalidatedFiles(maxDepth);
val filtered = files.filter(x => {
val f = x.file.absolute
@@ -105,8 +102,7 @@ trait DependencyAnalysis extends SubComponent with Files {
filtered match {
case Nil => println("No changes to recompile");
case x => println("Recompiling " + (
- if(settings.debug.value) x.mkString(", ")
- else x.length + " files")
+ if(settings.debug.value) x.mkString(", ") else x.length + " files")
)
}
filtered
@@ -114,13 +110,13 @@ trait DependencyAnalysis extends SubComponent with Files {
case class Inherited(qualifier: String, member: Name)
- class AnalysisPhase(prev : Phase) extends StdPhase(prev){
+ class AnalysisPhase(prev: Phase) extends StdPhase(prev) {
override def cancelled(unit: CompilationUnit) =
super.cancelled(unit) && !unit.isJava
def apply(unit : global.CompilationUnit) {
- val f = unit.source.file.file;
+ val f = unit.source.file.file
// When we're passed strings by the interpreter
// they have no source file. We simply ignore this case
// as irrelevant to dependency analysis.
@@ -145,7 +141,7 @@ trait DependencyAnalysis extends SubComponent with Files {
dependencies.reset(source)
for (d <- unit.depends; if (d.sourceFile != null)){
- dependencies.depends(source, d.sourceFile);
+ dependencies.depends(source, d.sourceFile)
}
}
diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala
index ca72f6581b..7fd6538566 100644
--- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala
@@ -52,7 +52,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
compiler.addSourceless
assert(settings.docformat.value == "html")
if (!reporter.hasErrors) {
- val modelFactory = (new model.ModelFactory(compiler, settings))
+ val modelFactory = (new model.ModelFactory(compiler, settings) with model.comment.CommentFactory)
val docModel = modelFactory.makeModel
println("model contains " + modelFactory.templatesCount + " documentable templates")
(new html.HtmlFactory(docModel)) generate docModel
diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala
index 3d02689605..4897d78488 100644
--- a/src/compiler/scala/tools/nsc/doc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/doc/Settings.scala
@@ -32,4 +32,6 @@ class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) {
// working around issue described in r18708.
suppressVTWarn.value = true
+
+ // TODO: add a new setting for whether or not to document sourceless entities (e.g., Any, Unit, etc)
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
index ae98ecf1f6..31b932ac53 100644
--- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
@@ -215,4 +215,10 @@ abstract class HtmlPage { thisPage =>
case tpl :: tpls => templateToHtml(tpl) ++ sep ++ templatesToHtml(tpls, sep)
}
+ def docEntityKindToString(ety: DocTemplateEntity) =
+ if (ety.isTrait) "trait"
+ else if (ety.isClass) "class"
+ else if (ety.isObject) "object"
+ else if (ety.isPackage) "package"
+ else "class" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not
}
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala
index 8f1d537c43..784a92f1ff 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala
@@ -63,18 +63,41 @@ class Index(universe: Universe) extends HtmlPage {
<ol class="templates">{
val tpls: Map[String, Seq[DocTemplateEntity]] =
(pack.templates filter (t => !t.isPackage && !isExcluded(t) )) groupBy (_.name)
+
+ val placeholderSeq: NodeSeq = <div class="placeholder"></div>
+
+ def createLink(entity: DocTemplateEntity, includePlaceholder: Boolean, includeText: Boolean) = {
+ val entityType = docEntityKindToString(entity)
+ val linkContent = (
+ { if (includePlaceholder) placeholderSeq else NodeSeq.Empty }
+ ++
+ { if (includeText) <span class="tplLink">{ Text(packageQualifiedName(entity)) }</span> else NodeSeq.Empty }
+ )
+ <a class="tplshow" href={ relativeLinkTo(entity) }><span class={ entityType }>({ Text(entityType) })</span>{ linkContent }</a>
+ }
+
for (tn <- tpls.keySet.toSeq sortBy (_.toLowerCase)) yield {
- val entries = tpls(tn) sortWith { (less, more) => less.isTrait || more.isObject }
- def doEntry(ety: DocTemplateEntity, firstEty: Boolean): NodeSeq = {
- val etyTpe =
- if (ety.isTrait) "trait" else if (ety.isClass) "class" else if (ety.isObject) "object" else "package"
- <a class="tplshow" href={ relativeLinkTo(ety) }>
- { if (firstEty) Text(packageQualifiedName(ety)) else NodeSeq.Empty }
- <span class={ etyTpe }>({ Text(etyTpe) })</span>
- </a>
- }
- <li title={ entries.head.qualifiedName }>{
- doEntry(entries.head, true) ++ (entries.tail map (doEntry(_, false)))
+ val entities = tpls(tn)
+ val row = (entities find (e => e.isPackage || e.isObject), entities find (e => e.isTrait || e.isClass))
+
+ val itemContents = row match {
+ case (Some(obj), None) => createLink(obj, includePlaceholder = true, includeText = true)
+
+ case (maybeObj, Some(template)) =>
+ val firstLink = maybeObj match {
+ case Some(obj) => createLink(obj, includePlaceholder = false, includeText = false)
+ case None => placeholderSeq
+ }
+
+ firstLink ++ createLink(template, includePlaceholder = false, includeText = true)
+
+ case _ => // FIXME: this default case should not be necessary. For some reason AnyRef is not a package, object, trait, or class
+ val entry = entities.head
+ placeholderSeq ++ createLink(entry, includePlaceholder = false, includeText = true)
+ }
+
+ <li title={ entities.head.qualifiedName }>{
+ itemContents
}</li>
}
}</ol>
diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
index 41a7de93bd..12d24a7953 100644
--- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala
@@ -60,13 +60,13 @@ class Template(tpl: DocTemplateEntity) extends HtmlPage {
<div id="mbrsel">
{ if (tpl.linearization.isEmpty) NodeSeq.Empty else
<div id="ancestors">
- <h3>Inherited</h3>
+ <span class="filtertype">Inherited</span>
<ol><li class="hideall">Hide All</li><li class="showall">Show all</li></ol>
<ol id="linearization">{ tpl.linearization map { wte => <li class="in" name={ wte.qualifiedName }>{ wte.name }</li> } }</ol>
</div>
}
<div id="visbl">
- <h3>Visibility</h3>
+ <span class="filtertype">Visibility</span>
<ol><li class="public in">Public</li><li class="all out">All</li></ol>
</div>
</div>
@@ -263,8 +263,7 @@ class Template(tpl: DocTemplateEntity) extends HtmlPage {
}
def kindToString(mbr: MemberEntity): String = mbr match {
- case tpl: DocTemplateEntity =>
- if (tpl.isPackage) "package" else if (tpl.isClass) "class" else if (tpl.isTrait) "trait" else "object"
+ case tpl: DocTemplateEntity => docEntityKindToString(tpl)
case ctor: Constructor => "new"
case tme: MemberEntity =>
( if (tme.isImplicit) "implicit " else "" ) +
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css
index 0bae7dbc3a..fc3f6d4c29 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css
@@ -145,10 +145,21 @@ h1 {
}
#tpl ol > li .icon {
+ padding-right: 5px;
bottom: -2px;
position: relative;
}
+#tpl .templates div.placeholder {
+ padding-right: 5px;
+ width: 13px;
+ display: inline-block;
+}
+
+#tpl .templates span.tplLink {
+ padding-left: 8px;
+}
+
#content {
border-left-width: 1px;
border-left-color: black;
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
index f315a283b6..92de97f619 100644
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
+++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
@@ -198,13 +198,11 @@ div.members > ol > li {
.cmt code {
font-family: monospace;
-/* font-size: small;*/
}
.cmt pre {
display: block;
font-family: monospace;
-/* font-size: small;*/
margin: 2px 0 2px 0;
}
@@ -329,10 +327,12 @@ div.fullcomment dl.paramcmts > dd + dt + dd {
margin-bottom: 10px;
}
-#mbrsel > div > h3 {
+#mbrsel > div > span.filtertype {
padding: 4px;
- display: inline;
+ float: left;
+ display: inline-block;
color: white;
+ width: 4.5em;
}
#mbrsel > div > ol {
@@ -346,7 +346,6 @@ div.fullcomment dl.paramcmts > dd + dt + dd {
#mbrsel > div > ol > li {
padding: 4px 8px 4px 8px;
-/* font-weight: bold;*/
background-color: white;
display: inline-block;
cursor: crosshair;
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
index d7e1e2fe1a..e2a25d7ea4 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -13,7 +13,7 @@ import symtab.Flags
import model.{ RootPackage => RootPackageEntity }
/** This trait extracts all required information for documentation from compilation units */
-class ModelFactory(val global: Global, val settings: doc.Settings) extends CommentFactory { thisFactory =>
+class ModelFactory(val global: Global, val settings: doc.Settings) { thisFactory: ModelFactory with CommentFactory =>
import global._
import definitions.{ ObjectClass, ScalaObjectClass, RootPackage, EmptyPackage, NothingClass, AnyClass, AnyRefClass }
@@ -502,9 +502,11 @@ class ModelFactory(val global: Global, val settings: doc.Settings) extends Comme
val name = optimize(nameBuffer.toString)
}
- def templateShouldDocument(aSym: Symbol): Boolean =
- (aSym.isPackageClass || (aSym.sourceFile != null)) && localShouldDocument(aSym) &&
+ def templateShouldDocument(aSym: Symbol): Boolean = {
+ // TODO: document sourceless entities (e.g., Any, etc), based on a new Setting to be added
+ (aSym.isPackageClass || (aSym.sourceFile != null)) && localShouldDocument(aSym) &&
( aSym.owner == NoSymbol || templateShouldDocument(aSym.owner) )
+ }
def localShouldDocument(aSym: Symbol): Boolean =
!aSym.isPrivate && (aSym.isProtected || aSym.privateWithin == NoSymbol) && !aSym.isSynthetic
diff --git a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala
index a998f9dfc8..a42b8347a7 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ByteCode.scala
@@ -10,7 +10,8 @@ import java.io.File
import java.lang.reflect
import java.util.jar.{ JarEntry, JarFile }
import java.util.concurrent.ConcurrentHashMap
-import util.ScalaClassLoader.getSystemLoader
+import util.ScalaClassLoader
+import ScalaClassLoader.getSystemLoader
object ByteCode {
/** Until I figure out why I can't get scalap onto the classpath such
@@ -34,8 +35,8 @@ object ByteCode {
def scalaSigBytesForPath(path: String) =
for {
module <- DECODER
- method <- decoderMethod("scalaSigBytes", classOf[String], classOf[ClassLoader])
- names <- method.invoke(module, path, this.getClass.getClassLoader).asInstanceOf[Option[Array[Byte]]]
+ method <- decoderMethod("scalaSigAnnotationBytes", classOf[String])
+ names <- method.invoke(module, path).asInstanceOf[Option[Array[Byte]]]
}
yield names
diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala
index b90da5bf98..fddb1ee928 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala
@@ -3,35 +3,14 @@
* @author Paul Phillips
*/
-//
-// TODO, if practical:
-//
-// 1) Types: val s: String = x.<tab> should only show members which result in a String.
-// Possible approach: evaluate buffer as if current identifier is
-// 2) Implicits: x.<tab> should show not only x's members but those of anything for which
-// there is an implicit conversion from x.
-// 4) Imports: after import scala.collection.mutable._, HashMap should be among
-// my top level identifiers.
-// 5) Caching: parsing the jars every startup seems wasteful, but experimentally
-// there is little to no gain from caching.
package scala.tools.nsc
package interpreter
import jline._
-import java.net.URL
import java.util.{ List => JList }
-import java.lang.reflect
-import scala.tools.util.PathResolver
-import io.{ Path, Directory }
object Completion {
- // methods to leave out of completion
- val excludeMethods = List("hashCode", "equals", "wait", "notify", "notifyAll")
-
- // strings to look for an exclude by default
- val excludeStrings = List("$$super", "MODULE$")
-
def looksLikeInvocation(code: String) = (
(code != null)
&& (code startsWith ".")
@@ -40,80 +19,231 @@ object Completion {
&& !(code startsWith "..")
)
- trait Forwarder extends CompletionAware {
- def forwardTo: Option[CompletionAware]
-
- override def completions() = forwardTo map (_.completions()) getOrElse Nil
- override def follow(s: String) = forwardTo flatMap (_ follow s)
+ object Forwarder {
+ def apply(forwardTo: () => Option[CompletionAware]): CompletionAware = new CompletionAware {
+ def completions(verbosity: Int) = forwardTo() map (_ completions verbosity) getOrElse Nil
+ override def follow(s: String) = forwardTo() flatMap (_ follow s)
+ }
}
}
import Completion._
// REPL completor - queries supplied interpreter for valid
// completions based on current contents of buffer.
-class Completion(repl: Interpreter) {
- self =>
+class Completion(val repl: Interpreter) extends CompletionOutput {
+ // verbosity goes up with consecutive tabs
+ private var verbosity: Int = 0
+ def resetVerbosity() = verbosity = 0
- private lazy val classPath = repl.compilerClasspath
+ def isCompletionDebug = repl.isCompletionDebug
+ def DBG(msg: => Any) = if (isCompletionDebug) println(msg.toString)
+ def debugging[T](msg: String): T => T = (res: T) => returning[T](res)(x => DBG(msg + x))
- // the unqualified vals/defs/etc visible in the repl
- val ids = new IdentCompletion(repl)
- // the top level packages we know about
- val pkgs = new PackageCompletion(classPath)
- // members of Predef
- val predef = new StaticCompletion(classOf[scala.Predef$]) {
- override def filterNotFunction(s: String) = (
- (s contains "2") ||
- (s startsWith "wrap") ||
- (s endsWith "Wrapper") ||
- (s endsWith "Ops")
- )
+ lazy val global: repl.compiler.type = repl.compiler
+ import global._
+ import definitions.{ PredefModule, RootClass, AnyClass, AnyRefClass, ScalaPackage, JavaLangPackage }
+
+ // XXX not yet used.
+ lazy val dottedPaths = {
+ def walk(tp: Type): scala.List[Symbol] = {
+ val pkgs = tp.nonPrivateMembers filter (_.isPackage)
+ pkgs ++ (pkgs map (_.tpe) flatMap walk)
+ }
+ walk(RootClass.tpe)
}
- // members of scala.*
- val scalalang = new pkgs.SubCompletor("scala") with Forwarder {
- def forwardTo = pkgs follow "scala"
- val arityClasses = {
- val names = List("Tuple", "Product", "Function")
- val expanded = for (name <- names ; index <- 0 to 22 ; dollar <- List("", "$")) yield name + index + dollar
- Set(expanded: _*)
+ def getType(name: String, isModule: Boolean) = {
+ val f = if (isModule) definitions.getModule(_: Name) else definitions.getClass(_: Name)
+ try Some(f(name).tpe)
+ catch { case _: MissingRequirementError => None }
+ }
+
+ def typeOf(name: String) = getType(name, false)
+ def moduleOf(name: String) = getType(name, true)
+
+ trait CompilerCompletion {
+ def tp: Type
+ def effectiveTp = tp match {
+ case MethodType(Nil, resType) => resType
+ case PolyType(Nil, resType) => resType
+ case _ => tp
}
- override def filterNotFunction(s: String) = {
- val simple = s.reverse takeWhile (_ != '.') reverse
+ // for some reason any's members don't show up in subclasses, which
+ // we need so 5.<tab> offers asInstanceOf etc.
+ private def anyMembers = AnyClass.tpe.nonPrivateMembers
+ def anyRefMethodsToShow = List("isInstanceOf", "asInstanceOf", "toString")
+
+ def tos(sym: Symbol) = sym.name.decode.toString
+ def memberNamed(s: String) = members find (x => tos(x) == s)
+ def hasMethod(s: String) = methods exists (x => tos(x) == s)
+
+ // XXX we'd like to say "filterNot (_.isDeprecated)" but this causes the
+ // compiler to crash for reasons not yet known.
+ def members = (effectiveTp.nonPrivateMembers ++ anyMembers) filter (_.isPublic)
+ def methods = members filter (_.isMethod)
+ def packages = members filter (_.isPackage)
+ def aliases = members filter (_.isAliasType)
- (arityClasses contains simple) ||
- (s endsWith "Exception") ||
- (s endsWith "Error")
+ def memberNames = members map tos
+ def methodNames = methods map tos
+ def packageNames = packages map tos
+ def aliasNames = aliases map tos
+ }
+
+ object TypeMemberCompletion {
+ def apply(tp: Type): TypeMemberCompletion = {
+ if (tp.typeSymbol.isPackageClass) new PackageCompletion(tp)
+ else new TypeMemberCompletion(tp)
}
+ def imported(tp: Type) = new ImportCompletion(tp)
}
- // members of java.lang.*
- val javalang = new pkgs.SubCompletor("java.lang") with Forwarder {
- def forwardTo = pkgs follow "java.lang"
- import reflect.Modifier.isPublic
- private def existsAndPublic(s: String): Boolean = {
- val name = if (s contains ".") s else "java.lang." + s
- val clazz = classForName(name) getOrElse (return false)
-
- isPublic(clazz.getModifiers)
+
+ class TypeMemberCompletion(val tp: Type) extends CompletionAware with CompilerCompletion {
+ def excludeEndsWith: List[String] = Nil
+ def excludeStartsWith: List[String] = List("<") // <byname>, <repeated>, etc.
+ def excludeNames: List[String] = anyref.methodNames -- anyRefMethodsToShow ++ List("_root_")
+
+ def methodSignatureString(sym: Symbol) = {
+ def asString = new MethodSymbolOutput(sym).methodString()
+
+ if (isCompletionDebug)
+ repl.power.showAtAllPhases(asString)
+
+ atPhase(currentRun.typerPhase)(asString)
+ }
+
+ def exclude(name: String): Boolean = (
+ (name contains "$") ||
+ (excludeNames contains name) ||
+ (excludeEndsWith exists (name endsWith _)) ||
+ (excludeStartsWith exists (name startsWith _))
+ )
+ def filtered(xs: List[String]) = xs filterNot exclude distinct
+
+ def completions(verbosity: Int) =
+ debugging(tp + " completions ==> ")(filtered(memberNames))
+
+ override def follow(s: String): Option[CompletionAware] =
+ debugging(tp + " -> '" + s + "' ==> ")(memberNamed(s) map (x => TypeMemberCompletion(x.tpe)))
+
+ override def alternativesFor(id: String): List[String] =
+ debugging(id + " alternatives ==> ") {
+ val alts = members filter (x => x.isMethod && tos(x) == id) map methodSignatureString
+
+ if (alts.nonEmpty) "" :: alts else Nil
+ }
+
+ override def toString = "TypeMemberCompletion(%s)".format(tp)
+ }
+
+ class PackageCompletion(tp: Type) extends TypeMemberCompletion(tp) {
+ override def excludeNames = anyref.methodNames
+ }
+
+ class LiteralCompletion(lit: Literal) extends TypeMemberCompletion(lit.value.tpe) {
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => filtered(memberNames)
+ case _ => memberNames
+ }
+ }
+
+ class ImportCompletion(tp: Type) extends TypeMemberCompletion(tp) {
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => filtered(members filterNot (_.isSetter) map tos)
+ case _ => super.completions(verbosity)
+ }
+ }
+
+ // not for completion but for excluding
+ object anyref extends TypeMemberCompletion(AnyRefClass.tpe) { }
+
+ // the unqualified vals/defs/etc visible in the repl
+ object ids extends CompletionAware {
+ override def completions(verbosity: Int) = repl.unqualifiedIds ::: List("classOf")
+ // we try to use the compiler and fall back on reflection if necessary
+ // (which at present is for anything defined in the repl session.)
+ override def follow(id: String) =
+ if (completions(0) contains id) {
+ for (clazz <- repl clazzForIdent id) yield {
+ (typeOf(clazz.getName) map TypeMemberCompletion.apply) getOrElse new InstanceCompletion(clazz)
+ }
+ }
+ else None
+ }
+
+ // wildcard imports in the repl like "import global._" or "import String._"
+ private def imported = repl.wildcardImportedTypes map TypeMemberCompletion.imported
+
+ // literal Ints, Strings, etc.
+ object literals extends CompletionAware {
+ def simpleParse(code: String): Tree = {
+ val unit = new CompilationUnit(new util.BatchSourceFile("<console>", code))
+ val scanner = new syntaxAnalyzer.UnitParser(unit)
+ val tss = scanner.templateStatSeq(false)._2
+
+ if (tss.size == 1) tss.head else EmptyTree
}
- override def filterNotFunction(s: String) = {
- (s endsWith "Exception") ||
- (s endsWith "Error") ||
- (s endsWith "Impl") ||
- (s startsWith "CharacterData")
+
+ def completions(verbosity: Int) = Nil
+
+ override def follow(id: String) = simpleParse(id) match {
+ case x: Literal => Some(new LiteralCompletion(x))
+ case _ => None
}
- override def completions() = super.completions() filter existsAndPublic
}
- val literals = new LiteralCompletion {
- lazy val global = repl.compiler
- val parent = self
+
+ // top level packages
+ object rootClass extends TypeMemberCompletion(RootClass.tpe) { }
+ // members of Predef
+ object predef extends TypeMemberCompletion(PredefModule.tpe) {
+ override def excludeEndsWith = super.excludeEndsWith ++ List("Wrapper", "ArrayOps")
+ override def excludeStartsWith = super.excludeStartsWith ++ List("wrap")
+ override def excludeNames = anyref.methodNames
+
+ override def exclude(name: String) = super.exclude(name) || (
+ (name contains "2")
+ )
+
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => Nil
+ case _ => super.completions(verbosity)
+ }
}
+ // members of scala.*
+ object scalalang extends PackageCompletion(ScalaPackage.tpe) {
+ def arityClasses = List("Product", "Tuple", "Function")
+ def skipArity(name: String) = arityClasses exists (x => name != x && (name startsWith x))
+ override def exclude(name: String) = super.exclude(name) || (
+ skipArity(name)
+ )
- def lastResult = new Forwarder {
- def forwardTo = ids follow repl.mostRecentVar
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => filtered(packageNames ++ aliasNames)
+ case _ => super.completions(verbosity)
+ }
+ }
+ // members of java.lang.*
+ object javalang extends PackageCompletion(JavaLangPackage.tpe) {
+ override lazy val excludeEndsWith = super.excludeEndsWith ++ List("Exception", "Error")
+ override lazy val excludeStartsWith = super.excludeStartsWith ++ List("CharacterData")
+
+ override def completions(verbosity: Int) = verbosity match {
+ case 0 => filtered(packageNames)
+ case _ => super.completions(verbosity)
+ }
}
+ // the list of completion aware objects which should be consulted
+ lazy val topLevelBase: List[CompletionAware] = List(ids, rootClass, predef, scalalang, javalang, literals)
+ def topLevel = topLevelBase ++ imported
+
+ // the first tier of top level objects (doesn't include file completion)
+ def topLevelFor(parsed: Parsed) = topLevel flatMap (_ completionsFor parsed)
+
+ // the most recent result
+ def lastResult = Forwarder(() => ids follow repl.mostRecentVar)
+
def lastResultFor(parsed: Parsed) = {
/** The logic is a little tortured right now because normally '.' is
* ignored as a delimiter, but on .<tab> it needs to be propagated.
@@ -122,12 +252,6 @@ class Completion(repl: Interpreter) {
if (parsed.isEmpty) xs map ("." + _) else xs
}
- // the list of completion aware objects which should be consulted
- val topLevel: List[CompletionAware] = List(ids, pkgs, predef, scalalang, javalang, literals)
-
- // the first tier of top level objects (doesn't include file completion)
- def topLevelFor(parsed: Parsed) = topLevel flatMap (_ completionsFor parsed)
-
// chasing down results which won't parse
def execute(line: String): Option[Any] = {
val parsed = Parsed(line)
@@ -136,34 +260,43 @@ class Completion(repl: Interpreter) {
if (noDotOrSlash) None // we defer all unqualified ids to the repl.
else {
(ids executionFor parsed) orElse
- (pkgs executionFor parsed) orElse
+ (rootClass executionFor parsed) orElse
(FileCompletion executionFor line)
}
}
- // override if history is available
- def lastCommand: Option[String] = None
+ // generic interface for querying (e.g. interpreter loop, testing)
+ def completions(buf: String): List[String] =
+ topLevelFor(Parsed.dotted(buf + ".", buf.length + 1))
// jline's entry point
lazy val jline: ArgumentCompletor =
returning(new ArgumentCompletor(new JLineCompletion, new JLineDelimiter))(_ setStrict false)
+ /** This gets a little bit hairy. It's no small feat delegating everything
+ * and also keeping track of exactly where the cursor is and where it's supposed
+ * to end up. The alternatives mechanism is a little hacky: if there is an empty
+ * string in the list of completions, that means we are expanding a unique
+ * completion, so don't update the "last" buffer because it'll be wrong.
+ */
class JLineCompletion extends Completor {
// For recording the buffer on the last tab hit
- private var lastTab: (String, String) = (null, null)
+ private var lastBuf: String = ""
+ private var lastCursor: Int = -1
// Does this represent two consecutive tabs?
- def isConsecutiveTabs(buf: String) = (buf, lastCommand orNull) == lastTab
+ def isConsecutiveTabs(buf: String, cursor: Int) = cursor == lastCursor && buf == lastBuf
- // verbosity goes up with consecutive tabs
- // TODO - actually implement.
- private var verbosity = 0
+ // Longest common prefix
+ def commonPrefix(xs: List[String]) =
+ if (xs.isEmpty) ""
+ else xs.reduceLeft(_ zip _ takeWhile (x => x._1 == x._2) map (_._1) mkString)
// This is jline's entry point for completion.
- override def complete(buf: String, cursor: Int, candidates: JList[String]): Int = {
- // println("complete: buf = %s, cursor = %d".format(buf, cursor))
- verbosity = if (isConsecutiveTabs(buf)) verbosity + 1 else 0
- lastTab = (buf, lastCommand orNull)
+ override def complete(_buf: String, cursor: Int, candidates: JList[String]): Int = {
+ val buf = onull(_buf)
+ verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0
+ DBG("complete(%s, %d) last = (%s, %d), verbosity: %s".format(buf, cursor, lastBuf, lastCursor, verbosity))
// we don't try lower priority completions unless higher ones return no results.
def tryCompletion(p: Parsed, completionFunction: Parsed => List[String]): Option[Int] = {
@@ -172,17 +305,30 @@ class Completion(repl: Interpreter) {
case xs =>
// modify in place and return the position
xs foreach (candidates add _)
- Some(p.position)
+
+ // update the last buffer unless this is an alternatives list
+ if (xs contains "") Some(p.cursor)
+ else {
+ val advance = commonPrefix(xs)
+ lastCursor = p.position + advance.length
+ lastBuf = (buf take p.position) + advance
+
+ DBG("tryCompletion(%s, _) lastBuf = %s, lastCursor = %s, p.position = %s".format(p, lastBuf, lastCursor, p.position))
+ Some(p.position)
+ }
}
}
+ def mkDotted = Parsed.dotted(buf, cursor) withVerbosity verbosity
+ def mkUndelimited = Parsed.undelimited(buf, cursor) withVerbosity verbosity
+
// a single dot is special cased to completion on the previous result
def lastResultCompletion =
if (!looksLikeInvocation(buf)) None
else tryCompletion(Parsed.dotted(buf drop 1, cursor), lastResultFor)
- def regularCompletion = tryCompletion(Parsed.dotted(buf, cursor), topLevelFor)
- def fileCompletion = tryCompletion(Parsed.undelimited(buf, cursor), FileCompletion completionsFor _.buffer)
+ def regularCompletion = tryCompletion(mkDotted, topLevelFor)
+ def fileCompletion = tryCompletion(mkUndelimited, FileCompletion completionsFor _.buffer)
(lastResultCompletion orElse regularCompletion orElse fileCompletion) getOrElse cursor
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
index 7e94b687bf..cfd3b5e05f 100644
--- a/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/CompletionAware.scala
@@ -21,8 +21,7 @@ trait CompletionAware {
/** The complete list of unqualified Strings to which this
* object will complete.
*/
- def completions(): List[String]
- def completions(start: String): List[String] = completions filter (_ startsWith start)
+ def completions(verbosity: Int): List[String]
/** Default filter to apply to completions.
*/
@@ -47,6 +46,19 @@ trait CompletionAware {
*/
def execute(id: String): Option[Any] = None
+ /** A list of useful information regarding a specific uniquely
+ * identified completion. This is specifically written for the
+ * following situation, but should be useful elsewhere too:
+ *
+ * x.y.z.methodName<tab>
+ *
+ * If "methodName" is among z's completions, and verbosity > 0
+ * indicating tab has been pressed twice consecutively, then we
+ * call alternativesFor and show a list of overloaded method
+ * signatures.
+ */
+ def alternativesFor(id: String): List[String] = Nil
+
/** Given string 'buf', return a list of all the strings
* to which it can complete. This may involve delegating
* to other CompletionAware objects.
@@ -54,12 +66,16 @@ trait CompletionAware {
def completionsFor(parsed: Parsed): List[String] = {
import parsed._
- val cs =
- if (isEmpty) completions()
- else if (isUnqualified && !isLastDelimiter) completions(buffer)
+ val comps = completions(verbosity) filter (_ startsWith buffer)
+ val results =
+ if (isEmpty) comps
+ else if (isUnqualified && !isLastDelimiter) {
+ if (verbosity > 0 && (comps contains buffer)) alternativesFor(buffer)
+ else comps
+ }
else follow(bufferHead) map (_ completionsFor bufferTail) getOrElse Nil
- cs filterNot filterNotFunction map mapFunction sortWith (sortFunction _)
+ results filterNot filterNotFunction map mapFunction sortWith (sortFunction _)
}
/** TODO - unify this and completionsFor under a common traverser.
@@ -67,14 +83,14 @@ trait CompletionAware {
def executionFor(parsed: Parsed): Option[Any] = {
import parsed._
- if (isUnqualified && !isLastDelimiter && (completions contains buffer)) execute(buffer)
+ if (isUnqualified && !isLastDelimiter && (completions(verbosity) contains buffer)) execute(buffer)
else if (!isQualified) None
else follow(bufferHead) flatMap (_ executionFor bufferTail)
}
}
object CompletionAware {
- val Empty = new CompletionAware { val completions = Nil }
+ val Empty = new CompletionAware { def completions(verbosity: Int) = Nil }
// class Forwarder(underlying: CompletionAware) extends CompletionAware {
// override def completions() = underlying.completions()
@@ -101,6 +117,7 @@ object CompletionAware {
def apply(terms: () => List[String], followFunction: String => Option[CompletionAware]): CompletionAware =
new CompletionAware {
def completions = terms()
+ def completions(verbosity: Int) = completions
override def follow(id: String) = followFunction(id)
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala
new file mode 100644
index 0000000000..9b9d9a36f1
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala
@@ -0,0 +1,88 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package interpreter
+
+/** This has a lot of duplication with other methods in Symbols and Types,
+ * but repl completion utility is very sensitive to precise output. Best
+ * thing would be to abstract an interface for how such things are printed,
+ * as is also in progress with error messages.
+ */
+trait CompletionOutput {
+ self: Completion =>
+
+ import global._
+ import definitions.{ NothingClass, AnyClass, isTupleType, isFunctionType, isRepeatedParamType }
+
+ /** Reducing fully qualified noise for some common packages.
+ */
+ val typeTransforms = List(
+ "java.lang." -> "",
+ "scala.collection.immutable." -> "immutable.",
+ "scala.collection.mutable." -> "mutable.",
+ "scala.collection.generic." -> "generic."
+ )
+
+ def quietString(tp: String): String =
+ typeTransforms.foldLeft(tp) {
+ case (str, (prefix, replacement)) =>
+ if (str startsWith prefix) replacement + (str stripPrefix prefix)
+ else str
+ }
+
+ class MethodSymbolOutput(method: Symbol) {
+ val pkg = method.ownerChain find (_.isPackageClass) map (_.fullName) getOrElse ""
+
+ def relativize(str: String): String = quietString(str stripPrefix (pkg + "."))
+ def relativize(tp: Type): String = relativize(tp.normalize.toString)
+ def relativize(sym: Symbol): String = relativize(sym.info)
+
+ def braceList(tparams: List[String]) = if (tparams.isEmpty) "" else (tparams map relativize).mkString("[", ", ", "]")
+ def parenList(params: List[Any]) = params.mkString("(", ", ", ")")
+
+ def methodTypeToString(mt: MethodType) =
+ (mt.paramss map paramsString mkString "") + ": " + relativize(mt.finalResultType)
+
+ def typeToString(tp: Type): String = relativize(
+ tp match {
+ case x if isFunctionType(x) => functionString(x)
+ case x if isTupleType(x) => tupleString(x)
+ case x if isRepeatedParamType(x) => typeToString(x.typeArgs.head) + "*"
+ case mt @ MethodType(_, _) => methodTypeToString(mt)
+ case x => x.toString
+ }
+ )
+
+ def tupleString(tp: Type) = parenList(tp.normalize.typeArgs map relativize)
+ def functionString(tp: Type) = tp.normalize.typeArgs match {
+ case List(t, r) => t + " => " + r
+ case xs => parenList(xs.init) + " => " + xs.last
+ }
+
+ def tparamsString(tparams: List[Symbol]) = braceList(tparams map (_.defString))
+ def paramsString(params: List[Symbol]) = {
+ def paramNameString(sym: Symbol) = if (sym.isSynthetic) "" else sym.nameString + ": "
+ def paramString(sym: Symbol) = paramNameString(sym) + typeToString(sym.info.normalize)
+
+ val isImplicit = params.nonEmpty && params.head.isImplicit
+ val strs = (params map paramString) match {
+ case x :: xs if isImplicit => ("implicit " + x) :: xs
+ case xs => xs
+ }
+ parenList(strs)
+ }
+
+ def methodString() =
+ method.keyString + " " + method.nameString + (method.info.normalize match {
+ case PolyType(Nil, resType) => ": " + typeToString(resType) // nullary method
+ case PolyType(tparams, resType) => tparamsString(tparams) + typeToString(resType)
+ case mt @ MethodType(_, _) => methodTypeToString(mt)
+ case x =>
+ DBG("methodString(): %s / %s".format(x.getClass, x))
+ x.toString
+ })
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala
deleted file mode 100644
index b0152dbbc6..0000000000
--- a/src/compiler/scala/tools/nsc/interpreter/IdentCompletion.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2010 LAMP/EPFL
- * @author Paul Phillips
- */
-
-package scala.tools.nsc
-package interpreter
-
-/** Top level identifiers visible in the repl. It immediately
- * delegates to an InstanceCompletion.
- */
-class IdentCompletion(repl: Interpreter) extends CompletionAware {
- val INTERPRETER_VAR_PREFIX = "res"
-
- def completions() = repl.unqualifiedIds ::: List("classOf")
- override def follow(id: String) =
- // XXX this will be nice but needs solidifying.
- // (repl completionAwareImplicit id) orElse
- if (completions contains id) {
- (repl completionAware id) orElse {
- repl clazzForIdent id map (x => new InstanceCompletion(x))
- }
- }
- else None
-}
diff --git a/src/compiler/scala/tools/nsc/interpreter/LiteralCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/LiteralCompletion.scala
deleted file mode 100644
index 3b74549d27..0000000000
--- a/src/compiler/scala/tools/nsc/interpreter/LiteralCompletion.scala
+++ /dev/null
@@ -1,50 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2010 LAMP/EPFL
- * @author Paul Phillips
- */
-
-package scala.tools.nsc
-package interpreter
-
-import util.BatchSourceFile
-
-/** Literals, so we can pretend they are objects with methods.
- */
-abstract class LiteralCompletion extends CompletionAware {
- val parent: Completion
- val global: Global
-
- import global._
-
- // TODO - figure out how to enumerate available implicit conversions.
- // def richInt = new InstanceCompletion(classOf[scala.runtime.RichInt])
-
- class PrimitiveCompletion(x: Type) extends CompletionAware {
- lazy val completions = x.nonPrivateMembers map (_.name.toString)
- override def follow(s: String) = {
- val member = x.nonPrivateMembers find (_.name.toString == s)
- member flatMap (m => Option(m.tpe)) map (_.resultType) map (x => new PrimitiveCompletion(x))
- }
- }
-
- def simpleParse(code: String): Tree = {
- val unit = new CompilationUnit(new BatchSourceFile("<console>", code))
- val scanner = new syntaxAnalyzer.UnitParser(unit)
-
- // only single statements
- scanner.templateStatSeq(false) match {
- case (_, List(t)) => t
- case (_, x) => EmptyTree
- }
- }
-
- def completions() = Nil
- override def follow(id: String) = simpleParse(id) match {
- case Literal(c @ Constant(_)) => Some(new PrimitiveCompletion(c.tpe))
- // TODO - more AST trees.
- // case Apply(fn @ Ident(name), args) =>
- // classForName(name.toString) map (x => new StaticCompletion(x))
- // None
- case x => None
- }
-}
diff --git a/src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala
deleted file mode 100644
index 26ae4106c6..0000000000
--- a/src/compiler/scala/tools/nsc/interpreter/PackageCompletion.scala
+++ /dev/null
@@ -1,187 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2010 LAMP/EPFL
- * @author Paul Phillips
- */
-
-package scala.tools.nsc
-package interpreter
-
-import java.net.URL
-import java.lang.reflect
-import java.util.concurrent.ConcurrentHashMap
-import io.{ Path, Directory, File, Streamable }
-import scala.tools.util.PathResolver.Defaults.scalaHomeDir
-import scala.concurrent.DelayedLazyVal
-import scala.reflect.NameTransformer.{ decode, encode }
-import PackageCompletion._
-
-/** Completion among all known packages. It examines the jars in a
- * separate thread so as not to slow down startup. If it arrives at
- * an object, it delegates to StaticCompletion for that object.
- */
-class PackageCompletion(classpath: List[URL]) extends CompletionAware {
- // it takes a little while to look through the jars so we use a future and a concurrent map
- class CompletionAgent {
- val dottedPaths: ConcurrentHashMap[String, List[CompletionInfo]] = new ConcurrentHashMap[String, List[CompletionInfo]]
- val topLevelPackages = new DelayedLazyVal(
- () => enumToList(dottedPaths.keys) filterNot (_ contains '.'),
- getDottedPaths(dottedPaths, classpath)
- )
- }
- val agent = new CompletionAgent
- import agent._
-
- def completions() = topLevelPackages()
- override def follow(id: String) =
- if (dottedPaths containsKey id) Some(new SubCompletor(id))
- else None
-
- class SubCompletor(root: String) extends CompletionAware {
- // Look for a type alias
- private def aliasCompletor(path: String): Option[CompletionAware] =
- for (name <- ByteCode aliasForType path ; clazz <- classForName(name + "$")) yield
- new StaticCompletion(clazz)
-
- lazy val pkgObject = classForName(root + ".package$") map (x => new PackageObjectCompletion(x))
- def pkgObjectMembers = pkgObject map (_ completionsFor Parsed("")) getOrElse Nil
-
- private def infos = Option(dottedPaths get root) getOrElse Nil
- def completions() = {
- val xs = infos map (_.visibleName) filterNot (_ == "package")
- xs ::: pkgObjectMembers
- }
-
- override def follow(segment: String): Option[CompletionAware] = {
- PackageCompletion.this.follow(root + "." + segment) orElse {
- for (CompletionInfo(`segment`, className) <- infos ; clazz <- classForName(className)) {
- return Some(new StaticCompletion(clazz))
- }
-
- aliasCompletor(root + "." + segment)
- }
- }
- override def toString = "SubCompletor(%s)" format root
- }
-}
-
-object PackageCompletion {
- import java.util.jar.{ JarEntry, JarFile }
-
- val EXPAND_SEPARATOR_STRING = "$$"
- val ANON_CLASS_NAME = "$anon"
- val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$"
- val IMPL_CLASS_SUFFIX ="$class"
-
- def ignoreClassName(x: String) =
- (x contains EXPAND_SEPARATOR_STRING) ||
- (x contains ANON_CLASS_NAME) ||
- (x contains TRAIT_SETTER_SEPARATOR_STRING) ||
- (x endsWith IMPL_CLASS_SUFFIX) ||
- (x matches """.*\$\d+$""")
-
- def enumToList[T](e: java.util.Enumeration[T]): List[T] = enumToListInternal(e, Nil)
- private def enumToListInternal[T](e: java.util.Enumeration[T], xs: List[T]): List[T] =
- if (e == null || !e.hasMoreElements) xs else enumToListInternal(e, e.nextElement :: xs)
-
- private def isClass(s: String) = s endsWith ".class"
- private def processNames(xs: List[String]) = xs map (_ dropRight 6) filterNot ignoreClassName distinct
-
- def getDirClassFiles(dir: Directory): List[String] =
- processNames(dir.deepList() map (dir relativize _ path) filter isClass toList)
-
- def getJarClassFiles(jar: File): List[String] =
- if (!jar.exists) Nil
- else processNames(enumToList(new JarFile(jar.path).entries) map (_.getName) filter isClass)
-
- object CompletionInfo {
- def unapply(that: Any) = that match {
- case x: CompletionInfo => Some((x.visibleName, x.className))
- case _ => None
- }
- }
-
- abstract class CompletionInfo {
- def visibleName: String
- def className: String
- def getBytes(): Array[Byte]
-
- override def hashCode = visibleName.hashCode
- override def equals(other: Any) = other match {
- case x: CompletionInfo => visibleName == x.visibleName
- case _ => false
- }
- }
-
- case class DirCompletionInfo(visibleName: String, className: String, dir: Directory) extends CompletionInfo {
- lazy val file = dir / File(className)
-
- def getBytes(): Array[Byte] = try file.toByteArray() catch { case _: Exception => Array() }
- }
-
- case class JarCompletionInfo(visibleName: String, className: String, jar: File) extends CompletionInfo {
- lazy val jarfile = new JarFile(jar.path)
- lazy val entry = jarfile getEntry className
-
- def getBytes(): Array[Byte] = {
- if (entry == null) Array() else {
- val x = new Streamable.Bytes { def inputStream() = jarfile getInputStream entry }
- x.toByteArray()
- }
- }
- }
-
- // all the dotted path to classfiles we can find by poking through the jars
- def getDottedPaths(map: ConcurrentHashMap[String, List[CompletionInfo]], classpath: List[URL]): Unit = {
- val cp = classpath.distinct map (x => Path(x.getPath))
- val jars = cp filter (_ hasExtension "jar") map (_.toFile)
-
- /** If we process all dirs uncritically, someone who has '.' in their classpath and
- * runs scala from the filesystem root directory will induce a traversal of their
- * entire filesystem. We could apply some heuristics to avoid this, but for now we
- * will look only in the scalaHome directories, which is most of what we want.
- */
- def isUnderScalaHome(d: Directory) = d.parents exists (_ == scalaHomeDir)
- val dirs = cp collect { case x: Directory => x } filter isUnderScalaHome
-
- // for e.g. foo.bar.baz.C, returns (foo -> bar), (foo.bar -> baz), (foo.bar.baz -> C)
- // and scala.Range$BigInt needs to go scala -> Range -> BigInt
- def subpaths(s: String): List[(String, String)] = {
- val segs = decode(s).split("""[/.]""")
- val components = segs dropRight 1
-
- (1 to components.length).toList flatMap { i =>
- val k = components take i mkString "."
- if (segs(i) contains "$") {
- val dollarsegs = segs(i).split("$").toList
- for (j <- 1 to (dollarsegs.length - 1) toList) yield {
- val newk = k + "." + (dollarsegs take j mkString ".")
- (k -> dollarsegs(j))
- }
- }
- else List(k -> segs(i))
- }
- }
-
- def addToMap(key: String, info: CompletionInfo) = {
- if (map containsKey key) {
- val vs = map.get(key)
- if (vs contains info) ()
- else map.put(key, info :: vs)
- }
- else map.put(key, List(info))
- }
-
- def oneDir(dir: Directory) {
- for (cl <- getDirClassFiles(dir) ; (k, v) <- subpaths(cl))
- addToMap(k, DirCompletionInfo(v, cl, dir))
- }
-
- def oneJar(jar: File) {
- for (cl <- getJarClassFiles(jar) ; (k, v) <- subpaths(cl))
- addToMap(k, JarCompletionInfo(v, cl, jar))
- }
-
- jars foreach oneJar
- dirs foreach oneDir
- }
-} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
index b130396cc6..0b92608d88 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Parsed.scala
@@ -15,18 +15,22 @@ class Parsed private (
val cursor: Int,
val delimited: Char => Boolean
) extends Delimited {
- def isEmpty = buffer == ""
+ def isEmpty = args.isEmpty
def isUnqualified = args.size == 1
def isQualified = args.size > 1
def isAtStart = cursor <= 0
+ private var _verbosity = 0
+ def verbosity = _verbosity
+ def withVerbosity(v: Int): this.type = returning[this.type](this)(_ => _verbosity = v)
+
def args = toArgs(buffer take cursor).toList
def bufferHead = args.head
def headLength = bufferHead.length + 1
- def bufferTail = new Parsed(buffer drop headLength, cursor - headLength, delimited)
+ def bufferTail = new Parsed(buffer drop headLength, cursor - headLength, delimited) withVerbosity verbosity
- def prev = new Parsed(buffer, cursor - 1, delimited)
- def next = new Parsed(buffer, cursor + 1, delimited)
+ def prev = new Parsed(buffer, cursor - 1, delimited) withVerbosity verbosity
+ def next = new Parsed(buffer, cursor + 1, delimited) withVerbosity verbosity
def currentChar = buffer(cursor)
def currentArg = args.last
def position =
@@ -52,7 +56,7 @@ class Parsed private (
object Parsed {
def apply(s: String): Parsed = apply(onull(s), onull(s).length)
- def apply(s: String, cursor: Int): Parsed = apply(onull(s), cursor, "(){},`; \t" contains _)
+ def apply(s: String, cursor: Int): Parsed = apply(onull(s), cursor, "{},`; \t" contains _)
def apply(s: String, cursor: Int, delimited: Char => Boolean): Parsed =
new Parsed(onull(s), cursor, delimited)
diff --git a/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala
index 2aaa6114c2..6c066580ae 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ProductCompletion.scala
@@ -8,6 +8,7 @@ package interpreter
class SeqCompletion[T](elems: Seq[T]) extends CompletionAware {
lazy val completions = elems.indices.toList map ("(%d)" format _)
+ def completions(verbosity: Int) = completions
private def elemAt(name: String) =
if (completions contains name) Some(elems(name drop 1 dropRight 1 toInt)) else None
@@ -27,6 +28,7 @@ class ProductCompletion(root: Product) extends CompletionAware {
}
lazy val completions = caseNames
+ def completions(verbosity: Int) = completions
override def execute(name: String) = fieldForName(name)
override def follow(name: String) = fieldForName(name) map (x => ProductCompletion(x))
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
index 89490119ff..f9ff894d59 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReflectionCompletion.scala
@@ -12,7 +12,6 @@ import Modifier.{ isPrivate, isProtected, isStatic }
import scala.reflect.NameTransformer
import scala.collection.mutable.HashMap
import ReflectionCompletion._
-import Completion.{ excludeMethods }
trait ReflectionCompletion extends CompletionAware {
def clazz: Class[_]
@@ -30,12 +29,6 @@ trait ReflectionCompletion extends CompletionAware {
case x => error(x.toString)
}
- override def filterNotFunction(s: String): Boolean = {
- (excludeMethods contains s) ||
- (s contains "$$super") ||
- (s == "MODULE$")
- }
-
lazy val (staticMethods, instanceMethods) = clazz.getMethods.toList partition (x => isStatic(x.getModifiers))
lazy val (staticFields, instanceFields) = clazz.getFields.toList partition (x => isStatic(x.getModifiers))
@@ -53,15 +46,6 @@ trait ReflectionCompletion extends CompletionAware {
}
}
-/** An instance completion which hides a few useless members.
- */
-class PackageObjectCompletion(clazz: Class[_]) extends InstanceCompletion(clazz) {
- override lazy val completions = memberCompletions
- override def filterNotFunction(s: String) = {
- super.filterNotFunction(s) || (s == "getClass") || (s == "toString")
- }
-}
-
/** A completion aware object representing a single instance of some class.
* It completes to instance fields and methods, and delegates to another
* InstanceCompletion object if it can determine the result type of the element.
@@ -70,6 +54,7 @@ class InstanceCompletion(val clazz: Class[_]) extends ReflectionCompletion {
protected def visibleMembers = instanceMethods ::: instanceFields
def extras = List("isInstanceOf", "asInstanceOf", "toString")
lazy val completions = memberCompletions ::: extras
+ def completions(verbosity: Int) = completions
val (zeroArg, otherArg) = instanceMethods partition (_.getParameterTypes.size == 0)
override def follow(id: String) = {
@@ -85,6 +70,7 @@ class InstanceCompletion(val clazz: Class[_]) extends ReflectionCompletion {
class StaticCompletion(val clazz: Class[_]) extends ReflectionCompletion {
protected def visibleMembers = whichMethods ::: whichFields
lazy val completions = memberCompletions
+ def completions(verbosity: Int) = completions
private def aliasForPath(path: String) = ByteCode aliasForType path flatMap (x => classForName(x + "$"))
def className = clazz.getName
diff --git a/src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala
index 67063192bd..f2af57cc36 100644
--- a/src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/XMLCompletion.scala
@@ -33,6 +33,7 @@ class XMLCompletion(root: Node) extends CompletionAware {
s :: res
}).sorted
}
+ def completions(verbosity: Int) = completions
override def execute(id: String) = getNode(id)
override def follow(id: String) = getNode(id) map (x => new XMLCompletion(x))
diff --git a/src/compiler/scala/tools/nsc/io/Process.scala b/src/compiler/scala/tools/nsc/io/Process.scala
index 698082d19e..ebd7937f33 100644
--- a/src/compiler/scala/tools/nsc/io/Process.scala
+++ b/src/compiler/scala/tools/nsc/io/Process.scala
@@ -153,9 +153,8 @@ class Process(processCreator: () => JProcess) extends Iterable[String] {
private val reader = new BufferedReader(new InputStreamReader(in))
private def finish() {
- // make sure this thread is complete, and close the process's stdin
+ // make sure this thread is complete
join()
- _in.close()
}
def slurp(): String = {
@@ -171,14 +170,19 @@ class Process(processCreator: () => JProcess) extends Iterable[String] {
def next = it.next
}
}
- @tailrec override final def run() {
- reader.readLine match {
- case null =>
- reader.close()
- case x =>
- queue put x
- run()
+ override final def run() {
+ @tailrec def loop() {
+ reader.readLine match {
+ case null =>
+ reader.close()
+ case x =>
+ queue put x
+ loop()
+ }
}
+
+ try loop()
+ catch { case _: IOException => () }
}
}
diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
index ce10f560e9..e35843fc9c 100644
--- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
@@ -29,7 +29,7 @@ abstract class AbstractReporter extends Reporter {
protected def info0(pos: Position, msg: String, _severity: Severity, force: Boolean) {
val severity =
- if (settings.Ywarnfatal.value && _severity == WARNING) ERROR
+ if (settings.Xwarnfatal.value && _severity == WARNING) ERROR
else _severity
severity match {
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index b32796e829..e8443d11c1 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -8,7 +8,7 @@ package scala.tools
package nsc
package settings
-import io.AbstractFile
+import io.{AbstractFile, VirtualDirectory}
import scala.tools.util.StringOps
import scala.collection.mutable.ListBuffer
@@ -291,7 +291,11 @@ class MutableSettings(val errorFn: String => Unit) extends AbsSettings with Scal
classFile.path.startsWith(outDir.path)
singleOutDir match {
- case Some(d) => Nil
+ case Some(d) =>
+ d match {
+ case _: VirtualDirectory => Nil
+ case _ => List(d.lookupPathUnchecked(srcPath, false))
+ }
case None =>
(outputs filter (isBelow _).tupled) match {
case Nil => Nil
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 1b6b8297ef..51b47f87d6 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -27,7 +27,6 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
* Temporary Settings
*/
val suppressVTWarn = BooleanSetting ("-Ysuppress-vt-typer-warnings", "Suppress warnings from the typer when testing the virtual class encoding, NOT FOR FINAL!")
- val javaignorecp = BooleanSetting ("-javaignorecp", "Does nothing - is being removed.") // !!! marked for death, but need new starr.
/**
* Standard settings
@@ -80,6 +79,9 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
val Xshowobj = StringSetting ("-Xshow-object", "object", "Show object info", "")
val showPhases = BooleanSetting ("-Xshow-phases", "Print a synopsis of compiler phases")
val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files", "scala.tools.nsc.io.SourceReader")
+ val Xwarnfatal = BooleanSetting ("-Xfatal-warnings", "Fail the compilation if there are any warnings.")
+ val Xwarninit = BooleanSetting ("-Xwarninit", "Warn about possible changes in initialization semantics")
+ val Xchecknull = BooleanSetting ("-Xcheck-null", "Emit warning on selection of nullable reference")
/** Compatibility stubs for options whose value name did
* not previously match the option name.
@@ -133,21 +135,13 @@ trait ScalaSettings extends AbsScalaSettings with StandardScalaSettings {
val Ytyperdebug = BooleanSetting ("-Ytyper-debug", "Trace all type assignements")
val Ypmatdebug = BooleanSetting ("-Ypmat-debug", "Trace all pattern matcher activity.")
val Yrepldebug = BooleanSetting ("-Yrepl-debug", "Trace all repl activity.")
+ val Ycompletion = BooleanSetting ("-Ycompletion-debug", "Trace all tab completion activity.")
val Ypmatnaive = BooleanSetting ("-Ypmat-naive", "Desugar matches as naively as possible..")
- val Ytailrec = BooleanSetting ("-Ytailrecommend", "Alert methods which would be tail-recursive if private or final.")
val Yjenkins = BooleanSetting ("-Yjenkins-hashCodes", "Use jenkins hash algorithm for case class generated hashCodes.")
// Warnings
- val Ywarnfatal = BooleanSetting ("-Yfatal-warnings", "Fail the compilation if there are any warnings.")
- val Xwarninit = BooleanSetting ("-Xwarninit", "Warn about possible changes in initialization semantics")
- val Xchecknull = BooleanSetting ("-Xcheck-null", "Emit warning on selection of nullable reference")
- val Xwarndeadcode = BooleanSetting ("-Ywarn-dead-code", "Emit warnings for dead code")
- val YwarnShadow = BooleanSetting ("-Ywarn-shadowing", "Emit warnings about possible variable shadowing.")
- val YwarnCatches = BooleanSetting ("-Ywarn-catches", "Emit warnings about catch blocks which catch everything.")
- val Xwarnings = BooleanSetting ("-Xstrict-warnings", "Emit warnings about lots of things.") .
- withPostSetHook(_ =>
- List(YwarnShadow, YwarnCatches, Xwarndeadcode, Xwarninit) foreach (_.value = true)
- )
+ val Ywarndeadcode = BooleanSetting ("-Ywarn-dead-code", "Emit warnings for dead code")
+
/**
* "fsc-specific" settings.
*/
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index a2382063c3..db48189b6b 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -449,7 +449,8 @@ trait Definitions extends reflect.generic.StandardDefinitions {
lazy val BooleanBeanPropertyAttr: Symbol = getClass(sn.BooleanBeanProperty)
lazy val AnnotationDefaultAttr: Symbol = {
- val attr = newClass(RootClass, nme.AnnotationDefaultATTR, List(AnnotationClass.typeConstructor))
+ val RuntimePackageClass = getModule("scala.runtime").tpe.typeSymbol
+ val attr = newClass(RuntimePackageClass, nme.AnnotationDefaultATTR, List(AnnotationClass.typeConstructor))
// This attribute needs a constructor so that modifiers in parsed Java code make sense
attr.info.decls enter (attr newConstructor NoPosition setInfo MethodType(Nil, attr.tpe))
attr
@@ -815,7 +816,7 @@ trait Definitions extends reflect.generic.StandardDefinitions {
StringClass, "+", anyparam, stringtype) setFlag FINAL
val forced = List( // force initialization of every symbol that is entered as a side effect
- AnnotationDefaultAttr,
+ AnnotationDefaultAttr, // #2264
RepeatedParamClass,
JavaRepeatedParamClass,
ByNameParamClass,
@@ -839,9 +840,6 @@ trait Definitions extends reflect.generic.StandardDefinitions {
Object_asInstanceOf
)
- // #2264
- var tmp = AnnotationDefaultAttr
- tmp = RepeatedParamClass // force initialization
if (forMSIL) {
val intType = IntClass.typeConstructor
val intParam = List(intType)
diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
index 5c7e7925ea..9133228768 100644
--- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala
+++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
@@ -95,6 +95,7 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable =>
val SELECTOR_DUMMY = newTermName("<unapply-selector>")
val MODULE_INSTANCE_FIELD = newTermName("MODULE$")
+ val SPECIALIZED_INSTANCE = newTermName("specInstance$")
def isLocalName(name: Name) = name.endsWith(LOCAL_SUFFIX)
def isSetterName(name: Name) = name.endsWith(SETTER_SUFFIX)
@@ -122,6 +123,26 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable =>
} else name
}
+ /** Return the original name and the types on which this name
+ * is specialized. For example,
+ * {{{
+ * splitSpecializedName("foo$mIcD$sp") == ('foo', "I", "D")
+ * }}}
+ * `foo$mIcD$sp` is the name of a method specialized on two type
+ * parameters, the first one belonging to the method itself, on Int,
+ * and another one belonging to the enclosing class, on Double.
+ */
+ def splitSpecializedName(name: Name): (Name, String, String) =
+ if (name.endsWith("$sp")) {
+ val name1 = name.subName(0, name.length - 3)
+ val idxC = name1.lastPos('c')
+ val idxM = name1.lastPos('m', idxC)
+ (name1.subName(0, idxM - 1).toString,
+ name1.subName(idxC + 1, name1.length).toString,
+ name1.subName(idxM + 1, idxC).toString)
+ } else
+ (name, "", "")
+
def localToGetter(name: Name): Name = {
assert(isLocalName(name))//debug
name.subName(0, name.length - LOCAL_SUFFIX.length)
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index 9c6d6662e3..ebab5d9be7 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -606,6 +606,8 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
supersym == NoSymbol || supersym.isIncompleteIn(base)
}
+ // Does not always work if the rawInfo is a SourcefileLoader, see comment
+ // in "def coreClassesFirst" in Global.
final def exists: Boolean =
this != NoSymbol && (!owner.isPackageClass || { rawInfo.load(this); rawInfo != NoType })
@@ -910,8 +912,11 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
else {
val current = phase
try {
- while (phase.keepsTypeParams && (phase.prev ne NoPhase)) phase = phase.prev
+ while ((phase.prev ne NoPhase) && phase.prev.keepsTypeParams) phase = phase.prev
+// while (phase.keepsTypeParams && (phase.prev ne NoPhase)) phase = phase.prev
if (phase ne current) phase = phase.next
+ if (settings.debug.value && (phase ne current))
+ log("checking unsafeTypeParams(" + this + ") at: " + current + " reading at: " + phase)
rawInfo.typeParams
} finally {
phase = current
@@ -1538,7 +1543,7 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
"package object "+owner.nameString
else
compose(List(kindString,
- if (isClassConstructor) owner.simpleName+idString else nameString))
+ if (isClassConstructor) owner.simpleName.decode+idString else nameString))
/** If owner is a package object, its owner, else the normal owner.
*/
@@ -1742,20 +1747,22 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable =>
private var mtpePeriod = NoPeriod
private var mtpePre: Type = _
private var mtpeResult: Type = _
+ private var mtpeInfo: Type = _
override def cloneSymbolImpl(owner: Symbol): Symbol =
new MethodSymbol(owner, pos, name).copyAttrsFrom(this)
def typeAsMemberOf(pre: Type): Type = {
if (mtpePeriod == currentPeriod) {
- if (mtpePre eq pre) return mtpeResult
+ if ((mtpePre eq pre) && (mtpeInfo eq info)) return mtpeResult
} else if (isValid(mtpePeriod)) {
mtpePeriod = currentPeriod
- if (mtpePre eq pre) return mtpeResult
+ if ((mtpePre eq pre) && (mtpeInfo eq info)) return mtpeResult
}
val res = pre.computeMemberType(this)
mtpePeriod = currentPeriod
mtpePre = pre
+ mtpeInfo = info
mtpeResult = res
res
}
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 863ad7bec4..8f1ece5b9e 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -1205,7 +1205,18 @@ trait Types extends reflect.generic.Types { self: SymbolTable =>
} else {
incCounter(compoundBaseTypeSeqCount)
baseTypeSeqCache = undetBaseTypeSeq
- baseTypeSeqCache = memo(compoundBaseTypeSeq(this))(_.baseTypeSeq updateHead typeSymbol.tpe)
+ baseTypeSeqCache = if (typeSymbol.isRefinementClass)
+ memo(compoundBaseTypeSeq(this))(_.baseTypeSeq updateHead typeSymbol.tpe)
+ else
+ compoundBaseTypeSeq(this)
+ // [Martin] suppressing memo-ization solves the problem with "same type after erasure" errors
+ // when compiling with
+ // scalac scala.collection.IterableViewLike.scala scala.collection.IterableLike.scala
+ // I have not yet figured out precisely why this is the case.
+ // My current assumption is that taking memos forces baseTypeSeqs to be computed
+ // at stale types (i.e. the underlying typeSymbol has already another type).
+ // I do not yet see precisely why this would cause a problem, but it looks
+ // fishy in any case.
}
}
//Console.println("baseTypeSeq(" + typeSymbol + ") = " + baseTypeSeqCache.toList);//DEBUG
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index c78664bc19..fc635874a6 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -876,6 +876,7 @@ abstract class ClassfileParser {
val srcfileLeaf = pool.getName(in.nextChar).toString.trim
val srcpath = sym.enclosingPackage match {
case NoSymbol => srcfileLeaf
+ case definitions.EmptyPackage => srcfileLeaf
case pkg => pkg.fullName(File.separatorChar)+File.separator+srcfileLeaf
}
srcfile0 = settings.outputDirs.srcFilesFor(in.file, srcpath).find(_.exists)
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index 7169516560..ad88b783b4 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -1,4 +1,4 @@
-/* NSC -- new Scala compiler
+/* NSC -- new Scala compiler
* Copyright 2005-2010 LAMP/EPFL
* @author
*/
@@ -25,12 +25,18 @@ abstract class Constructors extends Transform with ast.TreeDSL {
new ConstructorTransformer(unit)
class ConstructorTransformer(unit: CompilationUnit) extends Transformer {
+ import collection.mutable
+
+ private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = new mutable.HashMap[Symbol, List[Tree]]
def transformClassTemplate(impl: Template): Template = {
val clazz = impl.symbol.owner // the transformed class
val stats = impl.body // the transformed template body
val localTyper = typer.atOwner(impl, clazz)
+ val specializedFlag: Symbol = clazz.info.decl(nme.SPECIALIZED_INSTANCE)
+ val shouldGuard = (specializedFlag != NoSymbol) && !clazz.hasFlag(SPECIALIZED)
+
var constr: DefDef = null // The primary constructor
var constrParams: List[Symbol] = null // ... and its parameters
var constrBody: Block = null // ... and its body
@@ -68,6 +74,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
}
var thisRefSeen: Boolean = false
+ var usesSpecializedField: Boolean = false
// A transformer for expressions that go into the constructor
val intoConstructorTransformer = new Transformer {
@@ -87,6 +94,8 @@ abstract class Constructors extends Transform with ast.TreeDSL {
gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos
case Select(_, _) =>
thisRefSeen = true
+ if (specializeTypes.specializedTypeVars(tree.symbol).nonEmpty)
+ usesSpecializedField = true
super.transform(tree)
case This(_) =>
thisRefSeen = true
@@ -275,12 +284,106 @@ abstract class Constructors extends Transform with ast.TreeDSL {
copyParam(acc, parameter(acc))
}
+ /** Return a single list of statements, merging the generic class constructor with the
+ * specialized stats. The original statements are retyped in the current class, and
+ * assignments to generic fields that have a corresponding specialized assignment in
+ * `specializedStats` are replaced by the specialized assignment.
+ */
+ def mergeConstructors(genericClazz: Symbol, originalStats: List[Tree], specializedStats: List[Tree]): List[Tree] = {
+ val specBuf = new ListBuffer[Tree]
+ specBuf ++= specializedStats
+
+ def specializedAssignFor(sym: Symbol): Option[Tree] =
+ specializedStats.find {
+ case Assign(sel @ Select(This(_), _), rhs) if sel.symbol.hasFlag(SPECIALIZED) =>
+ val (generic, _, _) = nme.splitSpecializedName(nme.localToGetter(sel.symbol.name))
+ generic == nme.localToGetter(sym.name)
+ case _ => false
+ }
+
+ log("merging: " + originalStats.mkString("\n") + " : " + specializedStats.mkString("\n"))
+ val res = for (s <- originalStats; val stat = s.duplicate) yield {
+ log("merge: looking at " + stat)
+ val stat1 = stat match {
+ case Assign(sel @ Select(This(_), field), _) =>
+ specializedAssignFor(sel.symbol).getOrElse(stat)
+ case _ => stat
+ }
+ if (stat1 ne stat) {
+ log("replaced " + stat + " with " + stat1)
+ specBuf -= stat1
+ }
+
+ if (stat1 eq stat) {
+ // statements coming from the original class need retyping in the current context
+ if (settings.debug.value) log("retyping " + stat1)
+ val d = new specializeTypes.Duplicator
+ d.retyped(localTyper.context1.asInstanceOf[d.Context],
+ stat1,
+ genericClazz,
+ clazz,
+ Map.empty)
+ } else
+ stat1
+ }
+ if (specBuf.nonEmpty)
+ println("residual specialized constructor statements: " + specBuf)
+ res
+ }
+
+ /** Add an 'if' around the statements coming after the super constructor. This
+ * guard is necessary if the code uses specialized fields. A specialized field is
+ * initialized in the subclass constructor, but the accessors are (already) overridden
+ * and pointing to the (empty) fields. To fix this, a class with specialized fields
+ * will not run its constructor statements if the instance is specialized. The specialized
+ * subclass includes a copy of those constructor statements, and runs them. To flag that a class
+ * has specialized fields, and their initialization should be deferred to the subclass, method
+ * 'specInstance$' is added in phase specialize.
+ */
+ def guardSpecializedInitializer(stats0: List[Tree]): List[Tree] = if (settings.nospecialization.value) stats0 else {
+ // split the statements in presuper and postsuper
+ var (prefix, postfix) = stats0.span(tree => !((tree.symbol ne null) && tree.symbol.isConstructor))
+ if (postfix.nonEmpty) {
+ prefix = prefix :+ postfix.head
+ postfix = postfix.tail
+ }
+
+ if (usesSpecializedField && shouldGuard && postfix.nonEmpty) {
+ // save them for duplication in the specialized subclass
+ guardedCtorStats(clazz) = postfix
+
+ val tree =
+ If(
+ Apply(
+ Select(
+ Apply(gen.mkAttributedRef(specializedFlag), List()),
+ definitions.getMember(definitions.BooleanClass, nme.UNARY_!)),
+ List()),
+ Block(postfix, Literal(())),
+ EmptyTree)
+
+ prefix ::: List(localTyper.typed(tree))
+ } else if (clazz.hasFlag(SPECIALIZED)) {
+ // add initialization from its generic class constructor
+ val (genericName, _, _) = nme.splitSpecializedName(clazz.name)
+ val genericClazz = clazz.owner.info.decl(genericName.toTypeName)
+ assert(genericClazz != NoSymbol)
+
+ guardedCtorStats.get(genericClazz) match {
+ case Some(stats1) =>
+ val merged = mergeConstructors(genericClazz, stats1, postfix)
+ prefix ::: merged
+ case None => stats0
+ }
+ } else stats0
+ }
+
// Assemble final constructor
defBuf += treeCopy.DefDef(
constr, constr.mods, constr.name, constr.tparams, constr.vparamss, constr.tpt,
treeCopy.Block(
constrBody,
- paramInits ::: constrPrefixBuf.toList ::: constrStatBuf.toList,
+ paramInits ::: constrPrefixBuf.toList ::: guardSpecializedInitializer(constrStatBuf.toList),
constrBody.expr));
// Unlink all fields that can be dropped from class scope
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 94d52bc1cb..88a7e13d80 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -152,7 +152,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with ast.
if (unboundedGenericArrayLevel(tp) == 1) ObjectClass.tpe
else if (args.head.typeSymbol == NothingClass || args.head.typeSymbol == NullClass) arrayType(ObjectClass.tpe)
else typeRef(apply(pre), sym, args map this)
- else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass) erasedTypeRef(ObjectClass)
+ else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass || sym == NotNullClass) erasedTypeRef(ObjectClass)
else if (sym == UnitClass) erasedTypeRef(BoxedUnitClass)
else if (sym.isRefinementClass) apply(intersectionDominator(tp.parents))
else if (sym.isClass) typeRef(apply(rebindInnerClass(pre, sym)), sym, List()) // #2585
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index d5d7ca254e..1ab310282d 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -119,6 +119,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* type bounds of other @specialized type parameters (and not in its result type).
*/
def degenerate = false
+
+ def isAccessor = false
}
/** Symbol is a special overloaded method of 'original', in the environment env. */
@@ -132,11 +134,16 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
/** Symbol is a specialized accessor for the `target' field. */
- case class SpecializedAccessor(target: Symbol) extends SpecializedInfo
+ case class SpecializedAccessor(target: Symbol) extends SpecializedInfo {
+ override def isAccessor = true
+ }
/** Symbol is a specialized method whose body should be the target's method body. */
case class Implementation(target: Symbol) extends SpecializedInfo
+ /** Symbol is a specialized override paired with `target'. */
+ case class SpecialOverride(target: Symbol) extends SpecializedInfo
+
/** An Inner class that specializes on a type parameter of the enclosing class. */
case class SpecializedInnerClass(target: Symbol, env: TypeEnv) extends SpecializedInfo
@@ -217,18 +224,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* specialization on method type parameters, the second on outer environment.
*/
private def specializedName(name: Name, types1: List[Type], types2: List[Type]): Name = {
- def split: (String, String, String) = {
- if (name.endsWith("$sp")) {
- val name1 = name.subName(0, name.length - 3)
- val idxC = name1.lastPos('c')
- val idxM = name1.lastPos('m', idxC)
- (name1.subName(0, idxM - 1).toString,
- name1.subName(idxC + 1, name1.length).toString,
- name1.subName(idxM + 1, idxC).toString)
- } else
- (name.toString, "", "")
- }
-
if (nme.INITIALIZER == name || (types1.isEmpty && types2.isEmpty))
name
else if (nme.isSetterName(name))
@@ -236,8 +231,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
else if (nme.isLocalName(name))
nme.getterToLocal(specializedName(nme.localToGetter(name), types1, types2))
else {
- val (base, cs, ms) = split
- newTermName(base + "$"
+ val (base, cs, ms) = nme.splitSpecializedName(name)
+ newTermName(base.toString + "$"
+ "m" + ms + types1.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "")
+ "c" + cs + types2.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "$sp"))
}
@@ -319,21 +314,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}))
- private def specializedTypeVars(tpe: List[Type]): immutable.Set[Symbol] =
+ def specializedTypeVars(tpe: List[Type]): immutable.Set[Symbol] =
tpe.foldLeft(immutable.ListSet.empty[Symbol]: immutable.Set[Symbol]) {
(s, tp) => s ++ specializedTypeVars(tp)
}
- private def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] =
+ def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] =
specializedTypeVars(atPhase(currentRun.typerPhase)(sym.info))
/** Return the set of @specialized type variables mentioned by the given type. */
- private def specializedTypeVars(tpe: Type): immutable.Set[Symbol] = tpe match {
+ def specializedTypeVars(tpe: Type): immutable.Set[Symbol] = tpe match {
case TypeRef(pre, sym, args) =>
if (sym.isTypeParameter && sym.hasAnnotation(SpecializedClass))
specializedTypeVars(args) + sym
else if (sym.isTypeSkolem && sym.deSkolemize.hasAnnotation(SpecializedClass)) {
- println("cought skolem without @specialized")
specializedTypeVars(args) + sym
} else
specializedTypeVars(args)
@@ -680,48 +674,51 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
*/
private def specialOverrides(clazz: Symbol): List[Symbol] = {
log("specialOverrides(" + clazz + ")")
- val opc = new overridingPairs.Cursor(clazz)
val oms = new mutable.ListBuffer[Symbol]
- while (opc.hasNext) {
+ for (overriding <- clazz.info.decls;
+ val allOverridden = overriding.allOverriddenSymbols
+ if !allOverridden.isEmpty;
+ val overridden = allOverridden.head) {
if (settings.debug.value)
- log("\toverriding pairs: " + opc.overridden.fullName + ": " + opc.overridden.info
- + " overriden by " + opc.overriding.fullName + ": " + opc.overriding.info)
- if (opc.overriding.owner == clazz && !specializedTypeVars(opc.overridden.info).isEmpty) {
- if (settings.debug.value) log("\t\tspecializedTVars: " + specializedTypeVars(opc.overridden.info))
- val env = unify(opc.overridden.info, opc.overriding.info, emptyEnv)
+ log("\toverriding pairs: " + overridden.fullName + ": " + overridden.info
+ + " overriden by " + overriding.fullName + ": " + overriding.info)
+ if (overriding.owner == clazz && !specializedTypeVars(overridden.info).isEmpty) {
+ if (settings.debug.value) log("\t\tspecializedTVars: " + specializedTypeVars(overridden.info))
+ val env = unify(overridden.info, overriding.info, emptyEnv)
if (settings.debug.value)
log("\t\tenv: " + env + "isValid: "
- + TypeEnv.isValid(env, opc.overridden)
- + " exists: " + opc.overridden.owner.info.decl(specializedName(opc.overridden, env)))
+ + TypeEnv.isValid(env, overridden)
+ + " looking for: " + specializedName(overridden, env) + " in:\n"
+ + atPhase(phase.next)(overridden.owner.info.decls)
+ + "found: " + atPhase(phase.next)(overridden.owner.info.decl(specializedName(overridden, env))))
if (!env.isEmpty
- && TypeEnv.isValid(env, opc.overridden)
- && opc.overridden.owner.info.decl(specializedName(opc.overridden, env)) != NoSymbol) {
- log("Added specialized overload for " + opc.overriding.fullName + " in env: " + env)
- val om = specializedOverload(clazz, opc.overridden, env)
+ && TypeEnv.isValid(env, overridden)
+ && atPhase(phase.next)(overridden.owner.info.decl(specializedName(overridden, env))) != NoSymbol) {
+ log("Added specialized overload for " + overriding.fullName + " in env: " + env)
+ val om = specializedOverload(clazz, overridden, env)
typeEnv(om) = env
- if (!opc.overriding.isDeferred) {
- concreteSpecMethods += opc.overriding
+ if (!overriding.isDeferred) {
+ concreteSpecMethods += overriding
// if the override is a normalized member, 'om' gets the implementation from
// its original target, and adds the environment of the normalized member (that is,
// any specialized /method/ type parameter bindings)
- info(om) = info.get(opc.overriding) match {
+ info(om) = info.get(overriding) match {
case Some(NormalizedMember(target)) =>
- typeEnv(om) = env ++ typeEnv(opc.overriding)
- Implementation(target)
- case _ => Implementation(opc.overriding)
+ typeEnv(om) = env ++ typeEnv(overriding)
+ SpecialOverride(target)
+ case _ => SpecialOverride(overriding)
}
- info(opc.overriding) = Forward(om)
+ info(overriding) = Forward(om)
log("typeEnv(om) = " + typeEnv(om))
- om setPos opc.overriding.pos // set the position of the concrete, overriding member
+ om setPos overriding.pos // set the position of the concrete, overriding member
}
- overloads(opc.overriding) = Overload(om, env) :: overloads(opc.overriding)
+ overloads(overriding) = Overload(om, env) :: overloads(overriding)
oms += om
atPhase(phase.next)(
- assert(opc.overridden.owner.info.decl(om.name) != NoSymbol,
- "Could not find " + om.name + " in " + opc.overridden.owner.info.decls))
+ assert(overridden.owner.info.decl(om.name) != NoSymbol,
+ "Could not find " + om.name + " in " + overridden.owner.info.decls))
}
}
- opc.next
}
oms.toList
}
@@ -827,13 +824,19 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case PolyType(targs, ClassInfoType(base, decls, clazz)) =>
val parents = base map specializedType
log("transformInfo (poly) " + clazz + " with parents1: " + parents + " ph: " + phase)
- PolyType(targs, ClassInfoType(parents, new Scope(specializeClass(clazz, typeEnv(clazz))), clazz))
+// if (clazz.name.toString == "$colon$colon")
+// (new Throwable).printStackTrace
+ PolyType(targs, ClassInfoType(parents,
+ new Scope(specializeClass(clazz, typeEnv(clazz)) ::: specialOverrides(clazz)),
+ clazz))
case ClassInfoType(base, decls, clazz) if !clazz.isPackageClass =>
atPhase(phase.next)(base.map(_.typeSymbol.info))
val parents = base map specializedType
log("transformInfo " + clazz + " with parents1: " + parents + " ph: " + phase)
- val res = ClassInfoType(base map specializedType, new Scope(specializeClass(clazz, typeEnv(clazz))), clazz)
+ val res = ClassInfoType(base map specializedType,
+ new Scope(specializeClass(clazz, typeEnv(clazz)) ::: specialOverrides(clazz)),
+ clazz)
res
case _ =>
@@ -893,16 +896,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
satisfiable(env, silent)
}
- import java.io.PrintWriter
-
/*************************** Term transformation ************************************/
class Duplicator extends {
val global: SpecializeTypes.this.global.type = SpecializeTypes.this.global
} with typechecker.Duplicators
- import global.typer.typed
-
def specializeCalls(unit: CompilationUnit) = new TypingTransformer(unit) {
/** Map a specializable method to it's rhs, when not deferred. */
val body: mutable.Map[Symbol, Tree] = new mutable.HashMap
@@ -928,8 +927,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
- import posAssigner._
-
override def transform(tree: Tree): Tree = {
val symbol = tree.symbol
@@ -1036,7 +1033,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case (tpe, idx) => TypeTree(tpe) setPos parents(idx).pos
}
treeCopy.Template(tree,
- parents1 /*currentOwner.info.parents.map(tpe => TypeTree(tpe) setPos parents.head.pos)*/,
+ parents1 /*currentOwner.info.parents.map(tpe => TypeTree(tpe) setPos parents.head.pos)*/ ,
self,
atOwner(currentOwner)(transformTrees(body ::: specMembers)))
@@ -1091,6 +1088,18 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
treeCopy.DefDef(tree1, mods, name, tparams, vparamss, tpt, transform(rhs))
}
+ case SpecialOverride(target) =>
+ assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName)
+ if (settings.debug.value) log("moving implementation: " + body(target))
+ // we have an rhs, specialize it
+ val tree1 = addBody(ddef, target)
+ (new ChangeOwnerTraverser(target, tree1.symbol))(tree1.rhs)
+ if (settings.debug.value)
+ println("changed owners, now: " + tree1)
+ val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree1
+ treeCopy.DefDef(tree1, mods, name, tparams, vparamss, tpt, transform(rhs))
+
+
case SpecialOverload(original, env) =>
log("completing specialized " + symbol.fullName + " calling " + original)
val t = DefDef(symbol, { vparamss =>
@@ -1118,7 +1127,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
localTyper.typed(treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1))
}
- case ValDef(mods, name, tpt, rhs) if symbol.hasFlag(SPECIALIZED) =>
+ case ValDef(mods, name, tpt, rhs) if symbol.hasFlag(SPECIALIZED) && !symbol.hasFlag(PARAMACCESSOR) =>
assert(body.isDefinedAt(symbol.alias))
val tree1 = treeCopy.ValDef(tree, mods, name, tpt, body(symbol.alias).duplicate)
if (settings.debug.value) log("now typing: " + tree1 + " in " + tree.symbol.owner.fullName)
@@ -1128,8 +1137,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
symbol.alias.enclClass,
symbol.enclClass,
typeEnv(symbol.alias) ++ typeEnv(tree.symbol))
+// val tree1 =
+// treeCopy.ValDef(tree, mods, name, tpt,
+// localTyper.typed(
+// Apply(Select(Super(currentClass, nme.EMPTY), symbol.alias.getter(symbol.alias.owner)),
+// List())))
+// if (settings.debug.value) log("replaced ValDef: " + tree1 + " in " + tree.symbol.owner.fullName)
+// tree1
+
+ case Apply(sel @ Select(sup @ Super(qual, name), name1), args)
+ if (sup.symbol.info.parents != atPhase(phase.prev)(sup.symbol.info.parents)) =>
+
+ def parents = sup.symbol.info.parents
+ log(tree + " parents changed from: " + atPhase(phase.prev)(parents) + " to: " + parents)
- case Apply(sel @ Select(sup @ Super(qual, name), name1), args) =>
val res = localTyper.typed(
Apply(Select(Super(qual, name) setPos sup.pos, name1) setPos sel.pos, transformTrees(args)) setPos tree.pos)
log("retyping call to super, from: " + symbol + " to " + res.symbol)
@@ -1146,11 +1167,31 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
(tparams1, tparams map (_.symbol))
}
- private def duplicateBody(tree: DefDef, target: Symbol): Tree = {
+
+ private def duplicateBody(tree: DefDef, source: Symbol) = {
+ val symbol = tree.symbol
+ val meth = addBody(tree, source)
+ log("now typing: " + meth + " in " + symbol.owner.fullName)
+ val d = new Duplicator
+ d.retyped(localTyper.context1.asInstanceOf[d.Context],
+ meth,
+ source.enclClass,
+ symbol.enclClass,
+ typeEnv(source) ++ typeEnv(symbol))
+ }
+
+
+ /** Put the body of 'source' as the right hand side of the method 'tree'.
+ * The destination method gets fresh symbols for type and value parameters,
+ * and the body is updated to the new symbols, and owners adjusted accordingly.
+ * However, if the same source tree is used in more than one place, full re-typing
+ * is necessary. @see method duplicateBody
+ */
+ private def addBody(tree: DefDef, source: Symbol): DefDef = {
val symbol = tree.symbol
if (settings.debug.value) log("specializing body of" + symbol.fullName + ": " + symbol.info)
val DefDef(mods, name, tparams, vparamss, tpt, _) = tree
- val (_, origtparams) = splitParams(target.typeParams)
+ val (_, origtparams) = splitParams(source.typeParams)
if (settings.debug.value) log("substituting " + origtparams + " for " + symbol.typeParams)
// skolemize type parameters
@@ -1163,21 +1204,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
// replace value and type parameters of the old method with the new ones
val symSubstituter = new ImplementationAdapter(
- parameters(target).flatten ::: origtparams,
+ parameters(source).flatten ::: origtparams,
vparamss1.flatten.map(_.symbol) ::: newtparams)
- val adapter = new AdaptSpecializedValues
- val tmp = symSubstituter(adapter(body(target).duplicate))
+ val tmp = symSubstituter(body(source).duplicate)
tpt.tpe = tpt.tpe.substSym(oldtparams, newtparams)
- val meth = treeCopy.DefDef(tree, mods, name, tparams, vparamss1, tpt, tmp)
-
- log("now typing: " + meth + " in " + symbol.owner.fullName)
- val d = new Duplicator
- d.retyped(localTyper.context1.asInstanceOf[d.Context],
- meth,
- target.enclClass,
- symbol.enclClass,
- typeEnv(target) ++ typeEnv(symbol))
+ treeCopy.DefDef(tree, mods, name, tparams, vparamss1, tpt, tmp)
}
/** A tree symbol substituter that substitutes on type skolems.
@@ -1201,10 +1233,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
*/
override def transform(tree: Tree): Tree = tree match {
case Select(qual, name) =>
- if (tree.symbol.hasFlag(PRIVATE | PROTECTED)) {
- log("changing private flag of " + tree.symbol + " privateWithin: " + tree.symbol.privateWithin)
+ val sym = tree.symbol
+ if (sym.hasFlag(PRIVATE | PROTECTED) && !nme.isLocalName(sym.name)
+ && (currentClass != sym.owner.enclClass)) {
+ log("changing private flag of " + sym)
// tree.symbol.resetFlag(PRIVATE).setFlag(PROTECTED)
- tree.symbol.makeNotPrivate(tree.symbol.owner)
+ sym.makeNotPrivate(sym.owner)
// tree.symbol.resetFlag(PRIVATE | PROTECTED)
// tree.symbol.privateWithin = NoSymbol
}
@@ -1215,36 +1249,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
- /** Does the given tree need a cast to a type parameter's upper bound?
- * A cast is needed for values of type A, where A is a specialized type
- * variable with a non-trivial upper bound. When A is specialized, its
- * specialization may not satisfy the upper bound. We generate casts to
- * be able to type check code. Such methods will never be called, as they
- * are not visible to the user. The compiler will insert such calls only when
- * the bounds are satisfied.
- */
- private class AdaptSpecializedValues extends Transformer {
- private def needsCast(tree: Tree): Boolean = {
- val sym = tree.tpe.typeSymbol
- (sym.isTypeParameterOrSkolem
- && sym.hasAnnotation(SpecializedClass)
- && sym.info.bounds.hi != definitions.AnyClass.tpe
- /*&& !(tree.tpe <:< sym.info.bounds.hi)*/)
- }
-
- override def transform(tree: Tree): Tree = {
- val tree1 = super.transform(tree)
- if (needsCast(tree1)) {
-// log("inserting cast for " + tree1 + " tpe: " + tree1.tpe)
-// val tree2 = gen.mkAsInstanceOf(tree1, tree1.tpe.typeSymbol.info.bounds.hi)
-// log(" casted to: " + tree2)
- tree1
- } else
- tree1
- }
- def apply(t: Tree): Tree = transform(t)
- }
-
def warn(clazz: Symbol)(pos: Position, err: String) =
if (!clazz.hasFlag(SPECIALIZED))
unit.warning(pos, err)
@@ -1254,9 +1258,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
*/
private def makeSpecializedMembers(cls: Symbol): List[Tree] = {
// add special overrides first
- if (!cls.hasFlag(SPECIALIZED))
- for (m <- specialOverrides(cls)) cls.info.decls.enter(m)
+// if (!cls.hasFlag(SPECIALIZED))
+// for (m <- specialOverrides(cls)) cls.info.decls.enter(m)
val mbrs = new mutable.ListBuffer[Tree]
+ var hasSpecializedFields = false
for (m <- cls.info.decls.toList
if m.hasFlag(SPECIALIZED)
@@ -1264,6 +1269,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
&& satisfiable(typeEnv(m), warn(cls))) {
log("creating tree for " + m.fullName)
if (m.isMethod) {
+ if (info(m).target.isGetterOrSetter) hasSpecializedFields = true
if (m.isClassConstructor) {
val origParamss = parameters(info(m).target)
assert(origParamss.length == 1) // we are after uncurry
@@ -1294,6 +1300,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
// log("created synthetic class: " + m.fullName)
}
}
+ if (hasSpecializedFields) {
+ val sym = cls.newMethod(nme.SPECIALIZED_INSTANCE, cls.pos)
+ .setInfo(MethodType(Nil, definitions.BooleanClass.tpe))
+ cls.info.decls.enter(sym)
+ mbrs += atPos(sym.pos) {
+ DefDef(sym, Literal(cls.hasFlag(SPECIALIZED)).setType(sym.tpe.finalResultType)).setType(NoType)
+ }
+ }
mbrs.toList
}
@@ -1372,6 +1386,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
class SpecializationTransformer(unit: CompilationUnit) extends Transformer {
+ log("specializing " + unit)
override def transform(tree: Tree) =
atPhase(phase.next) {
val res = specializeCalls(unit).transform(tree)
diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
index adeab550ee..9fff8534d3 100644
--- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala
+++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
@@ -39,9 +39,6 @@ abstract class TailCalls extends Transform
}
}
- /** The @tailrec annotation indicates TCO is mandatory */
- private def tailrecRequired(defdef: DefDef) = defdef.symbol hasAnnotation TailrecClass
-
/**
* A Tail Call Transformer
*
@@ -105,6 +102,9 @@ abstract class TailCalls extends Transform
/** Tells whether we are in a (possible) tail position */
var tailPos = false
+ /** The reason this method could not be optimized. */
+ var tailrecFailReason = "it contains a recursive call not in tail position"
+
/** Is the label accessed? */
var accessed = false
@@ -138,7 +138,8 @@ abstract class TailCalls extends Transform
t
}
- private var ctx: Context = new Context()
+ private var ctx: Context = new Context()
+ private def enclosingType = ctx.currentMethod.enclClass.typeOfThis
/** Rewrite this tree to contain no tail recursive calls */
def transform(tree: Tree, nctx: Context): Tree = {
@@ -150,11 +151,50 @@ abstract class TailCalls extends Transform
}
override def transform(tree: Tree): Tree = {
+ /** A possibly polymorphic apply to be considered for tail call transformation.
+ */
+ def rewriteApply(target: Tree, fun: Tree, targs: List[Tree], args: List[Tree]) = {
+ def isRecursiveCall = ctx.currentMethod eq fun.symbol
+ def isMandatory = ctx.currentMethod hasAnnotation TailrecClass
+ def isEligible = ctx.currentMethod.isEffectivelyFinal
+ def transformArgs = transformTrees(args, mkContext(ctx, false))
+ def matchesTypeArgs = ctx.tparams sameElements (targs map (_.tpe.typeSymbol))
+ def defaultTree = treeCopy.Apply(tree, target, transformArgs)
+
+ def sameTypeOfThis(receiver: Tree) =
+ receiver.tpe.widen =:= enclosingType.widen
+
+ /** Records failure reason in Context for reporting.
+ */
+ def cannotRewrite(reason: String) = {
+ if (isMandatory)
+ ctx.tailrecFailReason = reason
+
+ defaultTree
+ }
+ def rewriteTailCall(receiver: Tree, otherArgs: List[Tree]): Tree = {
+ log("Rewriting tail recursive method call at: " + fun.pos)
+
+ ctx.accessed = true
+ typed { atPos(fun.pos)(Apply(Ident(ctx.label), receiver :: otherArgs)) }
+ }
+
+ if (!isRecursiveCall) defaultTree
+ else if (!isEligible) cannotRewrite("it is neither private nor final so can be overridden")
+ else if (!ctx.tailPos) cannotRewrite("it contains a recursive call not in tail position")
+ else if (!matchesTypeArgs) cannotRewrite("it is called recursively with different type arguments")
+ else fun match {
+ case Select(_, _) if forMSIL => cannotRewrite("it cannot be optimized on MSIL")
+ case Select(qual, _) if !sameTypeOfThis(qual) => cannotRewrite("it changes type of 'this' on a polymorphic recursive call")
+ case Select(qual, _) => rewriteTailCall(qual, transformArgs)
+ case _ => rewriteTailCall(This(currentClass), transformArgs)
+ }
+ }
+
tree match {
case dd @ DefDef(mods, name, tparams, vparams, tpt, rhs) =>
log("Entering DefDef: " + name)
- var isTransformed = false
val newCtx = mkContext(ctx)
newCtx.currentMethod = tree.symbol
newCtx.makeLabel()
@@ -162,16 +202,10 @@ abstract class TailCalls extends Transform
newCtx.label.setInfo(MethodType(currentClassParam :: tree.symbol.tpe.params, tree.symbol.tpe.finalResultType))
newCtx.tailPos = true
- val isEligible = newCtx.currentMethod.isEffectivelyFinal || (newCtx.currentMethod.enclClass hasFlag Flags.MODULE)
- // If -Ytailrecommend is given, we speculatively try transforming ineligible methods and
- // report where we would have been successful.
- val recommend = settings.Ytailrec.value
- val savedFlags: Option[Long] = if (recommend) Some(newCtx.currentMethod.flags) else None
-
- if (isEligible || recommend) {
- if (recommend)
- newCtx.currentMethod.flags |= Flags.FINAL
+ val isEligible = newCtx.currentMethod.isEffectivelyFinal
+ val isMandatory = dd.symbol.hasAnnotation(TailrecClass) && !forMSIL // @tailrec annotation indicates mandatory transformation
+ if (isEligible) {
newCtx.tparams = Nil
log(" Considering " + name + " for tailcalls")
tree.symbol.tpe match {
@@ -185,12 +219,10 @@ abstract class TailCalls extends Transform
val t1 = treeCopy.DefDef(tree, mods, name, tparams, vparams, tpt, {
val transformed = transform(rhs, newCtx)
- savedFlags foreach (newCtx.currentMethod.flags = _)
transformed match {
case newRHS if isEligible && newCtx.accessed =>
log("Rewrote def " + newCtx.currentMethod)
- isTransformed = true
val newThis = newCtx.currentMethod
. newValue (tree.pos, nme.THIS)
. setInfo (currentClass.typeOfThis)
@@ -200,19 +232,14 @@ abstract class TailCalls extends Transform
List(ValDef(newThis, This(currentClass))),
LabelDef(newCtx.label, newThis :: (vparams.flatten map (_.symbol)), newRHS)
)))
- case _ if recommend =>
- if (newCtx.accessed)
- unit.warning(dd.pos, "method is tailrecommended")
- // transform with the original flags restored
- transform(rhs, newCtx)
+ case rhs =>
+ if (isMandatory)
+ unit.error(dd.pos, "could not optimize @tailrec annotated method: " + newCtx.tailrecFailReason)
- case rhs => rhs
+ rhs
}
})
- if (!forMSIL && !isTransformed && tailrecRequired(dd))
- unit.error(dd.pos, "could not optimize @tailrec annotated method")
-
log("Leaving DefDef: " + name)
t1
@@ -269,50 +296,16 @@ abstract class TailCalls extends Transform
case Typed(expr, tpt) => super.transform(tree)
case Apply(tapply @ TypeApply(fun, targs), vargs) =>
- lazy val defaultTree = treeCopy.Apply(tree, tapply, transformTrees(vargs, mkContext(ctx, false)))
- if ( ctx.currentMethod.isEffectivelyFinal &&
- ctx.tailPos &&
- isSameTypes(ctx.tparams, targs map (_.tpe.typeSymbol)) &&
- isRecursiveCall(fun)) {
- fun match {
- case Select(receiver, _) =>
- val recTpe = receiver.tpe.widen
- val enclTpe = ctx.currentMethod.enclClass.typeOfThis
- // make sure the type of 'this' doesn't change through this polymorphic recursive call
- if (!forMSIL &&
- (receiver.tpe.typeParams.isEmpty ||
- (receiver.tpe.widen == ctx.currentMethod.enclClass.typeOfThis)))
- rewriteTailCall(fun, receiver :: transformTrees(vargs, mkContext(ctx, false)))
- else
- defaultTree
- case _ => rewriteTailCall(fun, This(currentClass) :: transformTrees(vargs, mkContext(ctx, false)))
- }
- } else
- defaultTree
+ rewriteApply(tapply, fun, targs, vargs)
case TypeApply(fun, args) =>
super.transform(tree)
- case Apply(fun, args) if (fun.symbol == definitions.Boolean_or ||
- fun.symbol == definitions.Boolean_and) =>
- treeCopy.Apply(tree, fun, transformTrees(args))
-
case Apply(fun, args) =>
- lazy val defaultTree = treeCopy.Apply(tree, fun, transformTrees(args, mkContext(ctx, false)))
- if (ctx.currentMethod.isEffectivelyFinal &&
- ctx.tailPos &&
- isRecursiveCall(fun)) {
- fun match {
- case Select(receiver, _) =>
- if (!forMSIL)
- rewriteTailCall(fun, receiver :: transformTrees(args, mkContext(ctx, false)))
- else
- defaultTree
- case _ => rewriteTailCall(fun, This(currentClass) :: transformTrees(args, mkContext(ctx, false)))
- }
- } else
- defaultTree
-
+ if (fun.symbol == Boolean_or || fun.symbol == Boolean_and)
+ treeCopy.Apply(tree, fun, transformTrees(args))
+ else
+ rewriteApply(fun, fun, Nil, args)
case Super(qual, mix) =>
tree
@@ -333,28 +326,5 @@ abstract class TailCalls extends Transform
def transformTrees(trees: List[Tree], nctx: Context): List[Tree] =
trees map ((tree) => transform(tree, nctx))
-
- private def rewriteTailCall(fun: Tree, args: List[Tree]): Tree = {
- log("Rewriting tail recursive method call at: " +
- (fun.pos))
- ctx.accessed = true
- //println("fun: " + fun + " args: " + args)
- val t = atPos(fun.pos)(Apply(Ident(ctx.label), args))
- // println("TAIL: "+t)
- typed(t)
- }
-
- private def isSameTypes(ts1: List[Symbol], ts2: List[Symbol]) = ts1 sameElements ts2
-
- /** Returns <code>true</code> if the fun tree refers to the same method as
- * the one saved in <code>ctx</code>.
- *
- * @param fun the expression that is applied
- * @return <code>true</code> if the tree symbol refers to the innermost
- * enclosing method
- */
- private def isRecursiveCall(fun: Tree): Boolean =
- (fun.symbol eq ctx.currentMethod)
}
-
}
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index e339560837..bad98193b0 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -659,11 +659,6 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
}
treeCopy.DefDef(tree, mods, name, tparams, List(vparamss.flatten), tpt, rhs1)
case Try(body, catches, finalizer) =>
- // If warnings are enabled, alert about promiscuously catching cases.
- if (settings.YwarnCatches.value)
- for (cd <- catches find treeInfo.catchesThrowable)
- unit.warning(cd.pos, "catch clause swallows everything: not advised.")
-
if (catches forall treeInfo.isCatchCase) tree
else {
val exname = unit.fresh.newName(tree.pos, "ex$")
diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
index 635608520d..70bf55e661 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
@@ -22,6 +22,7 @@ trait Analyzer extends AnyRef
with SyntheticMethods
with Unapplies
with NamesDefaults
+ with TypeDiagnostics
{
val global : Global
import global._
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index becb4b069f..8758dd834c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -256,23 +256,25 @@ trait Contexts { self: Analyzer =>
if (diagnostic.isEmpty) ""
else diagnostic.mkString("\n","\n", "")
- def error(pos: Position, err: Throwable) {
- val msg = err.getMessage() + diagString
- if (reportGeneralErrors)
- unit.error(pos, if (checking) "**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg)
- else
- throw err
+ private def addDiagString(msg: String) = {
+ val ds = diagString
+ if (msg endsWith ds) msg else msg + ds
}
- def error(pos: Position, msg: String) {
- val msg1 = msg + diagString
- if (reportGeneralErrors)
- unit.error(pos, if (checking) "**** ERROR DURING INTERNAL CHECKING ****\n" + msg1 else msg1)
- else
- throw new TypeError(pos, msg1)
+ private def unitError(pos: Position, msg: String) =
+ unit.error(pos, if (checking) "**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg)
+
+ def error(pos: Position, err: Throwable) =
+ if (reportGeneralErrors) unitError(pos, addDiagString(err.getMessage()))
+ else throw err
+
+ def error(pos: Position, msg: String) = {
+ val msg1 = addDiagString(msg)
+ if (reportGeneralErrors) unitError(pos, msg1)
+ else throw new TypeError(pos, msg1)
}
- def warning(pos: Position, msg: String) {
+ def warning(pos: Position, msg: String) = {
if (reportGeneralErrors) unit.warning(pos, msg)
}
@@ -484,6 +486,19 @@ trait Contexts { self: Analyzer =>
}
implicitsCache
}
+
+ def lookup(name: Name, expectedOwner: Symbol) = {
+ var res: Symbol = NoSymbol
+ var ctx = this
+ while(res == NoSymbol && ctx.outer != ctx) {
+ val s = ctx.scope.lookup(name)
+ if (s != NoSymbol && s.owner == expectedOwner)
+ res = s
+ else
+ ctx = ctx.outer
+ }
+ res
+ }
}
class ImportInfo(val tree: Import, val depth: Int) {
/** The prefix expression */
diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
index 1dbeb0afd9..de9318d6b6 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
@@ -94,6 +94,15 @@ abstract class Duplicators extends Analyzer {
} else
super.mapOver(tpe)
+ case ThisType(sym) =>
+ val sym1 = updateSym(sym)
+ if (sym1 ne sym) {
+ log("fixing " + sym + " -> " + sym1)
+ ThisType(sym1)
+ } else
+ super.mapOver(tpe)
+
+
case _ =>
super.mapOver(tpe)
}
@@ -136,7 +145,7 @@ abstract class Duplicators extends Analyzer {
ldef.symbol = newsym
log("newsym: " + newsym + " info: " + newsym.info)
- case DefDef(_, _, tparams, vparamss, _, rhs) =>
+ case DefDef(_, name, tparams, vparamss, _, rhs) =>
// invalidate parameters
invalidate(tparams ::: vparamss.flatten)
tree.symbol = NoSymbol
@@ -244,6 +253,11 @@ abstract class Duplicators extends Analyzer {
if (settings.debug.value) log("mapped " + tree + " to " + tree1)
super.typed(atPos(tree.pos)(tree1), mode, pt)
+ case This(_) =>
+ tree.symbol = updateSym(tree.symbol)
+ tree.tpe = null
+ super.typed(tree, mode, pt)
+
case Super(qual, mix) if (oldClassOwner ne null) && (tree.symbol == oldClassOwner) =>
val tree1 = Super(qual, mix)
log("changed " + tree + " to " + tree1)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 4c07e50da9..9a6c4cc401 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -639,9 +639,9 @@ self: Analyzer =>
val applicable = applicableInfos(implicitInfoss, isLocal, invalidImplicits)
if (applicable.isEmpty && !invalidImplicits.isEmpty) {
- infer.setAddendum(tree.pos, () =>
+ setAddendum(tree.pos, () =>
"\n Note: implicit "+invalidImplicits.head+" is not applicable here"+
- "\n because it comes after the application point and it lacks an explicit result type")
+ " because it comes after the application point and it lacks an explicit result type")
}
val start = startCounter(subtypeImprovCount)
@@ -784,7 +784,6 @@ self: Analyzer =>
inferImplicit(tree, appliedType(manifestClass.typeConstructor, List(tp)), true, false, context).tree
def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass)
- def findElemManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else PartialManifestClass)
def mot(tp0: Type): Tree = {
val tp1 = tp0.normalize
@@ -801,39 +800,26 @@ self: Analyzer =>
} else if (sym == RepeatedParamClass || sym == ByNameParamClass) {
EmptyTree
} else if (sym == ArrayClass && args.length == 1) {
- manifestFactoryCall("arrayType", args.head, findElemManifest(args.head))
+ manifestFactoryCall("arrayType", args.head, findManifest(args.head))
} else if (sym.isClass) {
- val suffix = gen.mkClassOf(tp1) :: (args map findSubManifest)
+ val classarg0 = gen.mkClassOf(tp1)
+ val classarg = tp match {
+ case ExistentialType(_, _) =>
+ TypeApply(Select(classarg0, Any_asInstanceOf),
+ List(TypeTree(appliedType(ClassClass.typeConstructor, List(tp)))))
+ case _ =>
+ classarg0
+ }
+ val suffix = classarg :: (args map findSubManifest)
manifestFactoryCall(
"classType", tp,
(if ((pre eq NoPrefix) || pre.typeSymbol.isStaticOwner) suffix
else findSubManifest(pre) :: suffix): _*)
- } else {
- EmptyTree
-/* the following is dropped because it is dangerous
- *
- if (sym.isAbstractType) {
- if (sym.isExistentiallyBound)
- EmptyTree // todo: change to existential parameter manifest
- else if (sym.isTypeParameterOrSkolem)
- EmptyTree // a manifest should have been found by normal searchImplicit
- else {
- // The following is tricky! We want to find the parameterized version of
- // what will become the erasure of the upper bound.
- // But there is a case where the erasure is not a superclass of the current type:
- // Any erases to Object. So an abstract type having Any as upper bound will not see
- // Object as a baseType. That's why we do the basetype trick only when we must,
- // i.e. when the baseclass is parameterized.
- var era = erasure.erasure(tp1)
- if (era.typeSymbol.typeParams.nonEmpty)
- era = tp1.baseType(era.typeSymbol)
- manifestFactoryCall(
- "abstractType", tp,
- findSubManifest(pre) :: Literal(sym.name.toString) :: gen.mkClassOf(era) :: (args map findSubManifest): _*)
- }
+ } else if (sym.isExistentiallyBound && full) {
+ manifestFactoryCall("wildcardType", tp,
+ findManifest(tp.bounds.lo), findManifest(tp.bounds.hi))
} else {
EmptyTree // a manifest should have been found by normal searchImplicit
-*/
}
case RefinedType(parents, decls) =>
// refinement is not generated yet
@@ -841,10 +827,7 @@ self: Analyzer =>
else if (full) manifestFactoryCall("intersectionType", tp, parents map (findSubManifest(_)): _*)
else mot(erasure.erasure.intersectionDominator(parents))
case ExistentialType(tparams, result) =>
- existentialAbstraction(tparams, result) match {
- case ExistentialType(_, _) => mot(result)
- case t => mot(t)
- }
+ mot(tp1.skolemizeExistential)
case _ =>
EmptyTree
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index c8d3308077..1ee1604319 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -120,7 +120,7 @@ trait Infer {
case NoPrefix | ThisType(_) | ConstantType(_) =>
true
case TypeRef(pre, sym, args) =>
- isFullyDefined(pre) && (args.isEmpty || (args forall isFullyDefined))
+ isFullyDefined(pre) && (args forall isFullyDefined)
case SingleType(pre, sym) =>
isFullyDefined(pre)
case RefinedType(ts, decls) =>
@@ -197,72 +197,17 @@ trait Infer {
/** The context-dependent inferencer part */
class Inferencer(context: Context) {
-
/* -- Error Messages --------------------------------------------------- */
-
- private var addendumPos: Position = NoPosition
- private var addendum: () => String = _
-
- def setAddendum(pos: Position, msg: () => String) = {
- addendumPos = pos
- addendum = msg
- }
-
def setError[T <: Tree](tree: T): T = {
- if (tree.hasSymbol)
- if (context.reportGeneralErrors) {
- val name = newTermName("<error: " + tree.symbol + ">")
- tree.setSymbol(
- if (tree.isType) context.owner.newErrorClass(name.toTypeName)
- else context.owner.newErrorValue(name))
- } else {
- tree.setSymbol(if (tree.isType) stdErrorClass else stdErrorValue)
- }
- tree.setType(ErrorType)
- }
-
- def decode(name: Name): String =
- (if (name.isTypeName) "type " else "value ") + name.decode
+ def name = newTermName("<error: " + tree.symbol + ">")
+ def errorClass = if (context.reportGeneralErrors) context.owner.newErrorClass(name.toTypeName) else stdErrorClass
+ def errorValue = if (context.reportGeneralErrors) context.owner.newErrorValue(name) else stdErrorValue
+ def errorSym = if (tree.isType) errorClass else errorValue
- def treeSymTypeMsg(tree: Tree): String =
- if (tree.symbol eq null)
- "expression of type " + tree.tpe
- else if (tree.symbol.hasFlag(OVERLOADED))
- "overloaded method " + tree.symbol + " with alternatives " + tree.tpe
- else
- tree.symbol.toString() +
- (if (tree.symbol.isModule) ""
- else if (tree.tpe.paramSectionCount > 0) ": "+tree.tpe
- else " of type "+tree.tpe) +
- (if (tree.symbol.name == nme.apply) tree.symbol.locationString else "")
-
- def applyErrorMsg(tree: Tree, msg: String, argtpes: List[Type], pt: Type) =
- treeSymTypeMsg(tree) + msg + argtpes.mkString("(", ",", ")") +
- (if (isWildcard(pt)) "" else " with expected result type " + pt)
-
- // todo: use also for other error messages
- private def existentialContext(tp: Type) = tp.existentialSkolems match {
- case List() => ""
- case skolems =>
- def disambiguate(ss: List[String]) = ss match {
- case List() => ss
- case s :: ss1 => s :: (ss1 map (s1 => if (s1 == s) "(some other)"+s1 else s1))
- }
- " where "+(disambiguate(skolems map (_.existentialToString)) mkString ", ")
- }
-
- def foundReqMsg(found: Type, req: Type): String =
- withDisambiguation(found, req) {
- ";\n found : " + found.toLongString + existentialContext(found) +
- "\n required: " + req + existentialContext(req)
- }
+ if (tree.hasSymbol)
+ tree setSymbol errorSym
- def typeErrorMsg(found: Type, req: Type) = {
- //println(found.baseTypeSeq)
- "type mismatch" + foundReqMsg(found, req) +
- (if ((found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req))
- "\n possible cause: missing arguments for method or constructor"
- else "")
+ tree setType ErrorType
}
def error(pos: Position, msg: String) {
@@ -276,67 +221,27 @@ trait Infer {
def typeError(pos: Position, found: Type, req: Type) {
if (!found.isErroneous && !req.isErroneous) {
- error(pos,
- typeErrorMsg(found, req)+
- (if (pos != NoPosition && pos == addendumPos) addendum()
- else ""))
- if (settings.explaintypes.value) explainTypes(found, req)
+ error(pos, withAddendum(pos)(typeErrorMsg(found, req)))
+
+ if (settings.explaintypes.value)
+ explainTypes(found, req)
}
}
+ def typeErrorMsg(found: Type, req: Type) = {
+ def isPossiblyMissingArgs = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req)
+ def missingArgsMsg = if (isPossiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else ""
+
+ "type mismatch" + foundReqMsg(found, req) + missingArgsMsg
+ }
+
def typeErrorTree(tree: Tree, found: Type, req: Type): Tree = {
typeError(tree.pos, found, req)
setError(tree)
}
def explainTypes(tp1: Type, tp2: Type) =
- withDisambiguation(tp1, tp2) { global.explainTypes(tp1, tp2) }
-
- /** If types `tp1' `tp2' contain different type variables with same name
- * differentiate the names by including owner information. Also, if the
- * type error is because of a conflict between two identically named
- * classes and one is in package scala, fully qualify the name so one
- * need not deduce why "java.util.Iterator" and "Iterator" don't match.
- */
- private def withDisambiguation[T](tp1: Type, tp2: Type)(op: => T): T = {
-
- def explainName(sym: Symbol) = {
- if (!sym.name.toString.endsWith(")")) {
- sym.name = newTypeName(sym.name.toString+"(in "+sym.owner+")")
- }
- }
-
- val patches = new ListBuffer[(Symbol, Symbol, Name)]
- for {
- t1 @ TypeRef(_, sym1, _) <- tp1
- t2 @ TypeRef(_, sym2, _) <- tp2
- if sym1 != sym2
- } {
- if (t1.toString == t2.toString) { // type variable collisions
- val name = sym1.name
- explainName(sym1)
- explainName(sym2)
- if (sym1.owner == sym2.owner) sym2.name = newTypeName("(some other)"+sym2.name)
- patches += ((sym1, sym2, name))
- }
- else if (sym1.name == sym2.name) { // symbol name collisions where one is in scala._
- val name = sym1.name
- def scalaQualify(s: Symbol) =
- if (s.owner.isScalaPackageClass) s.name = newTypeName("scala." + s.name)
- List(sym1, sym2) foreach scalaQualify
- patches += ((sym1, sym2, name))
- }
- }
-
- val result = op
-
- for ((sym1, sym2, name) <- patches) {
- sym1.name = name
- sym2.name = name
- }
-
- result
- }
+ withDisambiguation(tp1, tp2)(global.explainTypes(tp1, tp2))
/* -- Tests & Checks---------------------------------------------------- */
@@ -1591,7 +1496,7 @@ trait Infer {
}
def checkDead(tree: Tree): Tree = {
- if (settings.Xwarndeadcode.value && tree.tpe != null && tree.tpe.typeSymbol == NothingClass)
+ if (settings.Ywarndeadcode.value && tree.tpe != null && tree.tpe.typeSymbol == NothingClass)
context.warning (tree.pos, "dead code following this construct")
tree
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index a78ee61ee2..a7f573f98b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -607,6 +607,17 @@ trait Namers { self: Analyzer =>
vparamss.map(_.map(enterValueParam))
}
+ /**
+ * Finds the companion module of a class symbol. Calling .companionModule
+ * does not work for classes defined inside methods.
+ */
+ private def companionModuleOf(clazz: Symbol) = {
+ var res = clazz.companionModule
+ if (res == NoSymbol)
+ res = context.lookup(clazz.name.toTermName, clazz.owner)
+ res
+ }
+
private def templateSig(templ: Template): Type = {
val clazz = context.owner
def checkParent(tpt: Tree): Type = {
@@ -712,29 +723,36 @@ trait Namers { self: Analyzer =>
// add apply and unapply methods to companion objects of case classes,
// unless they exist already; here, "clazz" is the module class
- Namers.this.caseClassOfModuleClass get clazz match {
- case Some(cdef) =>
- addApplyUnapply(cdef, templateNamer)
- caseClassOfModuleClass -= clazz
- case None =>
+ if (clazz.isModuleClass) {
+ Namers.this.caseClassOfModuleClass get clazz match {
+ case Some(cdef) =>
+ addApplyUnapply(cdef, templateNamer)
+ caseClassOfModuleClass -= clazz
+ case None =>
+ }
}
// add the copy method to case classes; this needs to be done here, not in SyntheticMethods, because
// the namer phase must traverse this copy method to create default getters for its parameters.
- Namers.this.caseClassOfModuleClass get clazz.companionModule.moduleClass match {
- case Some(cdef) =>
- def hasCopy(decls: Scope) = {
- decls.iterator exists (_.name == nme.copy)
- }
- if (!hasCopy(decls) &&
- !parents.exists(p => hasCopy(p.typeSymbol.info.decls)) &&
- !parents.flatMap(_.baseClasses).distinct.exists(bc => hasCopy(bc.info.decls)))
- addCopyMethod(cdef, templateNamer)
- case None =>
+ // here, clazz is the ClassSymbol of the case class (not the module).
+ // @check: this seems to work only if the type completer of the class runs before the one of the
+ // module class: the one from the module class removes the entry form caseClassOfModuleClass (see above).
+ if (clazz.isClass && !clazz.hasFlag(MODULE)) {
+ Namers.this.caseClassOfModuleClass get companionModuleOf(clazz).moduleClass match {
+ case Some(cdef) =>
+ def hasCopy(decls: Scope) = {
+ decls.iterator exists (_.name == nme.copy)
+ }
+ if (!hasCopy(decls) &&
+ !parents.exists(p => hasCopy(p.typeSymbol.info.decls)) &&
+ !parents.flatMap(_.baseClasses).distinct.exists(bc => hasCopy(bc.info.decls)))
+ addCopyMethod(cdef, templateNamer)
+ case None =>
+ }
}
- // if default getters (for constructor defaults) need to be added to that module,
- // here's the namer to use
+ // if default getters (for constructor defaults) need to be added to that module, here's the namer
+ // to use. clazz is the ModuleClass. sourceModule works also for classes defined in methods.
val module = clazz.sourceModule
if (classAndNamerOfModule contains module) {
val (cdef, _) = classAndNamerOfModule(module)
@@ -947,6 +965,7 @@ trait Namers { self: Analyzer =>
if (vparamss == List(Nil) && baseParamss.isEmpty) baseParamss = List(Nil)
assert(!overrides || vparamss.length == baseParamss.length, ""+ meth.fullName + ", "+ overridden.fullName)
+ // cache the namer used for entering the default getter symbols
var ownerNamer: Option[Namer] = None
var moduleNamer: Option[(ClassDef, Namer)] = None
@@ -978,7 +997,7 @@ trait Namers { self: Analyzer =>
val parentNamer = if (isConstr) {
val (cdef, nmr) = moduleNamer.getOrElse {
- val module = meth.owner.companionModule
+ val module = companionModuleOf(meth.owner)
module.initialize // call type completer (typedTemplate), adds the
// module's templateNamer to classAndNamerOfModule
val (cdef, nmr) = classAndNamerOfModule(module)
@@ -1029,7 +1048,8 @@ trait Namers { self: Analyzer =>
Modifiers(meth.flags & (PRIVATE | PROTECTED | FINAL)) | SYNTHETIC | DEFAULTPARAM | oflag,
name, deftParams, defvParamss, defTpt, defRhs)
}
- meth.owner.resetFlag(INTERFACE) // there's a concrete member now
+ if (!isConstr)
+ meth.owner.resetFlag(INTERFACE) // there's a concrete member now
val default = parentNamer.enterSyntheticSym(defaultTree)
} else if (baseHasDefault) {
// the parameter does not have a default itself, but the corresponding parameter
diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
index 89b4f910e5..18102a8bb4 100644
--- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -187,10 +187,21 @@ trait NamesDefaults { self: Analyzer =>
baseFun1 match {
// constructor calls
- case Select(New(TypeTree()), _) if isConstr =>
- blockWithoutQualifier(None)
- case Select(TypeApply(New(TypeTree()), _), _) if isConstr =>
- blockWithoutQualifier(None)
+ case Select(New(tp @ TypeTree()), _) if isConstr =>
+ // fixes #3338. Same qualifier for selecting the companion object as for the class.
+ val dq = tp.tpe match {
+ case TypeRef(pre, _, _) if (!pre.typeSymbol.isEmptyPackageClass) =>
+ moduleQual(tp.pos, mod => gen.mkAttributedSelect(gen.mkAttributedQualifier(pre), mod))
+ case _ => None
+ }
+ blockWithoutQualifier(dq)
+ case Select(TypeApply(New(tp @ TypeTree()), _), _) if isConstr =>
+ val dq = tp.tpe match {
+ case TypeRef(pre, _, _) if (!pre.typeSymbol.isEmptyPackageClass) =>
+ moduleQual(tp.pos, mod => gen.mkAttributedSelect(gen.mkAttributedQualifier(pre), mod))
+ case _ => None
+ }
+ blockWithoutQualifier(dq)
case Select(New(Ident(_)), _) if isConstr =>
blockWithoutQualifier(None)
@@ -350,8 +361,11 @@ trait NamesDefaults { self: Analyzer =>
if (missing forall (_.hasFlag(DEFAULTPARAM))) {
val defaultArgs = missing map (p => {
var default1 = qual match {
- case Some(q) => gen.mkAttributedSelect(q.duplicate, defaultGetter(p, context))
- case None => gen.mkAttributedRef(defaultGetter(p, context))
+ case Some(q) => gen.mkAttributedSelect(q.duplicate, defaultGetter(p, context)._1)
+ case None =>
+ val (m, q) = defaultGetter(p, context)
+ if (q.isDefined) gen.mkAttributedSelect(q.get, m)
+ else gen.mkAttributedRef(m)
}
default1 = if (targs.isEmpty) default1
else TypeApply(default1, targs.map(_.duplicate))
@@ -369,35 +383,32 @@ trait NamesDefaults { self: Analyzer =>
/**
* For a parameter with default argument, find the method symbol of
- * the default getter.
+ * the default getter. Can return a qualifier tree for the selecting
+ * the method's symbol (part of #3334 fix).
*/
- def defaultGetter(param: Symbol, context: Context) = {
+ def defaultGetter(param: Symbol, context: Context): (Symbol, Option[Tree]) = {
val i = param.owner.paramss.flatten.findIndexOf(p => p.name == param.name) + 1
if (i > 0) {
if (param.owner.isConstructor) {
val defGetterName = "init$default$"+ i
- param.owner.owner.companionModule.info.member(defGetterName)
+ var mod = param.owner.owner.companionModule
+ // if the class's owner is a method, .companionModule does not work
+ if (mod == NoSymbol)
+ mod = context.lookup(param.owner.owner.name.toTermName, param.owner.owner.owner)
+ (mod.info.member(defGetterName), Some(gen.mkAttributedRef(mod)))
} else {
val defGetterName = param.owner.name +"$default$"+ i
+ // isClass also works for methods in objects, owner is the ModuleClassSymbol
if (param.owner.owner.isClass) {
// .toInterface: otherwise we get the method symbol of the impl class
- param.owner.owner.toInterface.info.member(defGetterName)
+ (param.owner.owner.toInterface.info.member(defGetterName), None)
} else {
// the owner of the method is another method. find the default
// getter in the context.
- var res: Symbol = NoSymbol
- var ctx = context
- while(res == NoSymbol && ctx.outer != ctx) {
- val s = ctx.scope.lookup(defGetterName)
- if (s != NoSymbol && s.owner == param.owner.owner)
- res = s
- else
- ctx = ctx.outer
- }
- res
+ (context.lookup(defGetterName, param.owner.owner), None)
}
}
- } else NoSymbol
+ } else (NoSymbol, None)
}
/**
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 003a173892..bd8482cd67 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -971,17 +971,20 @@ abstract class RefChecks extends InfoTransform {
private def checkAnnotations(tpes: List[Type], pos: Position) = tpes foreach (tp => checkTypeRef(tp, pos))
private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f
- private def applyRefchecksToAnnotations(tree: Tree) = tree match {
- case m: MemberDef =>
- checkAnnotations(m.symbol.annotations map (_.atp), tree.pos)
- transformTrees(m.symbol.annotations.flatMap(_.args))
- case TypeTree() => doTypeTraversal(tree) {
- case AnnotatedType(annots, _, _) =>
- checkAnnotations(annots map (_.atp), tree.pos)
- transformTrees(annots.flatMap(_.args))
+ private def applyRefchecksToAnnotations(tree: Tree) = {
+ def applyChecks(annots: List[AnnotationInfo]) = {
+ checkAnnotations(annots map (_.atp), tree.pos)
+ transformTrees(annots flatMap (_.args))
+ }
+
+ tree match {
+ case m: MemberDef => applyChecks(m.symbol.annotations)
+ case TypeTree() => doTypeTraversal(tree) {
+ case AnnotatedType(annots, _, _) => applyChecks(annots)
+ case _ =>
+ }
case _ =>
}
- case _ =>
}
private def transformCaseApply(tree: Tree, ifNot: => Unit) = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index 6f5175c0ea..8a7f4b0958 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -54,7 +54,11 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
private def checkPackedConforms(tree: Tree, pt: Type): Tree = {
if (tree.tpe exists (_.typeSymbol.isExistentialSkolem)) {
val packed = localTyper.packedType(tree, NoSymbol)
- if (!(packed <:< pt)) localTyper.infer.typeError(tree.pos, packed, pt)
+ if (!(packed <:< pt)) {
+ val errorContext = localTyper.context.make(localTyper.context.tree)
+ errorContext.reportGeneralErrors = true
+ analyzer.newTyper(errorContext).infer.typeError(tree.pos, packed, pt)
+ }
}
tree
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
new file mode 100644
index 0000000000..1166f62ddb
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -0,0 +1,268 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package typechecker
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+import scala.util.control.ControlThrowable
+import scala.util.control.Exception.ultimately
+import symtab.Flags._
+import PartialFunction._
+
+/** An interface to enable higher configurability of diagnostic messages
+ * regarding type errors. This is barely a beginning as error messages are
+ * distributed far and wide across the codebase. The plan is to partition
+ * error messages into some broad groups and provide some mechanism for
+ * being more or less verbose on a selective basis. Possible groups include
+ * such examples as
+ *
+ * arity errors
+ * kind errors
+ * variance errors
+ * ambiguity errors
+ * volatility/stability errors
+ * implementation restrictions
+ *
+ * And more, and there is plenty of overlap, so it'll be a process.
+ *
+ * @author Paul Phillips
+ * @version 1.0
+ */
+trait TypeDiagnostics {
+ self: Analyzer =>
+
+ import global._
+ import definitions._
+ import global.typer.infer
+
+ /** It can be quite difficult to know which of the many functions called "error"
+ * is being called at any given point in the compiler. To alleviate this I am
+ * renaming such functions inside this trait based on where it originated.
+ */
+ def inferError(pos: Position, msg: String) = infer.error(pos, msg)
+
+ /** The common situation of making sure nothing is erroneous could be
+ * nicer if Symbols, Types, and Trees all implemented some common interface
+ * in which isErroneous and similar would be placed.
+ */
+ def noErroneousTypes(tps: Type*) = tps forall (x => !x.isErroneous)
+ def noErroneousSyms(syms: Symbol*) = syms forall (x => !x.isErroneous)
+ def noErroneousTrees(trees: Tree*) = trees forall (x => !x.isErroneous)
+
+ /** A map of Positions to addendums - if an error involves a position in
+ * the map, the addendum should also be printed.
+ */
+ private var addendums = mutable.Map[Position, () => String]()
+
+ def setAddendum(pos: Position, msg: () => String) =
+ if (pos != NoPosition)
+ addendums(pos) = msg
+
+ def withAddendum(pos: Position) = (_: String) + addendums.getOrElse(pos, () => "")()
+
+ def decodeWithNamespace(name: Name): String = {
+ val prefix = if (name.isTypeName) "type " else "value "
+ prefix + name.decode
+ }
+
+ /** Does the positioned line assigned to t1 precede that of t2?
+ */
+ def linePrecedes(t1: Tree, t2: Tree) = t1.pos.isDefined && t1.pos.isDefined && t1.pos.line < t2.pos.line
+
+ def notAMember(sel: Tree, qual: Tree, name: Name) = {
+ def decoded = decodeWithNamespace(name)
+
+ def msg: String = name match {
+ case nme.CONSTRUCTOR => qual.tpe.widen+" does not have a constructor"
+ case _ =>
+ def memberOf = if (qual.tpe.typeSymbol.isTypeParameterOrSkolem) "type parameter " else ""
+ def possibleCause =
+ if (linePrecedes(qual, sel))
+ "\npossible cause: maybe a semicolon is missing before `"+decoded+"'?"
+ else
+ ""
+
+ decoded+" is not a member of "+ memberOf + qual.tpe.widen + possibleCause
+ }
+ inferError(sel.pos, withAddendum(qual.pos)(msg))
+ }
+
+ /** Only prints the parameter names if they're not synthetic,
+ * since "x$1: Int" does not offer any more information than "Int".
+ */
+ private def methodTypeErrorString(tp: Type) = tp match {
+ case mt @ MethodType(params, resultType) =>
+ def forString =
+ if (params exists (_.isSynthetic)) params map (_.tpe)
+ else params map (_.defString)
+
+ forString.mkString("(", ",", ")") + resultType
+ case x => x.toString
+ }
+
+ def alternatives(tree: Tree): List[Type] = tree.tpe match {
+ case OverloadedType(pre, alternatives) => alternatives map pre.memberType
+ case _ => Nil
+ }
+ def alternativesString(tree: Tree) =
+ alternatives(tree) map (x => " " + methodTypeErrorString(x)) mkString ("", " <and>\n", "\n")
+
+ def missingParameterTypeError(fun: Tree, vparam: ValDef) = {
+ val suffix = if (vparam.mods.isSynthetic) " for expanded function "+fun else ""
+
+ inferError(vparam.pos, "missing parameter type" + suffix)
+ ErrorType
+ }
+
+ def treeSymTypeMsg(tree: Tree): String = {
+ val sym = tree.symbol
+ def hasParams = tree.tpe.paramSectionCount > 0
+ def preResultString = if (hasParams) ": " else " of type "
+
+ def nullMessage = "expression of type " + tree.tpe
+ def overloadedMessage = "overloaded method " + sym + " with alternatives:\n" + alternativesString(tree)
+ def moduleMessage = "" + sym
+ def defaultMessage = moduleMessage + preResultString + tree.tpe
+ def applyMessage = defaultMessage + tree.symbol.locationString
+
+ if (sym == null) nullMessage
+ else if (sym.isOverloaded) overloadedMessage
+ else if (sym.isModule) moduleMessage
+ else if (sym.name == nme.apply) applyMessage
+ else defaultMessage
+ }
+
+ def applyErrorMsg(tree: Tree, msg: String, argtpes: List[Type], pt: Type) = {
+ def asParams(xs: List[Any]) = xs.mkString("(", ", ", ")")
+
+ def resType = if (isWildcard(pt)) "" else " with expected result type " + pt
+ def allTypes = (alternatives(tree) flatMap (_.paramTypes)) ++ argtpes :+ pt
+
+ withDisambiguation(allTypes: _*) {
+ treeSymTypeMsg(tree) + msg + asParams(argtpes) + resType
+ }
+ }
+
+ def disambiguate(ss: List[String]) = ss match {
+ case Nil => Nil
+ case s :: ss => s :: (ss map { case `s` => "(some other)"+s ; case x => x })
+ }
+
+ // todo: use also for other error messages
+ def existentialContext(tp: Type) = tp.existentialSkolems match {
+ case Nil => ""
+ case xs => " where " + (disambiguate(xs map (_.existentialToString)) mkString ", ")
+ }
+
+ def foundReqMsg(found: Type, req: Type): String =
+ withDisambiguation(found, req) {
+ ";\n found : " + found.toLongString + existentialContext(found) +
+ "\n required: " + req + existentialContext(req)
+ }
+
+ /** If two given types contain different type variables with the same name
+ * differentiate the names by including owner information. Also, if the
+ * type error is because of a conflict between two identically named
+ * classes and one is in package scala, fully qualify the name so one
+ * need not deduce why "java.util.Iterator" and "Iterator" don't match.
+ * Another disambiguation performed is to address the confusion present
+ * in the following snippet:
+ * def f[Int](x: Int) = x + 5.
+ */
+ def withDisambiguation[T](types: Type*)(op: => T): T = {
+ object SymExtractor {
+ def unapply(x: Any) = x match {
+ case t @ TypeRef(_, sym, _) => Some(t -> sym)
+ case t @ ConstantType(value) => Some(t -> t.underlying.typeSymbol)
+ case _ => None
+ }
+ }
+ val typerefs =
+ for (tp <- types.toList ; SymExtractor(t, sym) <- tp) yield
+ t -> sym
+
+ val savedNames = typerefs map { case (_, sym) => sym -> sym.name } toMap
+ def restoreNames = savedNames foreach { case (sym, name) => sym.name = name }
+
+ def isAlreadyAltered(sym: Symbol) = sym.name != savedNames(sym)
+
+ def modifyName(sym: Symbol)(f: String => String): Unit =
+ sym.name = newTypeName(f(sym.name.toString))
+
+ def scalaQualify(sym: Symbol) =
+ if (sym.owner.isScalaPackageClass)
+ modifyName(sym)("scala." + _)
+
+ def explainName(sym: Symbol) = {
+ scalaQualify(sym)
+
+ if (!isAlreadyAltered(sym))
+ modifyName(sym)(_ + "(in " + sym.owner + ")")
+ }
+
+ ultimately(restoreNames) {
+ for ((t1, sym1) <- typerefs ; (t2, sym2) <- typerefs ; if sym1 != sym2 && (sym1 isLess sym2)) {
+
+ if (t1.toString == t2.toString) { // type variable collisions
+ List(sym1, sym2) foreach explainName
+ if (sym1.owner == sym2.owner)
+ sym2.name = newTypeName("(some other)"+sym2.name)
+ }
+ else if (sym1.name == sym2.name) { // symbol name collisions
+ List(sym1, sym2) foreach { x =>
+ if (x.owner.isScalaPackageClass)
+ modifyName(x)("scala." + _)
+ else if (x.isTypeParameterOrSkolem)
+ explainName(x)
+ }
+ }
+ }
+
+ // performing the actual operation
+ op
+ }
+ }
+
+ trait TyperDiagnostics {
+ self: Typer =>
+
+ private def contextError(pos: Position, msg: String) = context.error(pos, msg)
+ private def contextError(pos: Position, err: Throwable) = context.error(pos, err)
+
+ def symWasOverloaded(sym: Symbol) = sym.owner.isClass && sym.owner.info.member(sym.name).isOverloaded
+ def cyclicAdjective(sym: Symbol) = if (symWasOverloaded(sym)) "overloaded" else "recursive"
+
+ /** Returns Some(msg) if the given tree is untyped apparently due
+ * to a cyclic reference, and None otherwise.
+ */
+ def cyclicReferenceMessage(sym: Symbol, tree: Tree) = condOpt(tree) {
+ case ValDef(_, _, tpt, _) if tpt.tpe == null => "recursive "+sym+" needs type"
+ case DefDef(_, _, _, _, tpt, _) if tpt.tpe == null => List(cyclicAdjective(sym), sym, "needs result type") mkString " "
+ }
+
+ /** Report a type error.
+ *
+ * @param pos0 The position where to report the error
+ * @param ex The exception that caused the error
+ */
+ def reportTypeError(pos: Position, ex: TypeError) {
+ if (ex.pos == NoPosition) ex.pos = pos
+ if (!context.reportGeneralErrors) throw ex
+ if (settings.debug.value) ex.printStackTrace()
+
+ ex match {
+ case CyclicReference(sym, info: TypeCompleter) =>
+ contextError(ex.pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage())
+
+ if (sym == ObjectClass)
+ throw new FatalError("cannot redefine root "+sym)
+ case _ =>
+ contextError(ex.pos, ex)
+ }
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 3fe937f241..92dd368ac5 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -161,7 +161,7 @@ trait Typers { self: Analyzer =>
else mode
}
- abstract class Typer(context0: Context) {
+ abstract class Typer(context0: Context) extends TyperDiagnostics {
import context0.unit
val infer = new Inferencer(context0) {
@@ -252,35 +252,6 @@ trait Typers { self: Analyzer =>
private[typechecker] var context = context0
def context1 = context
- /** Report a type error.
- *
- * @param pos0 The position where to report the error
- * @param ex The exception that caused the error
- */
- def reportTypeError(pos: Position, ex: TypeError) {
- if (ex.pos == NoPosition) ex.pos = pos
- if (!context.reportGeneralErrors) throw ex
- if (settings.debug.value) ex.printStackTrace()
- ex match {
- case CyclicReference(sym, info: TypeCompleter) =>
- val msg =
- info.tree match {
- case ValDef(_, _, tpt, _) if (tpt.tpe eq null) =>
- "recursive "+sym+" needs type"
- case DefDef(_, _, _, _, tpt, _) if (tpt.tpe eq null) =>
- (if (sym.owner.isClass && sym.owner.info.member(sym.name).hasFlag(OVERLOADED)) "overloaded "
- else "recursive ")+sym+" needs result type"
- case _ =>
- ex.getMessage()
- }
- context.error(ex.pos, msg)
- if (sym == ObjectClass)
- throw new FatalError("cannot redefine root "+sym)
- case _ =>
- context.error(ex.pos, ex)
- }
- }
-
/** Check that <code>tree</code> is a stable expression.
*
* @param tree ...
@@ -397,7 +368,7 @@ trait Typers { self: Analyzer =>
error(pos, "methods with `*'-parameters cannot be converted to function values");
*/
if (restpe.isDependent)
- error(pos, "method with dependent type "+tpe+" cannot be converted to function value");
+ error(pos, "method with dependent type "+tpe+" cannot be converted to function value")
checkParamsConvertible(pos, restpe)
case _ =>
}
@@ -1230,7 +1201,7 @@ trait Typers { self: Analyzer =>
if (!ps.isEmpty && !superclazz.isSubClass(ps.head.typeSymbol))
error(parent.pos, "illegal inheritance; super"+superclazz+
"\n is not a subclass of the super"+ps.head.typeSymbol+
- "\n of the mixin " + psym);
+ "\n of the mixin " + psym)
} else {
error(parent.pos, psym+" needs to be a trait to be mixed in")
}
@@ -1352,6 +1323,7 @@ trait Typers { self: Analyzer =>
*/
def addGetterSetter(stat: Tree): List[Tree] = stat match {
case ValDef(mods, name, tpt, rhs)
+ // PRIVATE | LOCAL are fields generated for primary constructor arguments
if (mods.flags & (PRIVATE | LOCAL)) != (PRIVATE | LOCAL).toLong && !stat.symbol.isModuleVar =>
val isDeferred = mods hasFlag DEFERRED
@@ -1592,7 +1564,7 @@ trait Typers { self: Analyzer =>
* @param rhs ...
*/
def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) {
- if (settings.debug.value) log("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs);//debug
+ if (settings.debug.value) log("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs)//debug
def decompose(call: Tree): (Tree, List[Tree]) = call match {
case Apply(fn, args) =>
val (superConstr, args1) = decompose(fn)
@@ -1600,7 +1572,7 @@ trait Typers { self: Analyzer =>
val args2 = if (params.isEmpty || !isRepeatedParamType(params.last.tpe)) args
else args.take(params.length - 1) ::: List(EmptyTree)
if (args2.length != params.length)
- assert(false, "mismatch " + clazz + " " + (params map (_.tpe)) + " " + args2);//debug
+ assert(false, "mismatch " + clazz + " " + (params map (_.tpe)) + " " + args2)//debug
(superConstr, args1 ::: args2)
case Block(stats, expr) if !stats.isEmpty =>
decompose(stats.last)
@@ -1639,7 +1611,7 @@ trait Typers { self: Analyzer =>
ownAcc = ownAcc.accessed
if (!ownAcc.isVariable && !alias.accessed.isVariable) {
if (settings.debug.value)
- log("" + ownAcc + " has alias "+alias + alias.locationString);//debug
+ log("" + ownAcc + " has alias "+alias + alias.locationString) //debug
ownAcc.asInstanceOf[TermSymbol].setAlias(alias)
}
}
@@ -1676,77 +1648,6 @@ trait Typers { self: Analyzer =>
}
}
- /** does given name name an identifier visible at this point?
- *
- * @param name the given name
- * @return <code>true</code> if an identifier with the given name is visible.
- */
- def namesSomeIdent(name: Name): Boolean = namesWhatIdent(name).isDefined
-
- /** If this name returns a visible identifier, return its symbol.
- *
- * @param name the given name
- * @return <code>Some(sym)</code> if an ident is visible, None otherwise.
- */
- def namesWhatIdent(name: Name): Option[Symbol] = {
- var cx = context
- while (cx != NoContext) {
- val pre = cx.enclClass.prefix
- val defEntry = cx.scope.lookupEntry(name)
- if ((defEntry ne null) && reallyExists(defEntry.sym))
- return Some(defEntry.sym)
-
- cx = cx.enclClass
- (pre member name filter (sym => reallyExists(sym) && context.isAccessible(sym, pre, false))) match {
- case NoSymbol => cx = cx.outer
- case other => return Some(other)
- }
- }
- context.imports map (_ importedSymbol name) find (_ != NoSymbol)
- }
-
- /** Does this tree declare a val or def with the same name as one in scope?
- * This only catches identifiers in the same file, so more work is needed.
- *
- * @param tree the given tree
- * @param filt filter for any conflicting symbols found -- false means ignore
- */
- def checkShadowings(tree: Tree, filt: (Symbol) => Boolean = _ => true) {
- def sameFile(other: Symbol) =
- (tree.symbol != null) && tree.symbol.sourceFile == other.sourceFile
- def inFile(other: Symbol) =
- if (sameFile(other)) ""
- else if (other.sourceFile != null) "in %s ".format(other.sourceFile)
- else ""
-
- def positionStr(other: Symbol) = other.pos match {
- case NoPosition => inFile(other) match { case "" => "(location unknown) " ; case x => x }
- case pos => "%sat line %s\n%s".format(inFile(other), pos.line, pos.lineContent) + """ /* is shadowed by */"""
- }
- def include(v: ValOrDefDef, other: Symbol) = {
- // shadowing on the same line is a good bet for noise
- (v.pos == NoPosition || other.pos == NoPosition || v.pos.line != other.pos.line) &&
- // not likely we'll shadow a whole package without realizing it
- !other.isPackage &&
- // (v.symbol == null || !v.symbol.hasTransOwner(other)) &&
- filt(other)
- }
-
- tree match {
- // while I try to figure out how to limit the noise far enough to make this
- // genuinely useful, I'm setting minimum identifier length to 3 to omit all
- // those x's and i's we so enjoy reusing.
- case v: ValOrDefDef if v.name.toString.length > 2 =>
- namesWhatIdent(v.name) map { other =>
- if (include(v, other) && unit != null) {
- val fstr = "%s (%s) shadows usage %s"
- unit.warning(v.pos, fstr.format(v.name, v.tpt, positionStr(other)))
- }
- }
- case _ =>
- }
- }
-
def typedUseCase(useCase: UseCase) {
def stringParser(str: String): syntaxAnalyzer.Parser = {
val file = new BatchSourceFile(context.unit.source.file, str) {
@@ -1793,15 +1694,6 @@ trait Typers { self: Analyzer =>
def typedDefDef(ddef: DefDef): DefDef = {
val meth = ddef.symbol
- // If warnings are enabled, attempt to alert about variable shadowing. This only
- // catches method parameters shadowing identifiers declared in the same file, so more
- // work is needed. Most of the code here is to filter out false positives.
- def isAuxConstructor(sym: Symbol) = sym.isConstructor && !sym.isPrimaryConstructor
- if (settings.YwarnShadow.value && !isAuxConstructor(ddef.symbol)) {
- for (v <- ddef.vparamss.flatten ; if v.symbol != null && !(v.symbol hasFlag SYNTHETIC))
- checkShadowings(v, (sym => !sym.isDeferred && !sym.isMethod))
- }
-
reenterTypeParams(ddef.tparams)
reenterValueParams(ddef.vparamss)
@@ -1933,7 +1825,6 @@ trait Typers { self: Analyzer =>
while ((e ne null) && (e.sym ne stat.symbol)) e = e.tail
if (e eq null) context.scope.enter(stat.symbol)
}
- if (settings.YwarnShadow.value) checkShadowings(stat)
enterLabelDef(stat)
}
if (phaseId(currentPeriod) <= currentRun.typerPhase.id) {
@@ -2155,18 +2046,18 @@ trait Typers { self: Analyzer =>
!(accessed hasFlag ACCESSOR) && accessed.isPrivateLocal
def checkNoDoubleDefsAndAddSynthetics(stats: List[Tree]): List[Tree] = {
- val scope = if (inBlock) context.scope else context.owner.info.decls;
+ val scope = if (inBlock) context.scope else context.owner.info.decls
val newStats = new ListBuffer[Tree]
var needsCheck = true
var moreToAdd = true
while (moreToAdd) {
val initSize = scope.size
- var e = scope.elems;
+ var e = scope.elems
while ((e ne null) && e.owner == scope) {
// check no double def
if (needsCheck) {
- var e1 = scope.lookupNextEntry(e);
+ var e1 = scope.lookupNextEntry(e)
while ((e1 ne null) && e1.owner == scope) {
if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) &&
(e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe)))
@@ -2394,7 +2285,8 @@ trait Typers { self: Analyzer =>
val (allArgs, missing) = addDefaults(args, qual, targs, previousArgss, params, fun.pos.focus, context)
if (allArgs.length == formals.length) {
// useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]()
- context.diagnostic = "Error occured in an application involving default arguments." :: context.diagnostic
+ val note = "Error occurred in an application involving default arguments."
+ if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic
doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)
} else {
tryTupleApply.getOrElse {
@@ -2466,7 +2358,7 @@ trait Typers { self: Analyzer =>
inferExprInstance(fun, tparams, WildcardType, true)
doTypedApply(tree, fun, args, mode, pt)
} else {
- assert((mode & PATTERNmode) == 0); // this case cannot arise for patterns
+ assert((mode & PATTERNmode) == 0) // this case cannot arise for patterns
val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt)
val strictTargs = (lenientTargs, tparams).zipped map ((targ, tparam) =>
if (targ == WildcardType) tparam.tpe else targ) //@M TODO: should probably be .tpeHK
@@ -2490,7 +2382,7 @@ trait Typers { self: Analyzer =>
val args1 = (args, formals).zipped map typedArgToPoly
if (args1 exists (_.tpe.isError)) setError(tree)
else {
- if (settings.debug.value) log("infer method inst "+fun+", tparams = "+tparams+", args = "+args1.map(_.tpe)+", pt = "+pt+", lobounds = "+tparams.map(_.tpe.bounds.lo)+", parambounds = "+tparams.map(_.info));//debug
+ if (settings.debug.value) log("infer method inst "+fun+", tparams = "+tparams+", args = "+args1.map(_.tpe)+", pt = "+pt+", lobounds = "+tparams.map(_.tpe.bounds.lo)+", parambounds = "+tparams.map(_.info)) //debug
// define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun"
// returns those undetparams which have not been instantiated.
val undetparams = inferMethodInstance(fun, tparams, args1, pt)
@@ -3016,6 +2908,8 @@ trait Typers { self: Analyzer =>
* @return ...
*/
protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
+ def isPatternMode = (mode & PATTERNmode) != 0
+
//Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")")
def ptOrLub(tps: List[Type]) = if (isFullyDefined(pt)) (pt, false) else weakLub(tps map (_.deconst))
@@ -3114,13 +3008,6 @@ trait Typers { self: Analyzer =>
if (vble == NoSymbol)
vble = context.owner.newValue(tree.pos, name)
if (vble.name.toTermName != nme.WILDCARD) {
-/*
- if (namesSomeIdent(vble.name))
- context.warning(tree.pos,
- "pattern variable"+vble.name+" shadows a value visible in the environment;\n"+
- "use backquotes `"+vble.name+"` if you mean to match against that value;\n" +
- "or rename the variable or use an explicit bind "+vble.name+"@_ to avoid this warning.")
-*/
if ((mode & ALTmode) != 0)
error(tree.pos, "illegal variable in pattern alternative")
vble = namer.enterInScope(vble)
@@ -3143,32 +3030,36 @@ trait Typers { self: Analyzer =>
}
def typedAssign(lhs: Tree, rhs: Tree): Tree = {
- val lhs1 = typed(lhs, EXPRmode | LHSmode, WildcardType)
- val varsym = lhs1.symbol
- if ((varsym ne null) && treeInfo.mayBeVarGetter(varsym))
+ val lhs1 = typed(lhs, EXPRmode | LHSmode, WildcardType)
+ val varsym = lhs1.symbol
+ def failMsg =
+ if (varsym != null && varsym.isValue) "reassignment to val"
+ else "assignment to non variable"
+
+ def fail = {
+ if (!lhs1.tpe.isError)
+ error(tree.pos, failMsg)
+
+ setError(tree)
+ }
+ if (varsym == null)
+ return fail
+
+ if (treeInfo.mayBeVarGetter(varsym)) {
lhs1 match {
case Select(qual, name) =>
- return typed(
- Apply(
- Select(qual, nme.getterToSetter(name)) setPos lhs.pos,
- List(rhs)) setPos tree.pos,
- mode, pt)
+ val sel = Select(qual, nme.getterToSetter(name)) setPos lhs.pos
+ val app = Apply(sel, List(rhs)) setPos tree.pos
+ return typed(app, mode, pt)
case _ =>
-
}
- if ((varsym ne null) && (varsym.isVariable || varsym.isValue && phase.erasedTypes)) {
+ }
+ if (varsym.isVariable || varsym.isValue && phase.erasedTypes) {
val rhs1 = typed(rhs, EXPRmode | BYVALmode, lhs1.tpe)
treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitClass.tpe
- } else {
- if (!lhs1.tpe.isError) {
- //println(lhs1+" = "+rhs+" "+varsym+" "+mayBeVarGetter(varsym)+" "+varsym.ownerChain+" "+varsym.info)// DEBUG
- error(tree.pos,
- if ((varsym ne null) && varsym.isValue) "reassignment to val"
- else "assignment to non variable")
- }
- setError(tree)
}
+ else fail
}
def typedIf(cond: Tree, thenp: Tree, elsep: Tree) = {
@@ -3269,7 +3160,7 @@ trait Typers { self: Analyzer =>
pt != WildcardType &&
pt != ErrorType &&
isSubType(pt, DelegateClass.tpe)) =>
- val scalaCaller = newScalaCaller(pt);
+ val scalaCaller = newScalaCaller(pt)
addScalaCallerInfo(scalaCaller, expr1.symbol)
val n: Name = scalaCaller.name
val del = Ident(DelegateClass) setType DelegateClass.tpe
@@ -3277,7 +3168,7 @@ trait Typers { self: Analyzer =>
//val f1 = TypeApply(f, List(Ident(pt.symbol) setType pt))
val args: List[Tree] = if(expr1.symbol.isStatic) List(Literal(Constant(null)))
else List(qual) // where the scala-method is located
- val rhs = Apply(f, args);
+ val rhs = Apply(f, args)
typed(rhs)
case _ =>
adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType))
@@ -3314,19 +3205,19 @@ trait Typers { self: Analyzer =>
t
case ex: TypeError =>
stopTimer(failedApplyNanos, start)
- def errorInResult(tree: Tree): Boolean = tree.pos == ex.pos || {
- tree match {
- case Block(_, r) => errorInResult(r)
- case Match(_, cases) => cases exists errorInResult
- case CaseDef(_, _, r) => errorInResult(r)
- case Annotated(_, r) => errorInResult(r)
- case If(_, t, e) => errorInResult(t) || errorInResult(e)
- case Try(b, catches, _) => errorInResult(b) || (catches exists errorInResult)
- case Typed(r, Function(List(), EmptyTree)) => errorInResult(r)
- case _ => false
- }
- }
- if (errorInResult(fun) || (args exists errorInResult) || errorInResult(tree)) {
+ def treesInResult(tree: Tree): List[Tree] = tree :: (tree match {
+ case Block(_, r) => treesInResult(r)
+ case Match(_, cases) => cases
+ case CaseDef(_, _, r) => treesInResult(r)
+ case Annotated(_, r) => treesInResult(r)
+ case If(_, t, e) => treesInResult(t) ++ treesInResult(e)
+ case Try(b, catches, _) => treesInResult(b) ++ catches
+ case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r)
+ case _ => Nil
+ })
+ def errorInResult(tree: Tree) = treesInResult(tree) exists (_.pos == ex.pos)
+
+ if (fun :: tree :: args exists errorInResult) {
if (printTypings) println("second try for: "+fun+" and "+args)
val Select(qual, name) = fun
val args1 = tryTypedArgs(args, argMode(fun, mode), ex)
@@ -3347,11 +3238,11 @@ trait Typers { self: Analyzer =>
def typedApply(fun: Tree, args: List[Tree]) = {
val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable
- if (stableApplication && (mode & PATTERNmode) != 0) {
+ if (stableApplication && isPatternMode) {
// treat stable function applications f() as expressions.
typed1(tree, mode & ~PATTERNmode | EXPRmode, pt)
} else {
- val funpt = if ((mode & PATTERNmode) != 0) pt else WildcardType
+ val funpt = if (isPatternMode) pt else WildcardType
val appStart = startTimer(failedApplyNanos)
val opeqStart = startTimer(failedOpEqNanos)
silent(_.typed(fun, funMode(mode), funpt)) match {
@@ -3392,7 +3283,7 @@ trait Typers { self: Analyzer =>
case ex: TypeError =>
fun match {
case Select(qual, name)
- if (mode & PATTERNmode) == 0 && nme.isOpAssignmentName(name.decode) =>
+ if !isPatternMode && nme.isOpAssignmentName(name.decode) =>
val qual1 = typedQualifier(qual)
if (treeInfo.isVariableOrGetter(qual1)) {
stopTimer(failedOpEqNanos, opeqStart)
@@ -3422,31 +3313,41 @@ trait Typers { self: Analyzer =>
Apply(
Select(vble.duplicate, prefix) setPos fun.pos.focus, args) setPos tree.pos.makeTransparent
) setPos tree.pos
+
+ def mkUpdate(table: Tree, indices: List[Tree]) = {
+ gen.evalOnceAll(table :: indices, context.owner, context.unit) { ts =>
+ val tab = ts.head
+ val is = ts.tail
+ Apply(
+ Select(tab(), nme.update) setPos table.pos,
+ ((is map (i => i())) ::: List(
+ Apply(
+ Select(
+ Apply(
+ Select(tab(), nme.apply) setPos table.pos,
+ is map (i => i())) setPos qual.pos,
+ prefix) setPos fun.pos,
+ args) setPos tree.pos)
+ )
+ ) setPos tree.pos
+ }
+ }
+
val tree1 = qual match {
+ case Ident(_) =>
+ mkAssign(qual)
+
case Select(qualqual, vname) =>
gen.evalOnce(qualqual, context.owner, context.unit) { qq =>
val qq1 = qq()
mkAssign(Select(qq1, vname) setPos qual.pos)
}
- case Apply(Select(table, nme.apply), indices) =>
- gen.evalOnceAll(table :: indices, context.owner, context.unit) { ts =>
- val tab = ts.head
- val is = ts.tail
- Apply(
- Select(tab(), nme.update) setPos table.pos,
- ((is map (i => i())) ::: List(
- Apply(
- Select(
- Apply(
- Select(tab(), nme.apply) setPos table.pos,
- is map (i => i())) setPos qual.pos,
- prefix) setPos fun.pos,
- args) setPos tree.pos)
- )
- ) setPos tree.pos
- }
- case Ident(_) =>
- mkAssign(qual)
+
+ case Apply(fn, indices) =>
+ treeInfo.methPart(fn) match {
+ case Select(table, nme.apply) => mkUpdate(table, indices)
+ case _ => errorTree(qual, "Unexpected tree during assignment conversion.")
+ }
}
typed1(tree1, mode, pt)
/*
@@ -3559,19 +3460,9 @@ trait Typers { self: Analyzer =>
if (name == nme.ERROR && onlyPresentation)
return makeErrorTree
- if (!qual.tpe.widen.isErroneous) {
- error(tree.pos,
- if (name == nme.CONSTRUCTOR)
- qual.tpe.widen+" does not have a constructor"
- else
- decode(name)+" is not a member of "+
- (if (qual.tpe.typeSymbol.isTypeParameterOrSkolem) "type parameter " else "") +
- qual.tpe.widen +
- (if ((context.unit ne null) && // Martin: why is this condition needed?
- qual.pos.isDefined && tree.pos.isDefined && qual.pos.line < tree.pos.line)
- "\npossible cause: maybe a semicolon is missing before `"+decode(name)+"'?"
- else ""))
- }
+ if (!qual.tpe.widen.isErroneous)
+ notAMember(tree, qual, name)
+
if (onlyPresentation) makeErrorTree else setError(tree)
} else {
val tree1 = tree match {
@@ -3647,8 +3538,8 @@ trait Typers { self: Analyzer =>
val symDepth = if (defEntry eq null) cx.depth
else cx.depth - (cx.scope.nestingLevel - defEntry.owner.nestingLevel)
- var impSym: Symbol = NoSymbol; // the imported symbol
- var imports = context.imports; // impSym != NoSymbol => it is imported from imports.head
+ var impSym: Symbol = NoSymbol // the imported symbol
+ var imports = context.imports // impSym != NoSymbol => it is imported from imports.head
while (!reallyExists(impSym) && !imports.isEmpty && imports.head.depth > symDepth) {
impSym = imports.head.importedSymbol(name)
if (!impSym.exists) imports = imports.tail
@@ -3711,7 +3602,7 @@ trait Typers { self: Analyzer =>
if (settings.debug.value) {
log(context.imports)//debug
}
- error(tree.pos, "not found: "+decode(name))
+ error(tree.pos, "not found: "+decodeWithNamespace(name))
defSym = context.owner.newErrorSymbol(name)
}
}
@@ -3779,7 +3670,7 @@ trait Typers { self: Analyzer =>
errorTree(tree, tpt1.tpe+" does not take type parameters")
} else {
//Console.println("\{tpt1}:\{tpt1.symbol}:\{tpt1.symbol.info}")
- if (settings.debug.value) Console.println(tpt1+":"+tpt1.symbol+":"+tpt1.symbol.info);//debug
+ if (settings.debug.value) Console.println(tpt1+":"+tpt1.symbol+":"+tpt1.symbol.info)//debug
errorTree(tree, "wrong number of type arguments for "+tpt1.tpe+", should be "+tparams.length)
}
}
@@ -3948,7 +3839,7 @@ trait Typers { self: Analyzer =>
val tpt1 = typedType(tpt, mode)
val expr1 = typed(expr, mode & stickyModes, tpt1.tpe.deconst)
val owntype =
- if ((mode & PATTERNmode) != 0) inferTypedPattern(tpt1.pos, tpt1.tpe, pt)
+ if (isPatternMode) inferTypedPattern(tpt1.pos, tpt1.tpe, pt)
else tpt1.tpe
//Console.println(typed pattern: "+tree+":"+", tp = "+tpt1.tpe+", pt = "+pt+" ==> "+owntype)//DEBUG
treeCopy.Typed(tree, expr1, tpt1) setType owntype
@@ -4146,15 +4037,15 @@ trait Typers { self: Analyzer =>
tree.tpe = null
if (tree.hasSymbol) tree.symbol = NoSymbol
}
- if (printTypings) println("typing "+tree+", pt = "+pt+", undetparams = "+context.undetparams+", implicits-enabled = "+context.implicitsEnabled+", silent = "+context.reportGeneralErrors); //DEBUG
+ if (printTypings) println("typing "+tree+", pt = "+pt+", undetparams = "+context.undetparams+", implicits-enabled = "+context.implicitsEnabled+", silent = "+context.reportGeneralErrors) //DEBUG
var tree1 = if (tree.tpe ne null) tree else typed1(tree, mode, dropExistential(pt))
- if (printTypings) println("typed "+tree1+":"+tree1.tpe+(if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")+", undetparams = "+context.undetparams+", pt = "+pt); //DEBUG
+ if (printTypings) println("typed "+tree1+":"+tree1.tpe+(if (isSingleType(tree1.tpe)) " with underlying "+tree1.tpe.widen else "")+", undetparams = "+context.undetparams+", pt = "+pt) //DEBUG
tree1.tpe = addAnnotations(tree1, tree1.tpe)
val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree)
- if (printTypings) println("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams); //DEBUG
+ if (printTypings) println("adapted "+tree1+":"+tree1.tpe.widen+" to "+pt+", "+context.undetparams) //DEBUG
// for (t <- tree1.tpe) assert(t != WildcardType)
// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe)
if (phase.id <= currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result)
@@ -4162,7 +4053,7 @@ trait Typers { self: Analyzer =>
} catch {
case ex: TypeError =>
tree.tpe = null
- if (printTypings) println("caught "+ex+" in typed: "+tree);//DEBUG
+ if (printTypings) println("caught "+ex+" in typed: "+tree) //DEBUG
reportTypeError(tree.pos, ex)
setError(tree)
case ex: Exception =>
@@ -4170,7 +4061,7 @@ trait Typers { self: Analyzer =>
Console.println("exception when typing "+tree+", pt = "+pt)
if ((context ne null) && (context.unit ne null) &&
(context.unit.source ne null) && (tree ne null))
- logError("AT: " + (tree.pos).dbgString, ex);
+ logError("AT: " + (tree.pos).dbgString, ex)
throw ex
}
finally {
diff --git a/src/compiler/scala/tools/nsc/util/Chars.scala b/src/compiler/scala/tools/nsc/util/Chars.scala
index 5a64f36eb4..562806b6eb 100755
--- a/src/compiler/scala/tools/nsc/util/Chars.scala
+++ b/src/compiler/scala/tools/nsc/util/Chars.scala
@@ -34,6 +34,17 @@ object Chars {
-1
}
+ /** Convert a character to a backslash-u escape */
+ def char2uescape(c: Char): String = {
+ var rest = c.toInt
+ val buf = new StringBuilder
+ for (i <- 1 to 4) {
+ buf ++= (rest % 16).toHexString
+ rest = rest / 16
+ }
+ "\\u" + buf.toString.reverse
+ }
+
/** Is character a line break? */
@inline def isLineBreakChar(c: Char) = (c: @switch) match {
case LF|FF|CR|SU => true
diff --git a/src/compiler/scala/tools/nsc/util/ShowPickled.scala b/src/compiler/scala/tools/nsc/util/ShowPickled.scala
index 740e42192b..b87d603570 100644
--- a/src/compiler/scala/tools/nsc/util/ShowPickled.scala
+++ b/src/compiler/scala/tools/nsc/util/ShowPickled.scala
@@ -13,6 +13,7 @@ import java.lang.Long.toHexString
import java.lang.Float.intBitsToFloat
import java.lang.Double.longBitsToDouble
+import cmd.program.Simple
import symtab.{ Flags, Names }
import scala.reflect.generic.{ PickleBuffer, PickleFormat }
import interpreter.ByteCode.scalaSigBytesForPath
@@ -288,15 +289,23 @@ object ShowPickled extends Names {
printFile(pickle, Console.out, bare)
}
+ private lazy val ShowPickledSpec =
+ Simple(
+ Simple.scalaProgramInfo("showPickled", "Usage: showPickled [--bare] <classname>"),
+ List("--bare" -> "suppress numbers in output"),
+ Nil,
+ null
+ )
+
/** Option --bare suppresses numbers so the output can be diffed.
*/
def main(args: Array[String]) {
- val parsed = cmd.CommandLine(args.toList, List("--bare"), Nil)
- def isBare = parsed isSet "--bare"
+ val runner = ShowPickledSpec instance args
+ import runner._
- parsed.residualArgs foreach { arg =>
+ residualArgs foreach { arg =>
(fromFile(arg) orElse fromName(arg)) match {
- case Some(pb) => show(arg, pb, isBare)
+ case Some(pb) => show(arg, pb, parsed isSet "--bare")
case _ => Console.println("Cannot read " + arg)
}
}
diff --git a/src/compiler/scala/tools/nsc/util/SourceFile.scala b/src/compiler/scala/tools/nsc/util/SourceFile.scala
index 57d2cc782f..f9f3c5e5fe 100644
--- a/src/compiler/scala/tools/nsc/util/SourceFile.scala
+++ b/src/compiler/scala/tools/nsc/util/SourceFile.scala
@@ -163,7 +163,6 @@ extends BatchSourceFile(name, contents)
override def positionInUltimateSource(position: Position) = {
if (!position.isDefined) super.positionInUltimateSource(position)
else {
- println("!!!")
var off = position.point
var compsLeft = components
// the search here has to be against the length of the files underlying the
@@ -171,7 +170,6 @@ extends BatchSourceFile(name, contents)
// less than the underlying length.) Otherwise we can and will overshoot the
// correct component and return a garbage position.
while (compsLeft.head.underlyingLength-1 <= off && !compsLeft.tail.isEmpty) {
- println("discarding "+compsLeft.head)
off = off - compsLeft.head.underlyingLength + 1
compsLeft = compsLeft.tail
}