summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala7
-rw-r--r--src/compiler/scala/tools/nsc/CompileServer.scala1
-rw-r--r--src/compiler/scala/tools/nsc/CompileSocket.scala3
-rw-r--r--src/compiler/scala/tools/nsc/CompilerCommand.scala10
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala258
-rw-r--r--src/compiler/scala/tools/nsc/Parsing.scala36
-rw-r--r--src/compiler/scala/tools/nsc/Properties.scala1
-rw-r--r--src/compiler/scala/tools/nsc/Reporting.scala15
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala58
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala53
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala130
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala41
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala501
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala46
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala56
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala861
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala846
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala396
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala293
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala70
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala21
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaScanners.scala2
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugins.scala2
-rw-r--r--src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala4
-rw-r--r--src/compiler/scala/tools/nsc/reporters/Reporter.scala4
-rw-r--r--src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala97
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala29
-rw-r--r--src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/Warnings.scala146
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala41
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala59
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala10
-rw-r--r--src/compiler/scala/tools/nsc/transform/Flatten.scala11
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/TypingTransformers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala41
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Adaptations.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala112
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala429
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala130
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala142
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala10
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala41
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala10
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala11
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala25
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala116
-rw-r--r--src/compiler/scala/tools/reflect/FormatInterpolator.scala18
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala2
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala13
59 files changed, 2768 insertions, 2477 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index f23bca77cd..0a356ed7b6 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -135,11 +135,8 @@ trait CompilationUnits { global: Global =>
@deprecated("Call global.currentRun.reporting.uncheckedWarning directly instead.", "2.11.2")
final def uncheckedWarning(pos: Position, msg: String): Unit = currentRun.reporting.uncheckedWarning(pos, msg)
- // called by ScalaDocAnalyzer, overridden by the IDE (in Reporter)
- // TODO: don't use reporter to communicate comments from parser to IDE!
- @deprecated("This method will be removed.", "2.11.2")
- final def comment(pos: Position, msg: String): Unit = reporter.comment(pos, msg)
-
+ @deprecated("This method will be removed. It does nothing.", "2.11.2")
+ final def comment(pos: Position, msg: String): Unit = {}
/** Is this about a .java source file? */
lazy val isJava = source.file.name.endsWith(".java")
diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala
index 1f3a4237eb..029e1c4629 100644
--- a/src/compiler/scala/tools/nsc/CompileServer.scala
+++ b/src/compiler/scala/tools/nsc/CompileServer.scala
@@ -152,6 +152,7 @@ class StandardCompileServer extends SocketServer {
clearCompiler()
case ex: Throwable =>
warn("Compile server encountered fatal condition: " + ex)
+ reporter.error(null, "Compile server encountered fatal condition: " + ex.getMessage)
shutdown = true
throw ex
}
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
index c4f06b59ec..c693fbe8e2 100644
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
@@ -32,7 +32,8 @@ trait HasCompileSocket {
if (isErrorMessage(line))
noErrors = false
- compileSocket.echo(line)
+ // be consistent with scalac: everything goes to stderr
+ compileSocket.warn(line)
loop()
}
try loop()
diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala
index a1d0d52dcf..3ded456378 100644
--- a/src/compiler/scala/tools/nsc/CompilerCommand.scala
+++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala
@@ -103,7 +103,15 @@ class CompilerCommand(arguments: List[String], val settings: Settings) {
val components = global.phaseNames // global.phaseDescriptors // one initializes
s"Phase graph of ${components.size} components output to ${genPhaseGraph.value}*.dot."
}
- else ""
+ // would be nicer if we could ask all the options for their helpful messages
+ else {
+ val sb = new StringBuilder
+ allSettings foreach {
+ case s: MultiChoiceSetting if s.isHelping => sb append s.help
+ case _ =>
+ }
+ sb.toString
+ }
}
/**
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 572e579aca..82ffb35c3f 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -45,7 +45,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
with Printers
with DocComments
with Positions
- with Reporting { self =>
+ with Reporting
+ with Parsing { self =>
// the mirror --------------------------------------------------
@@ -218,6 +219,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** Called from parser, which signals hereby that a method definition has been parsed. */
def signalParseProgress(pos: Position) {}
+ /** Called by ScalaDocAnalyzer when a doc comment has been parsed. */
+ def signalParsedDocComment(comment: String, pos: Position) = {
+ // TODO: this is all very borken (only works for scaladoc comments, not regular ones)
+ // --> add hooks to parser and refactor Interactive global to handle comments directly
+ // in any case don't use reporter for parser hooks
+ reporter.comment(pos, comment)
+ }
+
/** Register new context; called for every created context
*/
def registerContext(c: analyzer.Context) {
@@ -395,7 +404,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
reporter.cancelled || unit.isJava && this.id > maxJavaPhase
}
- final def applyPhase(unit: CompilationUnit) {
+ final def withCurrentUnit(unit: CompilationUnit)(task: => Unit) {
if ((unit ne null) && unit.exists)
lastSeenSourceFile = unit.source
@@ -407,7 +416,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
currentRun.currentUnit = unit
if (!cancelled(unit)) {
currentRun.informUnitStarting(this, unit)
- apply(unit)
+ task
}
currentRun.advanceUnit()
} finally {
@@ -415,6 +424,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
currentRun.currentUnit = unit0
}
}
+
+ final def applyPhase(unit: CompilationUnit) = withCurrentUnit(unit)(apply(unit))
}
// phaseName = "parser"
@@ -831,198 +842,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
} reverse
}
- // ------------ Invalidations ---------------------------------
-
- /** Is given package class a system package class that cannot be invalidated?
- */
- private def isSystemPackageClass(pkg: Symbol) =
- pkg == RootClass ||
- pkg == definitions.ScalaPackageClass || {
- val pkgname = pkg.fullName
- (pkgname startsWith "scala.") && !(pkgname startsWith "scala.tools")
- }
-
- /** Invalidates packages that contain classes defined in a classpath entry, and
- * rescans that entry.
- * @param paths Fully qualified names that refer to directories or jar files that are
- * a entries on the classpath.
- * First, causes the classpath entry referred to by `path` to be rescanned, so that
- * any new files or deleted files or changes in subpackages are picked up.
- * Second, invalidates any packages for which one of the following considitions is met:
-
- * - the classpath entry contained during the last compilation run classfiles
- * that represent a member in the package
- * - the classpath entry now contains classfiles
- * that represent a member in the package
- * - the set of subpackages has changed.
- *
- * The invalidated packages are reset in their entirety; all member classes and member packages
- * are re-accessed using the new classpath.
- * Not invalidated are system packages that the compiler needs to access as parts
- * of standard definitions. The criterion what is a system package is currently:
- * any package rooted in "scala", with the exception of packages rooted in "scala.tools".
- * This can be refined later.
- * @return A pair consisting of
- * - a list of invalidated packages
- * - a list of of packages that should have been invalidated but were not because
- * they are system packages.
- */
- def invalidateClassPathEntries(paths: String*): (List[ClassSymbol], List[ClassSymbol]) = {
- val invalidated, failed = new mutable.ListBuffer[ClassSymbol]
- classPath match {
- case cp: MergedClassPath[_] =>
- def assoc(path: String): List[(PlatformClassPath, PlatformClassPath)] = {
- val dir = AbstractFile getDirectory path
- val canonical = dir.canonicalPath
- def matchesCanonical(e: ClassPath[_]) = e.origin match {
- case Some(opath) =>
- (AbstractFile getDirectory opath).canonicalPath == canonical
- case None =>
- false
- }
- cp.entries find matchesCanonical match {
- case Some(oldEntry) =>
- List(oldEntry -> cp.context.newClassPath(dir))
- case None =>
- println(s"canonical = $canonical, origins = ${cp.entries map (_.origin)}")
- error(s"cannot invalidate: no entry named $path in classpath $classPath")
- List()
- }
- }
- val subst = Map(paths flatMap assoc: _*)
- if (subst.nonEmpty) {
- platform updateClassPath subst
- informProgress(s"classpath updated on entries [${subst.keys mkString ","}]")
- def mkClassPath(elems: Iterable[PlatformClassPath]): PlatformClassPath =
- if (elems.size == 1) elems.head
- else new MergedClassPath(elems, classPath.context)
- val oldEntries = mkClassPath(subst.keys)
- val newEntries = mkClassPath(subst.values)
- reSync(RootClass, Some(classPath), Some(oldEntries), Some(newEntries), invalidated, failed)
- }
- }
- def show(msg: String, syms: scala.collection.Traversable[Symbol]) =
- if (syms.nonEmpty)
- informProgress(s"$msg: ${syms map (_.fullName) mkString ","}")
- show("invalidated packages", invalidated)
- show("could not invalidate system packages", failed)
- (invalidated.toList, failed.toList)
- }
-
- /** Re-syncs symbol table with classpath
- * @param root The root symbol to be resynced (a package class)
- * @param allEntries Optionally, the corresponding package in the complete current classPath
- * @param oldEntries Optionally, the corresponding package in the old classPath entries
- * @param newEntries Optionally, the corresponding package in the new classPath entries
- * @param invalidated A listbuffer collecting the invalidated package classes
- * @param failed A listbuffer collecting system package classes which could not be invalidated
- * The resyncing strategy is determined by the absence or presence of classes and packages.
- * If either oldEntries or newEntries contains classes, root is invalidated, provided a corresponding package
- * exists in allEntries, or otherwise is removed.
- * Otherwise, the action is determined by the following matrix, with columns:
- *
- * old new all sym action
- * + + + + recurse into all child packages of old ++ new
- * + - + + invalidate root
- * + - - + remove root from its scope
- * - + + + invalidate root
- * - + + - create and enter root
- * - - * * no action
- *
- * Here, old, new, all mean classpaths and sym means symboltable. + is presence of an
- * entry in its column, - is absence, * is don't care.
- *
- * Note that new <= all and old <= sym, so the matrix above covers all possibilities.
- */
- private def reSync(root: ClassSymbol,
- allEntries: OptClassPath, oldEntries: OptClassPath, newEntries: OptClassPath,
- invalidated: mutable.ListBuffer[ClassSymbol], failed: mutable.ListBuffer[ClassSymbol]) {
- ifDebug(informProgress(s"syncing $root, $oldEntries -> $newEntries"))
-
- val getName: ClassPath[AbstractFile] => String = (_.name)
- def hasClasses(cp: OptClassPath) = cp.isDefined && cp.get.classes.nonEmpty
- def invalidateOrRemove(root: ClassSymbol) = {
- allEntries match {
- case Some(cp) => root setInfo new loaders.PackageLoader(cp)
- case None => root.owner.info.decls unlink root.sourceModule
- }
- invalidated += root
- }
- def packageNames(cp: PlatformClassPath): Set[String] = cp.packages.toSet map getName
- def subPackage(cp: PlatformClassPath, name: String): OptClassPath =
- cp.packages find (cp1 => getName(cp1) == name)
-
- val classesFound = hasClasses(oldEntries) || hasClasses(newEntries)
- if (classesFound && !isSystemPackageClass(root)) {
- invalidateOrRemove(root)
- } else {
- if (classesFound) {
- if (root.isRoot) invalidateOrRemove(EmptyPackageClass)
- else failed += root
- }
- (oldEntries, newEntries) match {
- case (Some(oldcp) , Some(newcp)) =>
- for (pstr <- packageNames(oldcp) ++ packageNames(newcp)) {
- val pname = newTermName(pstr)
- val pkg = (root.info decl pname) orElse {
- // package was created by external agent, create symbol to track it
- assert(!subPackage(oldcp, pstr).isDefined)
- loaders.enterPackage(root, pstr, new loaders.PackageLoader(allEntries.get))
- }
- reSync(
- pkg.moduleClass.asInstanceOf[ClassSymbol],
- subPackage(allEntries.get, pstr), subPackage(oldcp, pstr), subPackage(newcp, pstr),
- invalidated, failed)
- }
- case (Some(oldcp), None) =>
- invalidateOrRemove(root)
- case (None, Some(newcp)) =>
- invalidateOrRemove(root)
- case (None, None) =>
- }
- }
- }
-
- /** Invalidate contents of setting -Yinvalidate */
- def doInvalidation() = settings.Yinvalidate.value match {
- case "" =>
- case entry => invalidateClassPathEntries(entry)
- }
-
// ----------- Runs ---------------------------------------
private var curRun: Run = null
private var curRunId = 0
- /** A hook that lets subclasses of `Global` define whether a package or class should be kept loaded for the
- * next compiler run. If the parameter `sym` is a class or object, and `clearOnNextRun(sym)` returns `true`,
- * then the symbol is unloaded and reset to its state before the last compiler run. If the parameter `sym` is
- * a package, and clearOnNextRun(sym)` returns `true`, the package is recursively searched for
- * classes to drop.
- *
- * Example: Let's say I want a compiler that drops all classes corresponding to the current project
- * between runs. Then `keepForNextRun` of a toplevel class or object should return `true` if the
- * class or object does not form part of the current project, `false` otherwise. For a package,
- * clearOnNextRun should return `true` if no class in that package forms part of the current project,
- * `false` otherwise.
- *
- * @param sym A class symbol, object symbol, package, or package class.
- */
- @deprecated("use invalidateClassPathEntries instead", "2.10.0")
- def clearOnNextRun(sym: Symbol) = false
- /* To try out clearOnNext run on the scala.tools.nsc project itself
- * replace `false` above with the following code
-
- settings.Xexperimental.value && { sym.isRoot || {
- sym.fullName match {
- case "scala" | "scala.tools" | "scala.tools.nsc" => true
- case _ => sym.owner.fullName.startsWith("scala.tools.nsc")
- }
- }}
-
- * Then, fsc -Xexperimental clears the nsc project between successive runs of `fsc`.
- */
-
object typeDeconstruct extends {
val global: Global.this.type = Global.this
} with typechecker.StructuredTypeStrings
@@ -1155,7 +979,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** A Run is a single execution of the compiler on a set of units.
*/
- class Run extends RunContextApi with RunReporting {
+ class Run extends RunContextApi with RunReporting with RunParsing {
/** Have been running into too many init order issues with Run
* during erroneous conditions. Moved all these vals up to the
* top of the file so at least they're not trivially null.
@@ -1292,47 +1116,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
first
}
- /** Reset all classes contained in current project, as determined by
- * the clearOnNextRun hook
- */
- @deprecated("use invalidateClassPathEntries instead", "2.10.0")
- def resetProjectClasses(root: Symbol): Unit = try {
- def unlink(sym: Symbol) =
- if (sym != NoSymbol) root.info.decls.unlink(sym)
- if (settings.verbose) inform("[reset] recursing in "+root)
- val toReload = mutable.Set[String]()
- for (sym <- root.info.decls) {
- if (sym.isInitialized && clearOnNextRun(sym))
- if (sym.hasPackageFlag) {
- resetProjectClasses(sym.moduleClass)
- openPackageModule(sym.moduleClass)
- } else {
- unlink(sym)
- unlink(root.info.decls.lookup(
- if (sym.isTerm) sym.name.toTypeName else sym.name.toTermName))
- toReload += sym.fullName
- // note: toReload could be set twice with the same name
- // but reinit must happen only once per name. That's why
- // the following classPath.findClass { ... } code cannot be moved here.
- }
- }
- for (fullname <- toReload)
- classPath.findClass(fullname) match {
- case Some(classRep) =>
- if (settings.verbose) inform("[reset] reinit "+fullname)
- loaders.initializeFromClassPath(root, classRep)
- case _ =>
- }
- } catch {
- case ex: Throwable =>
- // this handler should not be nessasary, but it seems that `fsc`
- // eats exceptions if they appear here. Need to find out the cause for
- // this and fix it.
- inform("[reset] exception happened: "+ex)
- ex.printStackTrace()
- throw ex
- }
-
// --------------- Miscellania -------------------------------
/** Progress tracking. Measured in "progress units" which are 1 per
@@ -1397,7 +1180,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val erasurePhase = phaseNamed("erasure")
val posterasurePhase = phaseNamed("posterasure")
// val lazyvalsPhase = phaseNamed("lazyvals")
- // val lambdaliftPhase = phaseNamed("lambdalift")
+ val lambdaliftPhase = phaseNamed("lambdalift")
// val constructorsPhase = phaseNamed("constructors")
val flattenPhase = phaseNamed("flatten")
val mixinPhase = phaseNamed("mixin")
@@ -1542,8 +1325,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
compileUnitsInternal(units, fromPhase)
private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) {
- doInvalidation()
-
units foreach addUnit
val startTime = currentTime
@@ -1619,13 +1400,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// Clear any sets or maps created via perRunCaches.
perRunCaches.clearAll()
-
- // Reset project
- if (!stopPhase("namer")) {
- enteringPhase(namerPhase) {
- resetProjectClasses(RootClass)
- }
- }
}
/** Compile list of abstract files. */
diff --git a/src/compiler/scala/tools/nsc/Parsing.scala b/src/compiler/scala/tools/nsc/Parsing.scala
new file mode 100644
index 0000000000..4dd3c3f378
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/Parsing.scala
@@ -0,0 +1,36 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL, Typesafe Inc.
+ * @author Adriaan Moors
+ */
+
+package scala
+package tools.nsc
+
+import scala.reflect.internal.Positions
+import scala.tools.nsc.reporters.Reporter
+
+/** Similar to Reporting: gather global functionality specific to parsing.
+ */
+trait Parsing { self : Positions with Reporting =>
+ def currentRun: RunParsing
+
+ trait RunParsing {
+ val parsing: PerRunParsing = new PerRunParsing
+ }
+
+ class PerRunParsing {
+ // for repl
+ private[this] var incompleteHandler: (Position, String) => Unit = null
+ def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = {
+ val saved = incompleteHandler
+ incompleteHandler = handler
+ try thunk
+ finally incompleteHandler = saved
+ }
+
+ def incompleteHandled = incompleteHandler != null
+ def incompleteInputError(pos: Position, msg: String): Unit =
+ if (incompleteHandled) incompleteHandler(pos, msg)
+ else reporter.error(pos, msg)
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/Properties.scala b/src/compiler/scala/tools/nsc/Properties.scala
index ed5fda9c3f..59fefba954 100644
--- a/src/compiler/scala/tools/nsc/Properties.scala
+++ b/src/compiler/scala/tools/nsc/Properties.scala
@@ -14,6 +14,7 @@ object Properties extends scala.util.PropertiesTrait {
// settings based on jar properties
def residentPromptString = scalaPropOrElse("resident.prompt", "\nnsc> ")
def shellPromptString = scalaPropOrElse("shell.prompt", "\nscala> ")
+ def shellInterruptedString = scalaPropOrElse("shell.interrupted", ":quit\n")
// derived values
def isEmacsShell = propOrEmpty("env.emacs") != ""
diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala
index 0263586418..b164f395fe 100644
--- a/src/compiler/scala/tools/nsc/Reporting.scala
+++ b/src/compiler/scala/tools/nsc/Reporting.scala
@@ -104,20 +104,5 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
if (settings.fatalWarnings && reporter.hasWarnings)
reporter.error(NoPosition, "No warnings can be incurred under -Xfatal-warnings.")
}
-
- // for repl
- private[this] var incompleteHandler: (Position, String) => Unit = null
- def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = {
- val saved = incompleteHandler
- incompleteHandler = handler
- try thunk
- finally incompleteHandler = saved
- }
-
- def incompleteHandled = incompleteHandler != null
- def incompleteInputError(pos: Position, msg: String): Unit =
- if (incompleteHandled) incompleteHandler(pos, msg)
- else reporter.error(pos, msg)
-
}
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 883fd31dbc..8d810d456e 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -154,8 +154,8 @@ self =>
def unit = global.currentUnit
// suppress warnings; silent abort on errors
- def warning(offset: Offset, msg: String) {}
- def deprecationWarning(offset: Offset, msg: String) {}
+ def warning(offset: Offset, msg: String): Unit = ()
+ def deprecationWarning(offset: Offset, msg: String): Unit = ()
def syntaxError(offset: Offset, msg: String): Unit = throw new MalformedInput(offset, msg)
def incompleteInputError(msg: String): Unit = throw new MalformedInput(source.content.length - 1, msg)
@@ -204,13 +204,11 @@ self =>
override def newScanner() = new UnitScanner(unit, patches)
- override def warning(offset: Offset, msg: String) {
+ override def warning(offset: Offset, msg: String): Unit =
reporter.warning(o2p(offset), msg)
- }
- override def deprecationWarning(offset: Offset, msg: String) {
+ override def deprecationWarning(offset: Offset, msg: String): Unit =
currentRun.reporting.deprecationWarning(o2p(offset), msg)
- }
private var smartParsing = false
@inline private def withSmartParsing[T](body: => T): T = {
@@ -226,15 +224,15 @@ self =>
for ((offset, msg) <- syntaxErrors)
reporter.error(o2p(offset), msg)
- override def syntaxError(offset: Offset, msg: String) {
+ override def syntaxError(offset: Offset, msg: String): Unit = {
if (smartParsing) syntaxErrors += ((offset, msg))
else reporter.error(o2p(offset), msg)
}
- override def incompleteInputError(msg: String) {
+ override def incompleteInputError(msg: String): Unit = {
val offset = source.content.length - 1
if (smartParsing) syntaxErrors += ((offset, msg))
- else currentRun.reporting.incompleteInputError(o2p(offset), msg)
+ else currentRun.parsing.incompleteInputError(o2p(offset), msg)
}
/** parse unit. If there are inbalanced braces,
@@ -335,7 +333,7 @@ self =>
*/
private var inScalaPackage = false
private var currentPackage = ""
- def resetPackage() {
+ def resetPackage(): Unit = {
inScalaPackage = false
currentPackage = ""
}
@@ -514,7 +512,7 @@ self =>
finally inFunReturnType = saved
}
- protected def skip(targetToken: Token) {
+ protected def skip(targetToken: Token): Unit = {
var nparens = 0
var nbraces = 0
while (true) {
@@ -544,27 +542,25 @@ self =>
}
def warning(offset: Offset, msg: String): Unit
def incompleteInputError(msg: String): Unit
- private def syntaxError(pos: Position, msg: String, skipIt: Boolean) {
- syntaxError(pos pointOrElse in.offset, msg, skipIt)
- }
def syntaxError(offset: Offset, msg: String): Unit
- def syntaxError(msg: String, skipIt: Boolean) {
+
+ private def syntaxError(pos: Position, msg: String, skipIt: Boolean): Unit =
+ syntaxError(pos pointOrElse in.offset, msg, skipIt)
+ def syntaxError(msg: String, skipIt: Boolean): Unit =
syntaxError(in.offset, msg, skipIt)
- }
- def syntaxError(offset: Offset, msg: String, skipIt: Boolean) {
+ def syntaxError(offset: Offset, msg: String, skipIt: Boolean): Unit = {
if (offset > lastErrorOffset) {
syntaxError(offset, msg)
- // no more errors on this token.
- lastErrorOffset = in.offset
+ lastErrorOffset = in.offset // no more errors on this token.
}
if (skipIt)
skip(UNDEF)
}
- def warning(msg: String) { warning(in.offset, msg) }
+ def warning(msg: String): Unit = warning(in.offset, msg)
- def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean) {
+ def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean): Unit = {
if (in.token == EOF)
incompleteInputError(msg)
else
@@ -719,7 +715,7 @@ self =>
/** Convert tree to formal parameter. */
def convertToParam(tree: Tree): ValDef = atPos(tree.pos) {
- def removeAsPlaceholder(name: Name) {
+ def removeAsPlaceholder(name: Name): Unit = {
placeholderParams = placeholderParams filter (_.name != name)
}
def errorParam = makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.end))
@@ -1233,15 +1229,15 @@ self =>
skipIt = true)(EmptyTree)
// Like Swiss cheese, with holes
def stringCheese: Tree = atPos(in.offset) {
- val start = in.offset
+ val start = in.offset
val interpolator = in.name.encoded // ident() for INTERPOLATIONID
val partsBuf = new ListBuffer[Tree]
- val exprBuf = new ListBuffer[Tree]
+ val exprsBuf = new ListBuffer[Tree]
in.nextToken()
while (in.token == STRINGPART) {
partsBuf += literal()
- exprBuf += (
+ exprsBuf += (
if (inPattern) dropAnyBraces(pattern())
else in.token match {
case IDENTIFIER => atPos(in.offset)(Ident(ident()))
@@ -1254,11 +1250,13 @@ self =>
}
if (in.token == STRINGLIT) partsBuf += literal()
+ // Documenting that it is intentional that the ident is not rooted for purposes of virtualization
+ //val t1 = atPos(o2p(start)) { Select(Select (Ident(nme.ROOTPKG), nme.scala_), nme.StringContext) }
val t1 = atPos(o2p(start)) { Ident(nme.StringContext) }
val t2 = atPos(start) { Apply(t1, partsBuf.toList) }
t2 setPos t2.pos.makeTransparent
val t3 = Select(t2, interpolator) setPos t2.pos
- atPos(start) { Apply(t3, exprBuf.toList) }
+ atPos(start) { Apply(t3, exprsBuf.toList) }
}
if (inPattern) stringCheese
else withPlaceholders(stringCheese, isAny = true) // strinterpolator params are Any* by definition
@@ -1266,21 +1264,21 @@ self =>
/* ------------- NEW LINES ------------------------------------------------- */
- def newLineOpt() {
+ def newLineOpt(): Unit = {
if (in.token == NEWLINE) in.nextToken()
}
- def newLinesOpt() {
+ def newLinesOpt(): Unit = {
if (in.token == NEWLINE || in.token == NEWLINES)
in.nextToken()
}
- def newLineOptWhenFollowedBy(token: Offset) {
+ def newLineOptWhenFollowedBy(token: Offset): Unit = {
// note: next is defined here because current == NEWLINE
if (in.token == NEWLINE && in.next.token == token) newLineOpt()
}
- def newLineOptWhenFollowing(p: Token => Boolean) {
+ def newLineOptWhenFollowing(p: Token => Boolean): Unit = {
// note: next is defined here because current == NEWLINE
if (in.token == NEWLINE && p(in.next.token)) newLineOpt()
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 572497ac90..9ebc94b5fc 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -113,7 +113,7 @@ trait Scanners extends ScannersCommon {
case SU | CR | LF =>
case _ => nextChar() ; skipLineComment()
}
- private def maybeOpen() {
+ private def maybeOpen(): Unit = {
putCommentChar()
if (ch == '*') {
putCommentChar()
@@ -137,7 +137,7 @@ trait Scanners extends ScannersCommon {
def skipDocComment(): Unit = skipNestedComments()
def skipBlockComment(): Unit = skipNestedComments()
- private def skipToCommentEnd(isLineComment: Boolean) {
+ private def skipToCommentEnd(isLineComment: Boolean): Unit = {
nextChar()
if (isLineComment) skipLineComment()
else {
@@ -185,7 +185,7 @@ trait Scanners extends ScannersCommon {
/** append Unicode character to "cbuf" buffer
*/
- protected def putChar(c: Char) {
+ protected def putChar(c: Char): Unit = {
// assert(cbuf.size < 10000, cbuf)
cbuf.append(c)
}
@@ -196,7 +196,7 @@ trait Scanners extends ScannersCommon {
protected def emitIdentifierDeprecationWarnings = true
/** Clear buffer and set name and token */
- private def finishNamed(idtoken: Token = IDENTIFIER) {
+ private def finishNamed(idtoken: Token = IDENTIFIER): Unit = {
name = newTermName(cbuf.toString)
cbuf.clear()
token = idtoken
@@ -215,7 +215,7 @@ trait Scanners extends ScannersCommon {
}
/** Clear buffer and set string */
- private def setStrVal() {
+ private def setStrVal(): Unit = {
strVal = cbuf.toString
cbuf.clear()
}
@@ -270,7 +270,7 @@ trait Scanners extends ScannersCommon {
/** Produce next token, filling TokenData fields of Scanner.
*/
- def nextToken() {
+ def nextToken(): Unit = {
val lastToken = token
// Adapt sepRegions according to last token
(lastToken: @switch) match {
@@ -341,7 +341,7 @@ trait Scanners extends ScannersCommon {
prev copyFrom this
val nextLastOffset = charOffset - 1
fetchToken()
- def resetOffset() {
+ def resetOffset(): Unit = {
offset = prev.offset
lastOffset = prev.lastOffset
}
@@ -399,7 +399,7 @@ trait Scanners extends ScannersCommon {
/** read next token, filling TokenData fields of Scanner.
*/
- protected final def fetchToken() {
+ protected final def fetchToken(): Unit = {
offset = charOffset - 1
(ch: @switch) match {
@@ -604,7 +604,7 @@ trait Scanners extends ScannersCommon {
// Identifiers ---------------------------------------------------------------
- private def getBackquotedIdent() {
+ private def getBackquotedIdent(): Unit = {
nextChar()
getLitChars('`')
if (ch == '`') {
@@ -664,7 +664,7 @@ trait Scanners extends ScannersCommon {
else finishNamed()
}
- private def getIdentOrOperatorRest() {
+ private def getIdentOrOperatorRest(): Unit = {
if (isIdentifierPart(ch))
getIdentRest()
else ch match {
@@ -688,9 +688,11 @@ trait Scanners extends ScannersCommon {
setStrVal()
nextChar()
token = STRINGLIT
- } else syntaxError("unclosed string literal")
+ } else unclosedStringLit()
}
+ private def unclosedStringLit(): Unit = syntaxError("unclosed string literal")
+
private def getRawStringLit(): Unit = {
if (ch == '\"') {
nextRawChar()
@@ -764,7 +766,7 @@ trait Scanners extends ScannersCommon {
if (multiLine)
incompleteInputError("unclosed multi-line string literal")
else
- syntaxError("unclosed string literal")
+ unclosedStringLit()
}
else {
putChar(ch)
@@ -857,7 +859,7 @@ trait Scanners extends ScannersCommon {
/** read fractional part and exponent of floating point number
* if one is present.
*/
- protected def getFraction() {
+ protected def getFraction(): Unit = {
token = DOUBLELIT
while ('0' <= ch && ch <= '9') {
putChar(ch)
@@ -966,14 +968,13 @@ trait Scanners extends ScannersCommon {
def floatVal: Double = floatVal(negated = false)
- def checkNoLetter() {
+ def checkNoLetter(): Unit = {
if (isIdentifierPart(ch) && ch >= ' ')
syntaxError("Invalid literal number")
}
- /** Read a number into strVal and set base
- */
- protected def getNumber() {
+ /** Read a number into strVal and set base */
+ protected def getNumber(): Unit = {
val base1 = if (base < 10) 10 else base
// Read 8,9's even if format is octal, produce a malformed number error afterwards.
// At this point, we have already read the first digit, so to tell an innocent 0 apart
@@ -1052,7 +1053,7 @@ trait Scanners extends ScannersCommon {
/** Parse character literal if current character is followed by \',
* or follow with given op and return a symbol literal token
*/
- def charLitOr(op: () => Unit) {
+ def charLitOr(op: () => Unit): Unit = {
putChar(ch)
nextChar()
if (ch == '\'') {
@@ -1068,21 +1069,19 @@ trait Scanners extends ScannersCommon {
// Errors -----------------------------------------------------------------
- /** generate an error at the given offset
- */
- def syntaxError(off: Offset, msg: String) {
+ /** generate an error at the given offset */
+ def syntaxError(off: Offset, msg: String): Unit = {
error(off, msg)
token = ERROR
}
- /** generate an error at the current token offset
- */
+ /** generate an error at the current token offset */
def syntaxError(msg: String): Unit = syntaxError(offset, msg)
def deprecationWarning(msg: String): Unit = deprecationWarning(offset, msg)
/** signal an error where the input ended in the middle of a token */
- def incompleteInputError(msg: String) {
+ def incompleteInputError(msg: String): Unit = {
incompleteInputError(offset, msg)
token = EOF
}
@@ -1134,7 +1133,7 @@ trait Scanners extends ScannersCommon {
/** Initialization method: read first char, then first token
*/
- def init() {
+ def init(): Unit = {
nextChar()
nextToken()
}
@@ -1261,7 +1260,7 @@ trait Scanners extends ScannersCommon {
override def deprecationWarning(off: Offset, msg: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg)
override def error (off: Offset, msg: String) = reporter.error(unit.position(off), msg)
- override def incompleteInputError(off: Offset, msg: String) = currentRun.reporting.incompleteInputError(unit.position(off), msg)
+ override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg)
private var bracePatches: List[BracePatch] = patches
@@ -1490,6 +1489,6 @@ trait Scanners extends ScannersCommon {
// when skimming through the source file trying to heal braces
override def emitIdentifierDeprecationWarnings = false
- override def error(offset: Offset, msg: String) {}
+ override def error(offset: Offset, msg: String): Unit = ()
}
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
index 64b762696e..df2073785b 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
@@ -83,7 +83,7 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse
private def initialUnitBody(unit: CompilationUnit): Tree = {
if (unit.isJava) new JavaUnitParser(unit).parse()
- else if (currentRun.reporting.incompleteHandled) newUnitParser(unit).parse()
+ else if (currentRun.parsing.incompleteHandled) newUnitParser(unit).parse()
else newUnitParser(unit).smartParse()
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
new file mode 100644
index 0000000000..e5b4c4a6c2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -0,0 +1,130 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc.backend.jvm
+
+import scala.tools.nsc.Global
+import PartialFunction._
+
+/**
+ * This trait contains code shared between GenBCode and GenASM that depends on types defined in
+ * the compiler cake (Global).
+ */
+final class BCodeAsmCommon[G <: Global](val global: G) {
+ import global._
+
+ /**
+ * True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
+ * member class. This method is used to decide if we should emit an EnclosingMethod attribute.
+ * It is also used to decide whether the "owner" field in the InnerClass attribute should be
+ * null.
+ */
+ def isAnonymousOrLocalClass(classSym: Symbol): Boolean = {
+ def isDelambdafyLambdaClass(classSym: Symbol): Boolean = {
+ classSym.isAnonymousFunction && (settings.Ydelambdafy.value == "method")
+ }
+
+ assert(classSym.isClass, s"not a class: $classSym")
+
+ !isDelambdafyLambdaClass(classSym) &&
+ (classSym.isAnonymousClass || !classSym.originalOwner.isClass)
+ }
+
+ /**
+ * Returns the enclosing method for non-member classes. In the following example
+ *
+ * class A {
+ * def f = {
+ * class B {
+ * class C
+ * }
+ * }
+ * }
+ *
+ * the method returns Some(f) for B, but None for C, because C is a member class. For non-member
+ * classes that are not enclosed by a method, it returns None:
+ *
+ * class A {
+ * { class B }
+ * }
+ *
+ * In this case, for B, we return None.
+ *
+ * The EnclosingMethod attribute needs to be added to non-member classes (see doc in BTypes).
+ * This is a source-level property, so we need to use the originalOwner chain to reconstruct it.
+ */
+ private def enclosingMethodForEnclosingMethodAttribute(classSym: Symbol): Option[Symbol] = {
+ assert(classSym.isClass, classSym)
+ def enclosingMethod(sym: Symbol): Option[Symbol] = {
+ if (sym.isClass || sym == NoSymbol) None
+ else if (sym.isMethod) Some(sym)
+ else enclosingMethod(sym.originalOwner)
+ }
+ enclosingMethod(classSym.originalOwner)
+ }
+
+ /**
+ * The enclosing class for emitting the EnclosingMethod attribute. Since this is a source-level
+ * property, this method looks at the originalOwner chain. See doc in BTypes.
+ */
+ private def enclosingClassForEnclosingMethodAttribute(classSym: Symbol): Symbol = {
+ assert(classSym.isClass, classSym)
+ def enclosingClass(sym: Symbol): Symbol = {
+ if (sym.isClass) sym
+ else enclosingClass(sym.originalOwner)
+ }
+ enclosingClass(classSym.originalOwner)
+ }
+
+ final case class EnclosingMethodEntry(owner: String, name: String, methodDescriptor: String)
+
+ /**
+ * If data for emitting an EnclosingMethod attribute. None if `classSym` is a member class (not
+ * an anonymous or local class). See doc in BTypes.
+ *
+ * The class is parametrized by two functions to obtain a bytecode class descriptor for a class
+ * symbol, and to obtain a method signature descriptor fro a method symbol. These function depend
+ * on the implementation of GenASM / GenBCode, so they need to be passed in.
+ */
+ def enclosingMethodAttribute(classSym: Symbol, classDesc: Symbol => String, methodDesc: Symbol => String): Option[EnclosingMethodEntry] = {
+ if (isAnonymousOrLocalClass(classSym)) {
+ val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym)
+ debuglog(s"enclosing method for $classSym is $methodOpt (in ${methodOpt.map(_.enclClass)})")
+ Some(EnclosingMethodEntry(
+ classDesc(enclosingClassForEnclosingMethodAttribute(classSym)),
+ methodOpt.map(_.javaSimpleName.toString).orNull,
+ methodOpt.map(methodDesc).orNull))
+ } else {
+ None
+ }
+ }
+
+ /**
+ * This is basically a re-implementation of sym.isStaticOwner, but using the originalOwner chain.
+ *
+ * The problem is that we are interested in a source-level property. Various phases changed the
+ * symbol's properties in the meantime, mostly lambdalift modified (destructively) the owner.
+ * Therefore, `sym.isStatic` is not what we want. For example, in
+ * object T { def f { object U } }
+ * the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here.
+ */
+ def isOriginallyStaticOwner(sym: Symbol): Boolean = {
+ sym.isPackageClass || sym.isModuleClass && isOriginallyStaticOwner(sym.originalOwner)
+ }
+
+ /**
+ * The member classes of a class symbol. Note that the result of this method depends on the
+ * current phase, for example, after lambdalift, all local classes become member of the enclosing
+ * class.
+ */
+ def memberClassesOf(classSymbol: Symbol): List[Symbol] = classSymbol.info.decls.collect({
+ case sym if sym.isClass =>
+ sym
+ case sym if sym.isModule =>
+ val r = exitingPickler(sym.moduleClass)
+ assert(r != NoSymbol, sym.fullLocationString)
+ r
+ })(collection.breakOut)
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index 4583462b71..397171049f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -23,8 +23,9 @@ import scala.tools.asm
abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
import global._
import definitions._
- import bCodeICodeCommon._
import bTypes._
+ import bCodeICodeCommon._
+ import coreBTypes._
/*
* Functionality to build the body of ASM MethodNode, except for `synchronized` and `try` expressions.
@@ -92,7 +93,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val thrownKind = tpeTK(expr)
// `throw null` is valid although scala.Null (as defined in src/libray-aux) isn't a subtype of Throwable.
// Similarly for scala.Nothing (again, as defined in src/libray-aux).
- assert(thrownKind.isNullType || thrownKind.isNothingType || exemplars.get(thrownKind).isSubtypeOf(ThrowableReference))
+ assert(thrownKind.isNullType || thrownKind.isNothingType || thrownKind.asClassBType.isSubtypeOf(ThrowableReference))
genLoad(expr, thrownKind)
lineNumber(expr)
emit(asm.Opcodes.ATHROW) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level.
@@ -123,7 +124,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
// binary operation
case rarg :: Nil =>
- resKind = maxType(tpeTK(larg), tpeTK(rarg))
+ resKind = tpeTK(larg).maxType(tpeTK(rarg))
if (scalaPrimitives.isShiftOp(code) || scalaPrimitives.isBitwiseOp(code)) {
assert(resKind.isIntegralType || (resKind == BOOL),
s"$resKind incompatible with arithmetic modulo operation.")
@@ -321,7 +322,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
mnode.visitVarInsn(asm.Opcodes.ALOAD, 0)
generatedType =
if (tree.symbol == ArrayClass) ObjectReference
- else ClassBType(thisName) // inner class (if any) for claszSymbol already tracked.
+ else classBTypeFromSymbol(claszSymbol)
}
case Select(Ident(nme.EMPTY_PACKAGE_NAME), module) =>
@@ -459,9 +460,10 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case ClazzTag =>
val toPush: BType = {
- val kind = toTypeKind(const.typeValue)
- if (kind.isPrimitive) classLiteral(kind)
- else kind
+ toTypeKind(const.typeValue) match {
+ case kind: PrimitiveBType => boxedClassOfPrimitive(kind)
+ case kind => kind
+ }
}
mnode.visitLdcInsn(toPush.toASMType)
@@ -558,7 +560,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
abort(s"Erasure should have added an unboxing operation to prevent this cast. Tree: $app")
}
else if (r.isPrimitive) {
- bc isInstance classLiteral(r)
+ bc isInstance boxedClassOfPrimitive(r.asPrimitiveBType)
}
else {
assert(r.isRef, r) // ensure that it's not a method
@@ -619,7 +621,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
case rt: ClassBType =>
- assert(exemplar(ctor.owner).c == rt, s"Symbol ${ctor.owner.fullName} is different from $rt")
+ assert(classBTypeFromSymbol(ctor.owner) == rt, s"Symbol ${ctor.owner.fullName} is different from $rt")
mnode.visitTypeInsn(asm.Opcodes.NEW, rt.internalName)
bc dup generatedType
genLoadArguments(args, paramTKs(app))
@@ -632,16 +634,16 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) =>
val nativeKind = tpeTK(expr)
genLoad(expr, nativeKind)
- val MethodNameAndType(mname, mdesc) = asmBoxTo(nativeKind)
- bc.invokestatic(BoxesRunTime.internalName, mname, mdesc)
+ val MethodNameAndType(mname, methodType) = asmBoxTo(nativeKind)
+ bc.invokestatic(BoxesRunTime.internalName, mname, methodType.descriptor)
generatedType = boxResultType(fun.symbol) // was toTypeKind(fun.symbol.tpe.resultType)
case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) =>
genLoad(expr)
val boxType = unboxResultType(fun.symbol) // was toTypeKind(fun.symbol.owner.linkedClassOfClass.tpe)
generatedType = boxType
- val MethodNameAndType(mname, mdesc) = asmUnboxTo(boxType)
- bc.invokestatic(BoxesRunTime.internalName, mname, mdesc)
+ val MethodNameAndType(mname, methodType) = asmUnboxTo(boxType)
+ bc.invokestatic(BoxesRunTime.internalName, mname, methodType.descriptor)
case app @ Apply(fun, args) =>
val sym = fun.symbol
@@ -807,7 +809,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
def adapt(from: BType, to: BType) {
- if (!conforms(from, to)) {
+ if (!from.conformsTo(to)) {
to match {
case UNIT => bc drop from
case _ => bc.emitT2T(from, to)
@@ -948,7 +950,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
def genCallMethod(method: Symbol, style: InvokeStyle, hostClass0: Symbol = null, pos: Position = NoPosition) {
val siteSymbol = claszSymbol
- val hostSymbol = if (hostClass0 == null) method.owner else hostClass0;
+ val hostSymbol = if (hostClass0 == null) method.owner else hostClass0
val methodOwner = method.owner
// info calls so that types are up to date; erasure may add lateINTERFACE to traits
hostSymbol.info ; methodOwner.info
@@ -966,8 +968,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
|| methodOwner == definitions.ObjectClass
)
val receiver = if (useMethodOwner) methodOwner else hostSymbol
- val bmOwner = asmClassType(receiver)
- val jowner = bmOwner.internalName
+ val jowner = internalName(receiver)
val jname = method.javaSimpleName.toString
val bmType = asmMethodType(method)
val mdescr = bmType.descriptor
@@ -977,7 +978,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
if (!isModuleInitialized &&
jMethodName == INSTANCE_CONSTRUCTOR_NAME &&
jname == INSTANCE_CONSTRUCTOR_NAME &&
- isStaticModule(siteSymbol)) {
+ isStaticModuleClass(siteSymbol)) {
isModuleInitialized = true
mnode.visitVarInsn(asm.Opcodes.ALOAD, 0)
mnode.visitFieldInsn(
@@ -1100,7 +1101,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
genCZJUMP(success, failure, op, ObjectReference)
}
else {
- val tk = maxType(tpeTK(l), tpeTK(r))
+ val tk = tpeTK(l).maxType(tpeTK(r))
genLoad(l, tk)
genLoad(r, tk)
genCJUMP(success, failure, op, tk)
@@ -1206,7 +1207,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
genCZJUMP(success, failure, icodes.NE, BOOL)
} else {
// l == r -> if (l eq null) r eq null else l.equals(r)
- val eqEqTempLocal = locals.makeLocal(AnyRefReference, nme.EQEQ_LOCAL_VAR.toString)
+ val eqEqTempLocal = locals.makeLocal(ObjectReference, nme.EQEQ_LOCAL_VAR.toString)
val lNull = new asm.Label
val lNonNull = new asm.Label
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 31a392ed55..5670715cd3 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -8,8 +8,7 @@ package tools.nsc
package backend.jvm
import scala.tools.asm
-import scala.annotation.switch
-import scala.collection.{ immutable, mutable }
+import scala.collection.mutable
import scala.tools.nsc.io.AbstractFile
/*
@@ -19,9 +18,10 @@ import scala.tools.nsc.io.AbstractFile
* @version 1.0
*
*/
-abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
+abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
import global._
import bTypes._
+ import coreBTypes._
/*
* must-single-thread
@@ -53,24 +53,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
// https://issues.scala-lang.org/browse/SI-3872
// -----------------------------------------------------------------------------------------
- /*
- * can-multi-thread
- */
- def firstCommonSuffix(as: List[Tracked], bs: List[Tracked]): ClassBType = {
- var chainA = as
- var chainB = bs
- var fcs: Tracked = null
- do {
- if (chainB contains chainA.head) fcs = chainA.head
- else if (chainA contains chainB.head) fcs = chainB.head
- else {
- chainA = chainA.tail
- chainB = chainB.tail
- }
- } while (fcs == null)
- fcs.c
- }
-
/* An `asm.ClassWriter` that uses `jvmWiseLUB()`
* The internal name of the least common ancestor of the types given by inameA and inameB.
* It's what ASM needs to know in order to compute stack map frames, http://asm.ow2.org/doc/developer-guide.html#controlflow
@@ -78,57 +60,17 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
final class CClassWriter(flags: Int) extends asm.ClassWriter(flags) {
/**
- * This method is thread re-entrant because chrs never grows during its operation (that's
- * because all TypeNames being looked up have already been entered).
- * To stress this point, rather than using `newTypeName()` we use `lookupTypeName()`
- *
- * can-multi-thread
+ * This method is thread-safe: it depends only on the BTypes component, which does not depend
+ * on global. TODO @lry move to a different place where no global is in scope, on bTypes.
*/
override def getCommonSuperClass(inameA: String, inameB: String): String = {
- val a = ClassBType(lookupTypeName(inameA.toCharArray))
- val b = ClassBType(lookupTypeName(inameB.toCharArray))
- val lca = jvmWiseLUB(a, b)
- val lcaName = lca.internalName // don't call javaName because that side-effects innerClassBuffer.
- assert(lcaName != "scala/Any")
-
- lcaName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things.
- }
-
- }
-
- /**
- * Finding the least upper bound in agreement with the bytecode verifier (given two internal names
- * handed out by ASM)
- * Background:
- * http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
- * http://comments.gmane.org/gmane.comp.java.vm.languages/2293
- * https://issues.scala-lang.org/browse/SI-3872
- *
- * can-multi-thread
- */
- def jvmWiseLUB(a: ClassBType, b: ClassBType): ClassBType = {
-
- assert(a.isNonSpecial, s"jvmWiseLUB() received a non-plain-class $a")
- assert(b.isNonSpecial, s"jvmWiseLUB() received a non-plain-class $b")
-
- val ta = exemplars.get(a)
- val tb = exemplars.get(b)
-
- val res = (ta.isInterface, tb.isInterface) match {
- case (true, true) =>
- // exercised by test/files/run/t4761.scala
- if (tb.isSubtypeOf(ta.c)) ta.c
- else if (ta.isSubtypeOf(tb.c)) tb.c
- else ObjectReference
- case (true, false) =>
- if (tb.isSubtypeOf(a)) a else ObjectReference
- case (false, true) =>
- if (ta.isSubtypeOf(b)) b else ObjectReference
- case _ =>
- firstCommonSuffix(ta :: ta.superClasses, tb :: tb.superClasses)
+ val a = classBTypeFromInternalName(inameA)
+ val b = classBTypeFromInternalName(inameB)
+ val lub = a.jvmWiseLUB(b)
+ val lubName = lub.internalName
+ assert(lubName != "scala/Any")
+ lubName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things.
}
- assert(res.isNonSpecial, "jvmWiseLUB() returned a non-plain-class.")
- res
}
/*
@@ -230,7 +172,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
def fieldSymbols(cls: Symbol): List[Symbol] = {
for (f <- cls.info.decls.toList ;
if !f.isMethod && f.isTerm && !f.isModule
- ) yield f;
+ ) yield f
}
/*
@@ -261,38 +203,16 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*
* can-multi-thread
*/
- final def addInnerClassesASM(jclass: asm.ClassVisitor, refedInnerClasses: Iterable[BType]) {
- // used to detect duplicates.
- val seen = mutable.Map.empty[String, String]
- // result without duplicates, not yet sorted.
- val result = mutable.Set.empty[InnerClassEntry]
-
- for(s: BType <- refedInnerClasses;
- e: InnerClassEntry <- exemplars.get(s).innersChain) {
-
- assert(e.name != null, "saveInnerClassesFor() is broken.") // documentation
- val doAdd = seen.get(e.name) match {
- // TODO is it ok for prevOName to be null? (Someone should really document the invariants of the InnerClasses bytecode attribute)
- case Some(prevOName) =>
- // this occurs e.g. when innerClassBuffer contains both class Thread$State, object Thread$State,
- // i.e. for them it must be the case that oname == java/lang/Thread
- assert(prevOName == e.outerName, "duplicate")
- false
- case None => true
- }
-
- if (doAdd) {
- seen += (e.name -> e.outerName)
- result += e
- }
-
+ final def addInnerClassesASM(jclass: asm.ClassVisitor, refedInnerClasses: List[ClassBType]) {
+ val allNestedClasses = refedInnerClasses.flatMap(_.enclosingNestedClassesChain).distinct
+
+ // sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler
+ for (nestedClass <- allNestedClasses.sortBy(_.internalName.toString)) {
+ // Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes.
+ val Some(e) = nestedClass.innerClassAttributeEntry
+ jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags)
}
- // sorting ensures inner classes are listed after their enclosing class thus satisfying the Eclipse Java compiler
- for(e <- result.toList sortBy (_.name.toString)) {
- jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.access)
- }
-
- } // end of method addInnerClassesASM()
+ }
/*
* Custom attribute (JVMS 4.7.1) "ScalaSig" used as marker only
@@ -323,8 +243,8 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
* can-multi-thread
*/
def createJAttribute(name: String, b: Array[Byte], offset: Int, len: Int): asm.Attribute = {
- val dest = new Array[Byte](len);
- System.arraycopy(b, offset, dest, 0, len);
+ val dest = new Array[Byte](len)
+ System.arraycopy(b, offset, dest, 0, len)
new asm.CustomAttr(name, dest)
}
@@ -385,9 +305,9 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
def debugLevel = settings.debuginfo.indexOfChoice
- val emitSource = debugLevel >= 1
- val emitLines = debugLevel >= 2
- val emitVars = debugLevel >= 3
+ final val emitSource = debugLevel >= 1
+ final val emitLines = debugLevel >= 2
+ final val emitVars = debugLevel >= 3
/*
* Contains class-symbols that:
@@ -396,152 +316,135 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*
* In other words, the lifetime of `innerClassBufferASM` is associated to "the class being generated".
*/
- val innerClassBufferASM = mutable.Set.empty[BType]
+ final val innerClassBufferASM = mutable.Set.empty[ClassBType]
- /*
- * Tracks (if needed) the inner class given by `sym`.
- *
- * must-single-thread
+ /**
+ * The class internal name for a given class symbol. If the symbol describes a nested class, the
+ * ClassBType is added to the innerClassBufferASM.
*/
- final def internalName(sym: Symbol): String = asmClassType(sym).internalName
+ final def internalName(sym: Symbol): String = {
+ // For each java class, the scala compiler creates a class and a module (thus a module class).
+ // If the `sym` is a java module class, we use the java class instead. This ensures that we
+ // register the class (instead of the module class) in innerClassBufferASM.
+ // The two symbols have the same name, so the resulting internalName is the same.
+ val classSym = if (sym.isJavaDefined && sym.isModuleClass) sym.linkedClassOfClass else sym
+ getClassBTypeAndRegisterInnerClass(classSym).internalName
+ }
- /*
- * Tracks (if needed) the inner class given by `sym`.
+ private def assertClassNotArray(sym: Symbol): Unit = {
+ assert(sym.isClass, sym)
+ assert(sym != definitions.ArrayClass || isCompilingArray, sym)
+ }
+
+ private def assertClassNotArrayNotPrimitive(sym: Symbol): Unit = {
+ assertClassNotArray(sym)
+ assert(!primitiveTypeMap.contains(sym) || isCompilingPrimitive, sym)
+ }
+
+ /**
+ * The ClassBType for a class symbol. If the class is nested, the ClassBType is added to the
+ * innerClassBufferASM.
*
- * must-single-thread
+ * The class symbol scala.Nothing is mapped to the class scala.runtime.Nothing$. Similarly,
+ * scala.Null is mapped to scala.runtime.Null$. This is because there exist no class files
+ * for the Nothing / Null. If used for example as a parameter type, we use the runtime classes
+ * in the classfile method signature.
+ *
+ * Note that the referenced class symbol may be an implementation class. For example when
+ * compiling a mixed-in method that forwards to the static method in the implementation class,
+ * the class descriptor of the receiver (the implementation class) is obtained by creating the
+ * ClassBType.
*/
- final def asmClassType(sym: Symbol): ClassBType = {
- assert(
- hasInternalName(sym),
- {
- val msg0 = if (sym.isAbstractType) "An AbstractTypeSymbol (SI-7122) " else "A symbol ";
- msg0 + s"has reached the bytecode emitter, for which no JVM-level internal name can be found: ${sym.fullName}"
- }
- )
- val phantOpt = phantomTypeMap.get(sym)
- if (phantOpt.isDefined) {
- return phantOpt.get
- }
- val tracked = exemplar(sym)
- val tk = tracked.c
- if (tracked.isInnerClass) {
- innerClassBufferASM += tk
- }
+ final def getClassBTypeAndRegisterInnerClass(sym: Symbol): ClassBType = {
+ assertClassNotArrayNotPrimitive(sym)
- tk
+ if (sym == definitions.NothingClass) RT_NOTHING
+ else if (sym == definitions.NullClass) RT_NULL
+ else {
+ val r = classBTypeFromSymbol(sym)
+ if (r.isNestedClass) innerClassBufferASM += r
+ r
+ }
}
- /*
- * Returns the BType for the given type.
- * Tracks (if needed) the inner class given by `t`.
+ /**
+ * This method returns the BType for a type reference, for example a parameter type.
*
- * must-single-thread
+ * If the result is a ClassBType for a nested class, it is added to the innerClassBufferASM.
+ *
+ * If `t` references a class, toTypeKind ensures that the class is not an implementation class.
+ * See also comment on getClassBTypeAndRegisterInnerClass, which is invoked for implementation
+ * classes.
*/
final def toTypeKind(t: Type): BType = {
+ import definitions.ArrayClass
- /* Interfaces have to be handled delicately to avoid introducing spurious errors,
- * but if we treat them all as AnyRef we lose too much information.
+ /**
+ * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int.
+ * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType.
*/
- def newReference(sym0: Symbol): BType = {
- assert(!primitiveTypeMap.contains(sym0), "Use primitiveTypeMap instead.")
- assert(sym0 != definitions.ArrayClass, "Use arrayOf() instead.")
-
- if (sym0 == definitions.NullClass) return RT_NULL;
- if (sym0 == definitions.NothingClass) return RT_NOTHING;
-
- val sym = (
- if (!sym0.isPackageClass) sym0
- else sym0.info.member(nme.PACKAGE) match {
- case NoSymbol => abort(s"SI-5604: Cannot use package as value: ${sym0.fullName}")
- case s => abort(s"SI-5604: found package class where package object expected: $s")
- }
- )
-
- // Can't call .toInterface (at this phase) or we trip an assertion.
- // See PackratParser#grow for a method which fails with an apparent mismatch
- // between "object PackratParsers$class" and "trait PackratParsers"
- if (sym.isImplClass) {
- // pos/spec-List.scala is the sole failure if we don't check for NoSymbol
- val traitSym = sym.owner.info.decl(tpnme.interfaceName(sym.name))
- if (traitSym != NoSymbol) {
- // this tracks the inner class in innerClassBufferASM, if needed.
- return asmClassType(traitSym)
- }
- }
-
- assert(hasInternalName(sym), s"Invoked for a symbol lacking JVM internal name: ${sym.fullName}")
- assert(!phantomTypeMap.contains(sym), "phantom types not supposed to reach here.")
-
- val tracked = exemplar(sym)
- val tk = tracked.c
- if (tracked.isInnerClass) {
- innerClassBufferASM += tk
- }
-
- tk
+ def primitiveOrClassToBType(sym: Symbol): BType = {
+ assertClassNotArray(sym)
+ assert(!sym.isImplClass, sym)
+ primitiveTypeMap.getOrElse(sym, getClassBTypeAndRegisterInnerClass(sym))
}
- def primitiveOrRefType(sym: Symbol): BType = {
- assert(sym != definitions.ArrayClass, "Use primitiveOrArrayOrRefType() instead.")
-
- primitiveTypeMap.getOrElse(sym, newReference(sym))
- }
-
- def primitiveOrRefType2(sym: Symbol): BType = {
- primitiveTypeMap.get(sym) match {
- case Some(pt) => pt
- case None =>
- sym match {
- case definitions.NullClass => RT_NULL
- case definitions.NothingClass => RT_NOTHING
- case _ if sym.isClass => newReference(sym)
- case _ =>
- assert(sym.isType, sym) // it must be compiling Array[a]
- ObjectReference
- }
- }
+ /**
+ * When compiling Array.scala, the type parameter T is not erased and shows up in method
+ * signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference.
+ */
+ def nonClassTypeRefToBType(sym: Symbol): ClassBType = {
+ assert(sym.isType && isCompilingArray, sym)
+ ObjectReference
}
- import definitions.ArrayClass
-
- // Call to .normalize fixes #3003 (follow type aliases). Otherwise, primitiveOrArrayOrRefType() would return ObjectReference.
- t.normalize match {
-
- case ThisType(sym) =>
- if (sym == ArrayClass) ObjectReference
- else phantomTypeMap.getOrElse(sym, exemplar(sym).c)
-
- case SingleType(_, sym) => primitiveOrRefType(sym)
-
- case _: ConstantType => toTypeKind(t.underlying)
-
- case TypeRef(_, sym, args) =>
- if (sym == ArrayClass) ArrayBType(toTypeKind(args.head))
- else primitiveOrRefType2(sym)
-
- case ClassInfoType(_, _, sym) =>
- assert(sym != ArrayClass, "ClassInfoType to ArrayClass!")
- primitiveOrRefType(sym)
-
- // TODO @lry check below comments / todo's
- // !!! Iulian says types which make no sense after erasure should not reach here, which includes the ExistentialType, AnnotatedType, RefinedType.
- case ExistentialType(_, t) => toTypeKind(t) // TODO shouldn't get here but the following does: akka-actor/src/main/scala/akka/util/WildcardTree.scala
- case AnnotatedType(_, w) => toTypeKind(w) // TODO test/files/jvm/annotations.scala causes an AnnotatedType to reach here.
- case RefinedType(parents, _) => parents.map(toTypeKind(_).asClassBType) reduceLeft jvmWiseLUB
-
- // For sure WildcardTypes shouldn't reach here either, but when debugging such situations this may come in handy.
- // case WildcardType => REFERENCE(ObjectClass)
- case norm => abort(
- s"Unknown type: $t, $norm [${t.getClass}, ${norm.getClass}] TypeRef? ${t.isInstanceOf[TypeRef]}"
- )
+ t.dealiasWiden match {
+ case TypeRef(_, ArrayClass, List(arg)) => ArrayBType(toTypeKind(arg)) // Array type such as Array[Int] (kept by erasure)
+ case TypeRef(_, sym, _) if !sym.isClass => nonClassTypeRefToBType(sym) // See comment on nonClassTypeRefToBType
+ case TypeRef(_, sym, _) => primitiveOrClassToBType(sym) // Common reference to a type such as scala.Int or java.lang.String
+ case ClassInfoType(_, _, sym) => primitiveOrClassToBType(sym) // We get here, for example, for genLoadModule, which invokes toTypeKind(moduleClassSymbol.info)
+
+ /* AnnotatedType should (probably) be eliminated by erasure. However we know it happens for
+ * meta-annotated annotations (@(ann @getter) val x = 0), so we don't emit a warning.
+ * The type in the AnnotationInfo is an AnnotatedTpe. Tested in jvm/annotations.scala.
+ */
+ case a @ AnnotatedType(_, t) =>
+ debuglog(s"typeKind of annotated type $a")
+ toTypeKind(t)
+
+ /* ExistentialType should (probably) be eliminated by erasure. We know they get here for
+ * classOf constants:
+ * class C[T]
+ * class T { final val k = classOf[C[_]] }
+ */
+ case e @ ExistentialType(_, t) =>
+ debuglog(s"typeKind of existential type $e")
+ toTypeKind(t)
+
+ /* The cases below should probably never occur. They are kept for now to avoid introducing
+ * new compiler crashes, but we added a warning. The compiler / library bootstrap and the
+ * test suite don't produce any warning.
+ */
+
+ case tp =>
+ currentUnit.warning(tp.typeSymbol.pos,
+ s"an unexpected type representation reached the compiler backend while compiling $currentUnit: $tp. " +
+ "If possible, please file a bug on issues.scala-lang.org.")
+
+ tp match {
+ case ThisType(ArrayClass) => ObjectReference // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test
+ case ThisType(sym) => getClassBTypeAndRegisterInnerClass(sym)
+ case SingleType(_, sym) => primitiveOrClassToBType(sym)
+ case ConstantType(_) => toTypeKind(t.underlying)
+ case RefinedType(parents, _) => parents.map(toTypeKind(_).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b))
+ }
}
-
- } // end of method toTypeKind()
+ }
/*
* must-single-thread
*/
- def asmMethodType(msym: Symbol): MethodBType = {
+ final def asmMethodType(msym: Symbol): MethodBType = {
assert(msym.isMethod, s"not a method-symbol: $msym")
val resT: BType =
if (msym.isClassConstructor || msym.isConstructor) UNIT
@@ -549,45 +452,17 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
MethodBType(msym.tpe.paramTypes map toTypeKind, resT)
}
- /*
- * Returns all direct member inner classes of `csym`,
- * thus making sure they get entries in the InnerClasses JVM attribute
- * even if otherwise not mentioned in the class being built.
- *
- * must-single-thread
- */
- final def trackMemberClasses(csym: Symbol, lateClosuresBTs: List[BType]): List[BType] = {
- val lateInnerClasses = exitingErasure {
- for (sym <- List(csym, csym.linkedClassOfClass); memberc <- sym.info.decls.map(innerClassSymbolFor) if memberc.isClass)
- yield memberc
- }
- // as a precaution, do the following outside the above `exitingErasure` otherwise funny internal names might be computed.
- val result = for(memberc <- lateInnerClasses) yield {
- val tracked = exemplar(memberc)
- val memberCTK = tracked.c
- assert(tracked.isInnerClass, s"saveInnerClassesFor() says this was no inner-class after all: ${memberc.fullName}")
-
- memberCTK
- }
-
- exemplar(csym).directMemberClasses = result
-
- result
- }
-
- /*
- * Tracks (if needed) the inner class given by `t`.
- *
- * must-single-thread
+ /**
+ * The jvm descriptor of a type. If `t` references a nested class, its ClassBType is added to
+ * the innerClassBufferASM.
*/
final def descriptor(t: Type): String = { toTypeKind(t).descriptor }
- /*
- * Tracks (if needed) the inner class given by `sym`.
- *
- * must-single-thread
+ /**
+ * The jvm descriptor for a symbol. If `sym` represents a nested class, its ClassBType is added
+ * to the innerClassBufferASM.
*/
- final def descriptor(sym: Symbol): String = { asmClassType(sym).descriptor }
+ final def descriptor(sym: Symbol): String = { getClassBTypeAndRegisterInnerClass(sym).descriptor }
} // end of trait BCInnerClassGen
@@ -792,7 +667,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*/
// TODO: evaluate the other flags we might be dropping on the floor here.
// TODO: ACC_SYNTHETIC ?
- val flags = PublicStatic | (
+ val flags = GenBCode.PublicStatic | (
if (m.isVarargsMethod) asm.Opcodes.ACC_VARARGS else 0
)
@@ -898,62 +773,13 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
def addSerialVUID(id: Long, jclass: asm.ClassVisitor) {
// add static serialVersionUID field if `clasz` annotated with `@SerialVersionUID(uid: Long)`
jclass.visitField(
- PublicStaticFinal,
+ GenBCode.PublicStaticFinal,
"serialVersionUID",
"J",
null, // no java-generic-signature
new java.lang.Long(id)
).visitEnd()
}
-
- /*
- * @param owner internal name of the enclosing class of the class.
- *
- * @param name the name of the method that contains the class.
-
- * @param methodType the method that contains the class.
- */
- case class EnclMethodEntry(owner: String, name: String, methodType: BType)
-
- /*
- * @return null if the current class is not internal to a method
- *
- * Quoting from JVMS 4.7.7 The EnclosingMethod Attribute
- * A class must have an EnclosingMethod attribute if and only if it is a local class or an anonymous class.
- * A class may have no more than one EnclosingMethod attribute.
- *
- * must-single-thread
- */
- def getEnclosingMethodAttribute(clazz: Symbol): EnclMethodEntry = { // JVMS 4.7.7
-
- def newEEE(eClass: Symbol, m: Symbol) = {
- EnclMethodEntry(
- internalName(eClass),
- m.javaSimpleName.toString,
- asmMethodType(m)
- )
- }
-
- var res: EnclMethodEntry = null
- val sym = clazz.originalEnclosingMethod
- if (sym.isMethod) {
- debuglog(s"enclosing method for $clazz is $sym (in ${sym.enclClass})")
- res = newEEE(sym.enclClass, sym)
- } else if (clazz.isAnonymousClass) {
- val enclClass = clazz.rawowner
- assert(enclClass.isClass, enclClass)
- val sym = enclClass.primaryConstructor
- if (sym == NoSymbol) {
- log(s"Ran out of room looking for an enclosing method for $clazz: no constructor here: $enclClass.")
- } else {
- debuglog(s"enclosing method for $clazz is $sym (in $enclClass)")
- res = newEEE(enclClass, sym)
- }
- }
-
- res
- }
-
} // end of trait BCClassGen
/* basic functionality for class file building of plain, mirror, and beaninfo classes. */
@@ -982,11 +808,12 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*
* must-single-thread
*/
- def genMirrorClass(modsym: Symbol, cunit: CompilationUnit): asm.tree.ClassNode = {
- assert(modsym.companionClass == NoSymbol, modsym)
+ def genMirrorClass(moduleClass: Symbol, cunit: CompilationUnit): asm.tree.ClassNode = {
+ assert(moduleClass.isModuleClass)
+ assert(moduleClass.companionClass == NoSymbol, moduleClass)
innerClassBufferASM.clear()
this.cunit = cunit
- val moduleName = internalName(modsym) // + "$"
+ val moduleName = internalName(moduleClass) // + "$"
val mirrorName = moduleName.substring(0, moduleName.length() - 1)
val flags = (asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL)
@@ -996,7 +823,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
flags,
mirrorName,
null /* no java-generic-signature */,
- JAVA_LANG_OBJECT.internalName,
+ ObjectReference.internalName,
EMPTY_STRING_ARRAY
)
@@ -1005,18 +832,18 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
null /* SourceDebugExtension */)
}
- val ssa = getAnnotPickle(mirrorName, modsym.companionSymbol)
+ val ssa = getAnnotPickle(mirrorName, moduleClass.companionSymbol)
mirrorClass.visitAttribute(if (ssa.isDefined) pickleMarkerLocal else pickleMarkerForeign)
- emitAnnotations(mirrorClass, modsym.annotations ++ ssa)
+ emitAnnotations(mirrorClass, moduleClass.annotations ++ ssa)
- addForwarders(isRemote(modsym), mirrorClass, mirrorName, modsym)
+ addForwarders(isRemote(moduleClass), mirrorClass, mirrorName, moduleClass)
- innerClassBufferASM ++= trackMemberClasses(modsym, Nil /* TODO what about Late-Closure-Classes */ )
+ innerClassBufferASM ++= classBTypeFromSymbol(moduleClass).info.memberClasses
addInnerClassesASM(mirrorClass, innerClassBufferASM.toList)
mirrorClass.visitEnd()
- ("" + modsym.name) // this side-effect is necessary, really.
+ ("" + moduleClass.name) // this side-effect is necessary, really.
mirrorClass
}
@@ -1039,10 +866,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
innerClassBufferASM.clear()
- val flags = mkFlags(
- javaFlags(cls),
- if (isDeprecated(cls)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
- )
+ val flags = javaFlags(cls)
val beanInfoName = (internalName(cls) + "BeanInfo")
val beanInfoClass = new asm.tree.ClassNode
@@ -1088,9 +912,9 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
EMPTY_STRING_ARRAY // no throwable exceptions
)
- val stringArrayJType: BType = ArrayBType(JAVA_LANG_STRING)
+ val stringArrayJType: BType = ArrayBType(StringReference)
val conJType: BType = MethodBType(
- exemplar(definitions.ClassClass).c :: stringArrayJType :: stringArrayJType :: Nil,
+ classBTypeFromSymbol(definitions.ClassClass) :: stringArrayJType :: stringArrayJType :: Nil,
UNIT
)
@@ -1101,7 +925,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
constructor.visitLdcInsn(new java.lang.Integer(fi))
if (f == null) { constructor.visitInsn(asm.Opcodes.ACONST_NULL) }
else { constructor.visitLdcInsn(f) }
- constructor.visitInsn(JAVA_LANG_STRING.typedOpcode(asm.Opcodes.IASTORE))
+ constructor.visitInsn(StringReference.typedOpcode(asm.Opcodes.IASTORE))
fi += 1
}
}
@@ -1110,16 +934,16 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
constructor.visitVarInsn(asm.Opcodes.ALOAD, 0)
// push the class
- constructor.visitLdcInsn(exemplar(cls).c.toASMType)
+ constructor.visitLdcInsn(classBTypeFromSymbol(cls).toASMType)
// push the string array of field information
constructor.visitLdcInsn(new java.lang.Integer(fieldList.length))
- constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.internalName)
+ constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, StringReference.internalName)
push(fieldList)
// push the string array of method information
constructor.visitLdcInsn(new java.lang.Integer(methodList.length))
- constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.internalName)
+ constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, StringReference.internalName)
push(methodList)
// invoke the superclass constructor, which will do the
@@ -1130,7 +954,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments
constructor.visitEnd()
- innerClassBufferASM ++= trackMemberClasses(cls, Nil /* TODO what about Late-Closure-Classes */ )
+ innerClassBufferASM ++= classBTypeFromSymbol(cls).info.memberClasses
addInnerClassesASM(beanInfoClass, innerClassBufferASM.toList)
beanInfoClass.visitEnd()
@@ -1162,11 +986,11 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*/
def legacyAddCreatorCode(clinit: asm.MethodVisitor, cnode: asm.tree.ClassNode, thisName: String) {
// this tracks the inner class in innerClassBufferASM, if needed.
- val androidCreatorType = asmClassType(AndroidCreatorClass)
+ val androidCreatorType = getClassBTypeAndRegisterInnerClass(AndroidCreatorClass)
val tdesc_creator = androidCreatorType.descriptor
cnode.visitField(
- PublicStaticFinal,
+ GenBCode.PublicStaticFinal,
"CREATOR",
tdesc_creator,
null, // no java-generic-signature
@@ -1203,5 +1027,4 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
}
} // end of trait JAndroidBuilder
-
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index 9b7c975960..d58368b19d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
@@ -19,29 +19,23 @@ import scala.collection.mutable
*
*/
abstract class BCodeIdiomatic extends SubComponent {
- protected val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global)
-
- val bTypes = new BTypes[global.type](global) {
- def chrs = global.chrs
- override type BTypeName = global.TypeName
- override def createNewName(s: String) = global.newTypeName(s)
- }
+ val bTypes = new BTypesFromSymbols[global.type](global)
import global._
import bTypes._
+ import coreBTypes._
val classfileVersion: Int = settings.target.value match {
case "jvm-1.5" => asm.Opcodes.V1_5
case "jvm-1.6" => asm.Opcodes.V1_6
case "jvm-1.7" => asm.Opcodes.V1_7
+ case "jvm-1.8" => asm.Opcodes.V1_8
}
val majorVersion: Int = (classfileVersion & 0xFF)
val emitStackMapFrame = (majorVersion >= 50)
- def mkFlags(args: Int*) = args.foldLeft(0)(_ | _)
-
- val extraProc: Int = mkFlags(
+ val extraProc: Int = GenBCode.mkFlags(
asm.ClassWriter.COMPUTE_MAXS,
if (emitStackMapFrame) asm.ClassWriter.COMPUTE_FRAMES else 0
)
@@ -51,15 +45,6 @@ abstract class BCodeIdiomatic extends SubComponent {
val CLASS_CONSTRUCTOR_NAME = "<clinit>"
val INSTANCE_CONSTRUCTOR_NAME = "<init>"
- val ObjectReference = ClassBType("java/lang/Object")
- val AnyRefReference = ObjectReference
- val objArrayReference = ArrayBType(ObjectReference)
-
- val JAVA_LANG_OBJECT = ObjectReference
- val JAVA_LANG_STRING = ClassBType("java/lang/String")
-
- var StringBuilderReference: BType = null
-
val EMPTY_STRING_ARRAY = Array.empty[String]
val EMPTY_INT_ARRAY = Array.empty[Int]
val EMPTY_LABEL_ARRAY = Array.empty[asm.Label]
@@ -238,7 +223,7 @@ abstract class BCodeIdiomatic extends SubComponent {
final def genStringConcat(el: BType) {
val jtype =
- if (el.isArray || el.isClass) JAVA_LANG_OBJECT
+ if (el.isArray || el.isClass) ObjectReference
else el
val bt = MethodBType(List(jtype), StringBuilderReference)
@@ -265,7 +250,7 @@ abstract class BCodeIdiomatic extends SubComponent {
assert(
from.isNonVoidPrimitiveType && to.isNonVoidPrimitiveType,
- s"Cannot emit primitive conversion from $from to $to"
+ s"Cannot emit primitive conversion from $from to $to - ${global.currentUnit}"
)
def pickOne(opcs: Array[Int]) { // TODO index on to.sort
@@ -536,7 +521,7 @@ abstract class BCodeIdiomatic extends SubComponent {
final def emitTypeBased(opcs: Array[Int], tk: BType) {
assert(tk != UNIT, tk)
val opc = {
- if (tk.isRef) { opcs(0) }
+ if (tk.isRef) { opcs(0) }
else if (tk.isIntSizedType) {
(tk: @unchecked) match {
case BOOL | BYTE => opcs(1)
@@ -647,7 +632,7 @@ abstract class BCodeIdiomatic extends SubComponent {
*/
final def coercionTo(code: Int): BType = {
import scalaPrimitives._
- (code: @scala.annotation.switch) match {
+ (code: @switch) match {
case B2B | C2B | S2B | I2B | L2B | F2B | D2B => BYTE
case B2C | C2C | S2C | I2C | L2C | F2C | D2C => CHAR
case B2S | C2S | S2S | I2S | L2S | F2S | D2S => SHORT
@@ -658,21 +643,6 @@ abstract class BCodeIdiomatic extends SubComponent {
}
}
- final val typeOfArrayOp: Map[Int, BType] = {
- import scalaPrimitives._
- Map(
- (List(ZARRAY_LENGTH, ZARRAY_GET, ZARRAY_SET) map (_ -> BOOL)) ++
- (List(BARRAY_LENGTH, BARRAY_GET, BARRAY_SET) map (_ -> BYTE)) ++
- (List(SARRAY_LENGTH, SARRAY_GET, SARRAY_SET) map (_ -> SHORT)) ++
- (List(CARRAY_LENGTH, CARRAY_GET, CARRAY_SET) map (_ -> CHAR)) ++
- (List(IARRAY_LENGTH, IARRAY_GET, IARRAY_SET) map (_ -> INT)) ++
- (List(LARRAY_LENGTH, LARRAY_GET, LARRAY_SET) map (_ -> LONG)) ++
- (List(FARRAY_LENGTH, FARRAY_GET, FARRAY_SET) map (_ -> FLOAT)) ++
- (List(DARRAY_LENGTH, DARRAY_GET, DARRAY_SET) map (_ -> DOUBLE)) ++
- (List(OARRAY_LENGTH, OARRAY_GET, OARRAY_SET) map (_ -> ObjectReference)) : _*
- )
- }
-
/*
* Collects (in `result`) all LabelDef nodes enclosed (directly or not) by each node it visits.
*
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index 8845ffa0cd..4592031a31 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -26,6 +26,8 @@ import java.io.PrintWriter
abstract class BCodeSkelBuilder extends BCodeHelpers {
import global._
import bTypes._
+ import coreBTypes._
+ import bCodeAsmCommon._
/*
* There's a dedicated PlainClassBuilder for each CompilationUnit,
@@ -95,7 +97,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
claszSymbol = cd.symbol
isCZParcelable = isAndroidParcelableClass(claszSymbol)
- isCZStaticModule = isStaticModule(claszSymbol)
+ isCZStaticModule = isStaticModuleClass(claszSymbol)
isCZRemote = isRemote(claszSymbol)
thisName = internalName(claszSymbol)
@@ -116,7 +118,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
addClassFields()
- innerClassBufferASM ++= trackMemberClasses(claszSymbol, Nil)
+ innerClassBufferASM ++= classBTypeFromSymbol(claszSymbol).info.memberClasses
gen(cd.impl)
addInnerClassesASM(cnode, innerClassBufferASM.toList)
@@ -134,40 +136,28 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
private def initJClass(jclass: asm.ClassVisitor) {
val ps = claszSymbol.info.parents
- val superClass: String = if (ps.isEmpty) JAVA_LANG_OBJECT.internalName else internalName(ps.head.typeSymbol);
- val ifaces: Array[String] = {
- val arrIfacesTr: Array[Tracked] = exemplar(claszSymbol).ifaces
- val arrIfaces = new Array[String](arrIfacesTr.length)
- var i = 0
- while (i < arrIfacesTr.length) {
- val ifaceTr = arrIfacesTr(i)
- val bt = ifaceTr.c
- if (ifaceTr.isInnerClass) { innerClassBufferASM += bt }
- arrIfaces(i) = bt.internalName
- i += 1
- }
- arrIfaces
+ val superClass: String = if (ps.isEmpty) ObjectReference.internalName else internalName(ps.head.typeSymbol)
+ val interfaceNames = classBTypeFromSymbol(claszSymbol).info.interfaces map {
+ case classBType =>
+ if (classBType.isNestedClass) { innerClassBufferASM += classBType }
+ classBType.internalName
}
- // `internalName()` tracks inner classes.
- val flags = mkFlags(
- javaFlags(claszSymbol),
- if (isDeprecated(claszSymbol)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
- )
+ val flags = javaFlags(claszSymbol)
val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner)
cnode.visit(classfileVersion, flags,
thisName, thisSignature,
- superClass, ifaces)
+ superClass, interfaceNames.toArray)
if (emitSource) {
cnode.visitSource(cunit.source.toString, null /* SourceDebugExtension */)
}
- val enclM = getEnclosingMethodAttribute(claszSymbol)
- if (enclM != null) {
- val EnclMethodEntry(className, methodName, methodType) = enclM
- cnode.visitOuterClass(className, methodName, methodType.descriptor)
+ enclosingMethodAttribute(claszSymbol, internalName, asmMethodType(_).descriptor) match {
+ case Some(EnclosingMethodEntry(className, methodName, methodDescriptor)) =>
+ cnode.visitOuterClass(className, methodName, methodDescriptor)
+ case _ => ()
}
val ssa = getAnnotPickle(thisName, claszSymbol)
@@ -207,7 +197,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
*/
private def addModuleInstanceField() {
val fv =
- cnode.visitField(PublicStaticFinal, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
+ cnode.visitField(GenBCode.PublicStaticFinal, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
strMODULE_INSTANCE_FIELD,
"L" + thisName + ";",
null, // no java-generic-signature
@@ -223,7 +213,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
private def fabricateStaticInit() {
val clinit: asm.MethodVisitor = cnode.visitMethod(
- PublicStatic, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
+ GenBCode.PublicStatic, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
CLASS_CONSTRUCTOR_NAME,
"()V",
null, // no java-generic-signature
@@ -254,10 +244,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
*/
for (f <- fieldSymbols(claszSymbol)) {
val javagensig = getGenericSignature(f, claszSymbol)
- val flags = mkFlags(
- javaFieldFlags(f),
- if (isDeprecated(f)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
- )
+ val flags = javaFieldFlags(f)
val jfield = new asm.tree.FieldNode(
flags,
@@ -576,12 +563,11 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val isNative = methSymbol.hasAnnotation(definitions.NativeAttr)
val isAbstractMethod = (methSymbol.isDeferred || methSymbol.owner.isInterface)
- val flags = mkFlags(
+ val flags = GenBCode.mkFlags(
javaFlags(methSymbol),
if (claszSymbol.isInterface) asm.Opcodes.ACC_ABSTRACT else 0,
if (methSymbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0,
- if (isNative) asm.Opcodes.ACC_NATIVE else 0, // native methods of objects are generated in mirror classes
- if (isDeprecated(methSymbol)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
+ if (isNative) asm.Opcodes.ACC_NATIVE else 0 // native methods of objects are generated in mirror classes
)
// TODO needed? for(ann <- m.symbol.annotations) { ann.symbol.initialize }
@@ -695,7 +681,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
// android creator code
if (isCZParcelable) {
// add a static field ("CREATOR") to this class to cache android.os.Parcelable$Creator
- val andrFieldDescr = asmClassType(AndroidCreatorClass).descriptor
+ val andrFieldDescr = getClassBTypeAndRegisterInnerClass(AndroidCreatorClass).descriptor
cnode.visitField(
asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL,
"CREATOR",
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
index c271e7b129..7c95b7fc3b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
@@ -21,7 +21,7 @@ import scala.tools.asm
abstract class BCodeSyncAndTry extends BCodeBodyBuilder {
import global._
import bTypes._
-
+ import coreBTypes._
/*
* Functionality to lower `synchronized` and `try` expressions.
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
deleted file mode 100644
index 62dfb4917d..0000000000
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
+++ /dev/null
@@ -1,861 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2012 LAMP/EPFL
- * @author Martin Odersky
- */
-
-package scala
-package tools.nsc
-package backend.jvm
-
-import scala.tools.asm
-import scala.collection.{ immutable, mutable }
-
-/*
- * Utilities to mediate between types as represented in Scala ASTs and ASM trees.
- *
- * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded
- * @version 1.0
- *
- */
-abstract class BCodeTypes extends BCodeIdiomatic {
- import global._
- import bTypes._
-
- // when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface)
- val isCompilingStdLib = !(settings.sourcepath.isDefault)
-
- // special names
- var StringReference : ClassBType = null
- var ThrowableReference : ClassBType = null
- var jlCloneableReference : ClassBType = null // java/lang/Cloneable
- var jlNPEReference : ClassBType = null // java/lang/NullPointerException
- var jioSerializableReference : ClassBType = null // java/io/Serializable
- var scalaSerializableReference : ClassBType = null // scala/Serializable
- var classCastExceptionReference : ClassBType = null // java/lang/ClassCastException
-
- /* A map from scala primitive type-symbols to BTypes */
- var primitiveTypeMap: Map[Symbol, BType] = null
- /* A map from scala type-symbols for Nothing and Null to (runtime version) BTypes */
- var phantomTypeMap: Map[Symbol, ClassBType] = null
- /* Maps the method symbol for a box method to the boxed type of the result.
- * For example, the method symbol for `Byte.box()`) is mapped to the BType `Ljava/lang/Integer;`. */
- var boxResultType: Map[Symbol, BType] = null
- /* Maps the method symbol for an unbox method to the primitive type of the result.
- * For example, the method symbol for `Byte.unbox()`) is mapped to the BType BYTE. */
- var unboxResultType: Map[Symbol, BType] = null
-
- var hashMethodSym: Symbol = null // scala.runtime.ScalaRunTime.hash
-
- var AndroidParcelableInterface: Symbol = null
- var AndroidCreatorClass : Symbol = null // this is an inner class, use asmType() to get hold of its BType while tracking in innerClassBufferASM
-
- var BeanInfoAttr: Symbol = null
-
- /* The Object => String overload. */
- var String_valueOf: Symbol = null
-
- var ArrayInterfaces: Set[Tracked] = null
-
- // scala.FunctionX and scala.runtim.AbstractFunctionX
- val FunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1)
- val AbstractFunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1)
- val abstractFunctionArityMap = mutable.Map.empty[BType, Int]
-
- var PartialFunctionReference: ClassBType = null // scala.PartialFunction
- var AbstractPartialFunctionReference: ClassBType = null // scala.runtime.AbstractPartialFunction
-
- var BoxesRunTime: ClassBType = null
-
- /*
- * must-single-thread
- */
- def initBCodeTypes() {
- import definitions._
-
- primitiveTypeMap =
- Map(
- UnitClass -> UNIT,
- BooleanClass -> BOOL,
- CharClass -> CHAR,
- ByteClass -> BYTE,
- ShortClass -> SHORT,
- IntClass -> INT,
- LongClass -> LONG,
- FloatClass -> FLOAT,
- DoubleClass -> DOUBLE
- )
-
- phantomTypeMap =
- Map(
- NothingClass -> RT_NOTHING,
- NullClass -> RT_NULL,
- NothingClass -> RT_NOTHING, // we map on purpose to RT_NOTHING, getting rid of the distinction compile-time vs. runtime for NullClass.
- NullClass -> RT_NULL // ditto.
- )
-
- boxResultType =
- for((csym, msym) <- currentRun.runDefinitions.boxMethod)
- yield (msym -> classLiteral(primitiveTypeMap(csym)))
-
- unboxResultType =
- for((csym, msym) <- currentRun.runDefinitions.unboxMethod)
- yield (msym -> primitiveTypeMap(csym))
-
- // boxed classes are looked up in the `exemplars` map by jvmWiseLUB().
- // Other than that, they aren't needed there (e.g., `isSubtypeOf()` special-cases boxed classes, similarly for others).
- val boxedClasses = List(BoxedBooleanClass, BoxedCharacterClass, BoxedByteClass, BoxedShortClass, BoxedIntClass, BoxedLongClass, BoxedFloatClass, BoxedDoubleClass)
- for(csym <- boxedClasses) {
- val key = ClassBType(csym.javaBinaryName.toTypeName)
- val tr = buildExemplar(key, csym)
- symExemplars.put(csym, tr)
- exemplars.put(tr.c, tr)
- }
-
- // reversePrimitiveMap = (primitiveTypeMap map { case (s, pt) => (s.tpe, pt) } map (_.swap)).toMap
-
- hashMethodSym = getMember(ScalaRunTimeModule, nme.hash_)
-
- // TODO avoiding going through through missingHook for every line in the REPL: https://github.com/scala/scala/commit/8d962ed4ddd310cc784121c426a2e3f56a112540
- AndroidParcelableInterface = rootMirror.getClassIfDefined("android.os.Parcelable")
- AndroidCreatorClass = rootMirror.getClassIfDefined("android.os.Parcelable$Creator")
-
- // the following couldn't be an eager vals in Phase constructors:
- // that might cause cycles before Global has finished initialization.
- BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo")
-
- String_valueOf = {
- getMember(StringModule, nme.valueOf) filter (sym =>
- sym.info.paramTypes match {
- case List(pt) => pt.typeSymbol == ObjectClass
- case _ => false
- }
- )
- }
-
- exemplar(JavaCloneableClass)
- exemplar(JavaSerializableClass)
- exemplar(SerializableClass)
-
- StringReference = exemplar(StringClass).c
- StringBuilderReference = exemplar(StringBuilderClass).c
- ThrowableReference = exemplar(ThrowableClass).c
- jlCloneableReference = exemplar(JavaCloneableClass).c
- jlNPEReference = exemplar(NullPointerExceptionClass).c
- jioSerializableReference = exemplar(JavaSerializableClass).c
- scalaSerializableReference = exemplar(SerializableClass).c
- classCastExceptionReference = exemplar(ClassCastExceptionClass).c
-
- PartialFunctionReference = exemplar(PartialFunctionClass).c
- for(idx <- 0 to definitions.MaxFunctionArity) {
- FunctionReference(idx) = exemplar(FunctionClass(idx))
- AbstractFunctionReference(idx) = exemplar(AbstractFunctionClass(idx))
- abstractFunctionArityMap += (AbstractFunctionReference(idx).c -> idx)
- AbstractPartialFunctionReference = exemplar(AbstractPartialFunctionClass).c
- }
-
- BoxesRunTime = ClassBType("scala/runtime/BoxesRunTime")
- }
-
- /*
- * must-single-thread
- */
- def clearBCodeTypes() {
- symExemplars.clear()
- exemplars.clear()
- }
-
- val PublicStatic = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC
- val PublicStaticFinal = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL
-
- val strMODULE_INSTANCE_FIELD = nme.MODULE_INSTANCE_FIELD.toString
-
- // ------------------------------------------------
- // accessory maps tracking the isInterface, innerClasses, superClass, and supportedInterfaces relations,
- // allowing answering `conforms()` without resorting to typer.
- // ------------------------------------------------
-
- /**
- * TODO @lry should probably be a map form ClassBType to Tracked
- */
- val exemplars = new java.util.concurrent.ConcurrentHashMap[BType, Tracked]
-
- /**
- * Maps class symbols to their corresponding `Tracked` instance.
- */
- val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked]
-
- /**
- * A `Tracked` instance stores information about a BType. This allows ansering type questions
- * without resolving to the compiler, in a thread-safe manner, in particular isSubtypeOf.
- *
- * @param c the BType described by this `Tracked`
- * @param flags the java flags for the type, computed by BCodeTypes#javaFlags
- * @param sc the bytecode-level superclass if any, null otherwise
- * @param ifaces the interfaces explicitly declared. Not included are those transitively
- * supported, but the utility method `allLeafIfaces()` can be used for that.
- * @param innersChain the containing classes for a non-package-level class `c`, null otherwise.
- *
- * Note: the optimizer may inline anonymous closures, thus eliding those inner classes (no
- * physical class file is emitted for elided classes). Before committing `innersChain` to
- * bytecode, cross-check with the list of elided classes (SI-6546).
- *
- * All methods of this class can-multi-thread
- *
- * TODO @lry c: ClassBType. rename to ClassBTypeInfo
- */
- case class Tracked(c: ClassBType, flags: Int, sc: Tracked, ifaces: Array[Tracked], innersChain: Array[InnerClassEntry]) {
-
- // not a case-field because we initialize it only for JVM classes we emit.
- // TODO @lry make it an Option[List[BType]]
- // TODO: this is currently not used. a commit in the optimizer branch uses this field to
- // re-compute inner classes (ee4c185). leaving it in for now.
- private var _directMemberClasses: List[BType] = null
-
- def directMemberClasses: List[BType] = {
- assert(_directMemberClasses != null, s"getter directMemberClasses() invoked too early for $c")
- _directMemberClasses
- }
-
- def directMemberClasses_=(bs: List[BType]) {
- if (_directMemberClasses != null) {
- // TODO we enter here when both mirror class and plain class are emitted for the same ModuleClassSymbol.
- assert(_directMemberClasses.sameElements(bs))
- }
- _directMemberClasses = bs
- }
-
- /* `isCompilingStdLib` saves the day when compiling:
- * (1) scala.Nothing (the test `c.isNonSpecial` fails for it)
- * (2) scala.Boolean (it has null superClass and is not an interface)
- */
- assert(c.isNonSpecial || isCompilingStdLib /*(1)*/, s"non well-formed plain-type: $this")
- assert(
- if (sc == null) { (c == ObjectReference) || isInterface || isCompilingStdLib /*(2)*/ }
- else { (c != ObjectReference) && !sc.isInterface }
- , "non well-formed plain-type: " + this
- )
- assert(ifaces.forall(i => i.c.isNonSpecial && i.isInterface), s"non well-formed plain-type: $this")
-
- import asm.Opcodes._
- def hasFlags(mask: Int) = (flags & mask) != 0
- def isInterface = hasFlags(ACC_INTERFACE)
- def isFinal = hasFlags(ACC_FINAL)
- def isInnerClass = { innersChain != null }
- def isLambda = {
- // ie isLCC || isTraditionalClosureClass
- isFinal && (c.simpleName.contains(tpnme.ANON_FUN_NAME.toString)) && isFunctionType(c)
- }
-
- /* can-multi-thread */
- def superClasses: List[Tracked] = {
- if (sc == null) Nil else sc :: sc.superClasses
- }
-
- /* can-multi-thread */
- def isSubtypeOf(other: BType): Boolean = {
- assert(other.isNonSpecial, "so called special cases have to be handled in BCodeTypes.conforms()")
-
- if (c == other) return true;
-
- val otherIsIface = exemplars.get(other).isInterface
-
- if (this.isInterface) {
- if (other == ObjectReference) return true;
- if (!otherIsIface) return false;
- }
- else {
- if (sc != null && sc.isSubtypeOf(other)) return true;
- if (!otherIsIface) return false;
- }
-
- var idx = 0
- while (idx < ifaces.length) {
- if (ifaces(idx).isSubtypeOf(other)) return true;
- idx += 1
- }
-
- false
- }
-
- /*
- * The `ifaces` field lists only those interfaces declared by `c`
- * From the set of all supported interfaces, this method discards those which are supertypes of others in the set.
- */
- def allLeafIfaces: Set[Tracked] = {
- if (sc == null) { ifaces.toSet }
- else { minimizeInterfaces(ifaces.toSet ++ sc.allLeafIfaces) }
- }
-
- /*
- * This type may not support in its entirety the interface given by the argument, however it may support some of its super-interfaces.
- * We visualize each such supported subset of the argument's functionality as a "branch". This method returns all such branches.
- *
- * In other words, let Ri be a branch supported by `ib`,
- * this method returns all Ri such that this <:< Ri, where each Ri is maximally deep.
- */
- def supportedBranches(ib: Tracked): Set[Tracked] = {
- assert(ib.isInterface, s"Non-interface argument: $ib")
-
- val result: Set[Tracked] =
- if (this.isSubtypeOf(ib.c)) { Set(ib) }
- else { ib.ifaces.toSet[Tracked].flatMap( bi => supportedBranches(bi) ) }
-
- checkAllInterfaces(result)
-
- result
- }
-
- override def toString = { c.toString }
-
- }
-
- /* must-single-thread */
- final def isDeprecated(sym: Symbol): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr) }
-
- /* must-single-thread */
- final def hasInternalName(sym: Symbol) = { sym.isClass || (sym.isModule && !sym.isMethod) }
-
- /* must-single-thread */
- def getSuperInterfaces(csym: Symbol): List[Symbol] = {
-
- // Additional interface parents based on annotations and other cues
- def newParentForAttr(ann: AnnotationInfo): Symbol = ann.symbol match {
- case definitions.RemoteAttr => definitions.RemoteInterfaceClass
- case _ => NoSymbol
- }
-
- /* Drop redundant interfaces (which are implemented by some other parent) from the immediate parents.
- * In other words, no two interfaces in the result are related by subtyping.
- * This method works on Symbols, a similar one (not duplicate) works on Tracked instances.
- */
- def minimizeInterfaces(lstIfaces: List[Symbol]): List[Symbol] = {
- var rest = lstIfaces
- var leaves = List.empty[Symbol]
- while (!rest.isEmpty) {
- val candidate = rest.head
- val nonLeaf = leaves exists { lsym => lsym isSubClass candidate }
- if (!nonLeaf) {
- leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym })
- }
- rest = rest.tail
- }
-
- leaves
- }
-
- val superInterfaces0: List[Symbol] = csym.mixinClasses
- val superInterfaces = existingSymbols(superInterfaces0 ++ csym.annotations.map(newParentForAttr)).distinct
-
- assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}")
- assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}")
-
- minimizeInterfaces(superInterfaces)
- }
-
- /*
- * Records the superClass and supportedInterfaces relations,
- * so that afterwards queries can be answered without resorting to typer.
- * This method does not add to `innerClassBufferASM`, use `internalName()` or `asmType()` or `toTypeKind()` for that.
- * On the other hand, this method does record the inner-class status of the argument, via `buildExemplar()`.
- *
- * must-single-thread
- */
- final def exemplar(csym0: Symbol): Tracked = {
- assert(csym0 != NoSymbol, "NoSymbol can't be tracked")
-
- val csym = {
- if (csym0.isJavaDefined && csym0.isModuleClass) csym0.linkedClassOfClass
- else if (csym0.isModule) csym0.moduleClass
- else csym0 // we track only module-classes and plain-classes
- }
-
- assert(!primitiveTypeMap.contains(csym) || isCompilingStdLib, s"primitive types not tracked here: ${csym.fullName}")
- assert(!phantomTypeMap.contains(csym), s"phantom types not tracked here: ${csym.fullName}")
-
- val opt = symExemplars.get(csym)
- if (opt != null) {
- return opt
- }
- val key = new ClassBType(csym.javaBinaryName.toTypeName)
- assert(key.isNonSpecial || isCompilingStdLib, s"Not a class to track: ${csym.fullName}")
-
- // TODO accomodate the fix for SI-5031 of https://github.com/scala/scala/commit/0527b2549bcada2fda2201daa630369b377d0877
- // TODO Weaken this assertion? buildExemplar() needs to be updated, too. In the meantime, pos/t5031_3 has been moved to test/disabled/pos.
- val whatWasInExemplars = exemplars.get(key)
- assert(whatWasInExemplars == null, "Maps `symExemplars` and `exemplars` got out of synch.")
- val tr = buildExemplar(key, csym)
- symExemplars.put(csym, tr)
- if (csym != csym0) { symExemplars.put(csym0, tr) }
- exemplars.put(tr.c, tr) // tr.c is the hash-consed, internalized, canonical representative for csym's key.
- tr
- }
-
- /*
- * must-single-thread
- */
- private def buildExemplar(key: ClassBType, csym: Symbol): Tracked = {
- val sc =
- if (csym.isImplClass) definitions.ObjectClass
- else csym.superClass
- assert(
- if (csym == definitions.ObjectClass)
- sc == NoSymbol
- else if (csym.isInterface)
- sc == definitions.ObjectClass
- else
- ((sc != NoSymbol) && !sc.isInterface) || isCompilingStdLib,
- "superClass out of order"
- )
- val ifacesArr = getSuperInterfaces(csym).map(exemplar).toArray
-
- val flags = mkFlags(
- javaFlags(csym),
- if (isDeprecated(csym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
- )
-
- val tsc = if (sc == NoSymbol) null else exemplar(sc)
-
- val innersChain = saveInnerClassesFor(csym, key)
-
- Tracked(key, flags, tsc, ifacesArr, innersChain)
- }
-
- // ---------------- utilities around interfaces represented by Tracked instances. ----------------
-
- /* Drop redundant interfaces (those which are implemented by some other).
- * In other words, no two interfaces in the result are related by subtyping.
- * This method works on Tracked elements, a similar one (not duplicate) works on Symbols.
- */
- def minimizeInterfaces(lstIfaces: Set[Tracked]): Set[Tracked] = {
- checkAllInterfaces(lstIfaces)
- var rest = lstIfaces.toList
- var leaves = List.empty[Tracked]
- while (!rest.isEmpty) {
- val candidate = rest.head
- val nonLeaf = leaves exists { leaf => leaf.isSubtypeOf(candidate.c) }
- if (!nonLeaf) {
- leaves = candidate :: (leaves filterNot { leaf => candidate.isSubtypeOf(leaf.c) })
- }
- rest = rest.tail
- }
-
- leaves.toSet
- }
-
- def allInterfaces(is: Iterable[Tracked]): Boolean = { is forall { i => i.isInterface } }
- def nonInterfaces(is: Iterable[Tracked]): Iterable[Tracked] = { is filterNot { i => i.isInterface } }
-
- def checkAllInterfaces(ifaces: Iterable[Tracked]) {
- assert(allInterfaces(ifaces), s"Non-interfaces: ${nonInterfaces(ifaces).mkString}")
- }
-
- /*
- * Subtype check `a <:< b` on BTypes that takes into account the JVM built-in numeric promotions (e.g. BYTE to INT).
- * Its operation can be visualized more easily in terms of the Java bytecode type hierarchy.
- * This method used to be called, in the ICode world, TypeKind.<:<()
- *
- * can-multi-thread
- */
- final def conforms(a: BType, b: BType): Boolean = {
- if (a.isArray) { // may be null
- /* Array subtyping is covariant here, as in Java bytecode. Also necessary for Java interop. */
- if ((b == jlCloneableReference) ||
- (b == jioSerializableReference) ||
- (b == AnyRefReference)) { true }
- else if (b.isArray) { conforms(a.asArrayBType.componentType, // TODO @lry change to pattern match, get rid of casts
- b.asArrayBType.componentType) }
- else { false }
- }
- else if (a.isBoxed) { // may be null
- if (b.isBoxed) { a == b }
- else if (b == AnyRefReference) { true }
- else if (!(b.isClass)) { false }
- else { exemplars.get(a).isSubtypeOf(b) } // e.g., java/lang/Double conforms to java/lang/Number
- }
- else if (a.isNullType) { // known to be null
- if (b.isNothingType) { false }
- else if (b.isPrimitive) { false }
- else { true }
- }
- else if (a.isNothingType) { // known to be Nothing
- true
- }
- else if (a == UNIT) {
- b == UNIT
- }
- else if (a.isClass) { // may be null
- if (a.isNothingType) { true }
- else if (b.isClass) { exemplars.get(a).isSubtypeOf(b) }
- else if (b.isArray) { a.isNullType } // documentation only, because `if(a.isNullType)` (above) covers this case already.
- else { false }
- }
- else {
-
- def msg = s"(a: $a, b: $b)"
-
- assert(a.isNonVoidPrimitiveType, s"a isn't a non-Unit value type. $msg")
- assert(b.isPrimitive, s"b isn't a value type. $msg")
-
- (a eq b) || (a match {
- case BOOL | BYTE | SHORT | CHAR => b == INT || b == LONG // TODO Actually, BOOL does NOT conform to LONG. Even with adapt().
- case _ => a == b
- })
- }
- }
-
- /* The maxValueType of (Char, Byte) and of (Char, Short) is Int, to encompass the negative values of Byte and Short. See ticket #2087.
- *
- * can-multi-thread
- */
- def maxValueType(a: BType, other: BType): BType = {
- assert(a.isPrimitive, "maxValueType() is defined only for 1st arg valuetypes (2nd arg doesn't matter).")
-
- def uncomparable: Nothing = {
- abort(s"Uncomparable BTypes: $a with $other")
- }
-
- if (a.isNothingType) return other;
- if (other.isNothingType) return a;
- if (a == other) return a;
-
- a match {
-
- case UNIT => uncomparable
- case BOOL => uncomparable
-
- case BYTE =>
- if (other == CHAR) INT
- else if (other.isNumericType) other
- else uncomparable
-
- case SHORT =>
- other match {
- case BYTE => SHORT
- case CHAR => INT
- case INT | LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
- }
-
- case CHAR =>
- other match {
- case BYTE | SHORT => INT
- case INT | LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
- }
-
- case INT =>
- other match {
- case BYTE | SHORT | CHAR => INT
- case LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
- }
-
- case LONG =>
- if (other.isIntegralType) LONG
- else if (other.isRealType) DOUBLE
- else uncomparable
-
- case FLOAT =>
- if (other == DOUBLE) DOUBLE
- else if (other.isNumericType) FLOAT
- else uncomparable
-
- case DOUBLE =>
- if (other.isNumericType) DOUBLE
- else uncomparable
-
- case _ => uncomparable
- }
- }
-
- /* Takes promotions of numeric primitives into account.
- *
- * can-multi-thread
- */
- final def maxType(a: BType, other: BType): BType = {
- if (a.isPrimitive) { maxValueType(a, other) }
- else {
- if (a.isNothingType) return other;
- if (other.isNothingType) return a;
- if (a == other) return a;
- // Approximate `lub`. The common type of two references is always AnyRef.
- // For 'real' least upper bound wrt to subclassing use method 'lub'.
- assert(a.isArray || a.isBoxed || a.isClass, s"This is not a valuetype and it's not something else, what is it? $a")
- // TODO For some reason, ICode thinks `REFERENCE(...).maxType(BOXED(whatever))` is `uncomparable`. Here, that has maxType AnyRefReference.
- // BTW, when swapping arguments, ICode says BOXED(whatever).maxType(REFERENCE(...)) == AnyRefReference, so I guess the above was an oversight in REFERENCE.maxType()
- if (other.isRef) { AnyRefReference }
- else { abort(s"Uncomparable BTypes: $a with $other") }
- }
- }
-
- /*
- * Whether the argument is a subtype of
- * scala.PartialFunction[-A, +B] extends (A => B)
- * N.B.: this method returns true for a scala.runtime.AbstractPartialFunction
- *
- * can-multi-thread
- */
- def isPartialFunctionType(t: BType): Boolean = {
- (t.isClass) && exemplars.get(t).isSubtypeOf(PartialFunctionReference)
- }
-
- /*
- * Whether the argument is a subtype of scala.FunctionX where 0 <= X <= definitions.MaxFunctionArity
- *
- * can-multi-thread
- */
- def isFunctionType(t: BType): Boolean = {
- if (!t.isClass) return false
- var idx = 0
- val et: Tracked = exemplars.get(t)
- while (idx <= definitions.MaxFunctionArity) {
- if (et.isSubtypeOf(FunctionReference(idx).c)) {
- return true
- }
- idx += 1
- }
- false
- }
-
- /*
- * must-single-thread
- */
- def isTopLevelModule(sym: Symbol): Boolean = {
- exitingPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass }
- }
-
- /*
- * must-single-thread
- */
- def isStaticModule(sym: Symbol): Boolean = {
- sym.isModuleClass && !sym.isImplClass && !sym.isLifted
- }
-
- // ---------------------------------------------------------------------
- // ---------------- InnerClasses attribute (JVMS 4.7.6) ----------------
- // ---------------------------------------------------------------------
-
- val INNER_CLASSES_FLAGS =
- (asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED |
- asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_INTERFACE | asm.Opcodes.ACC_ABSTRACT | asm.Opcodes.ACC_FINAL)
-
- /*
- * @param name the internal name of an inner class.
- * @param outerName the internal name of the class to which the inner class belongs.
- * May be `null` for non-member inner classes (ie for a Java local class or a Java anonymous class).
- * @param innerName the (simple) name of the inner class inside its enclosing class. It's `null` for anonymous inner classes.
- * @param access the access flags of the inner class as originally declared in the enclosing class.
- */
- case class InnerClassEntry(name: String, outerName: String, innerName: String, access: Int) {
- assert(name != null, "Null isn't good as class name in an InnerClassEntry.")
- }
-
- /* For given symbol return a symbol corresponding to a class that should be declared as inner class.
- *
- * For example:
- * class A {
- * class B
- * object C
- * }
- *
- * then method will return:
- * NoSymbol for A,
- * the same symbol for A.B (corresponding to A$B class), and
- * A$C$ symbol for A.C.
- *
- * must-single-thread
- */
- def innerClassSymbolFor(s: Symbol): Symbol =
- if (s.isClass) s else if (s.isModule) s.moduleClass else NoSymbol
-
- /*
- * Computes the chain of inner-class (over the is-member-of relation) for the given argument.
- * The resulting chain will be cached in `exemplars`.
- *
- * The chain thus cached is valid during this compiler run, see in contrast
- * `innerClassBufferASM` for a cache that is valid only for the class being emitted.
- *
- * The argument can be any symbol, but given that this method is invoked only from `buildExemplar()`,
- * in practice it has been vetted to be a class-symbol.
- *
- * Returns:
- *
- * - a non-empty array of entries for an inner-class argument.
- * The array's first element is the outermost top-level class,
- * the array's last element corresponds to csym.
- *
- * - null otherwise.
- *
- * This method does not add to `innerClassBufferASM`, use instead `exemplar()` for that.
- *
- * must-single-thread
- */
- final def saveInnerClassesFor(csym: Symbol, csymTK: BType): Array[InnerClassEntry] = {
-
- val ics = innerClassSymbolFor(csym)
- if (ics == NoSymbol) {
- return null
- }
- assert(ics == csym, s"Disagreement between innerClassSymbolFor() and exemplar()'s tracked symbol for the same input: ${csym.fullName}")
-
- var chain: List[Symbol] = Nil
- var x = ics
- while (x ne NoSymbol) {
- assert(x.isClass, s"not a class symbol: ${x.fullName}")
- val isInner = !x.rawowner.isPackageClass
- if (isInner) {
- chain ::= x
- x = innerClassSymbolFor(x.rawowner)
- } else {
- x = NoSymbol
- }
- }
-
- if (chain.isEmpty) null
- else chain.map(toInnerClassEntry).toArray
- }
-
- /*
- * must-single-thread
- */
- private def toInnerClassEntry(innerSym: Symbol): InnerClassEntry = {
-
- /* The outer name for this inner class. Note that it returns null
- * when the inner class should not get an index in the constant pool.
- * That means non-member classes (anonymous). See Section 4.7.5 in the JVMS.
- */
- def outerName(innerSym: Symbol): Name = {
- if (innerSym.originalEnclosingMethod != NoSymbol)
- null
- else {
- val outerName = innerSym.rawowner.javaBinaryName
- if (isTopLevelModule(innerSym.rawowner)) nme.stripModuleSuffix(outerName)
- else outerName
- }
- }
-
- def innerName(innerSym: Symbol): String = {
- if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction)
- null
- else
- innerSym.rawname + innerSym.moduleSuffix
- }
-
- val flagsWithFinal: Int = mkFlags(
- if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0,
- javaFlags(innerSym),
- if (isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag
- ) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED)
- val flags = if (innerSym.isModuleClass) flagsWithFinal & ~asm.Opcodes.ACC_FINAL else flagsWithFinal // For SI-5676, object overriding.
-
- val jname = innerSym.javaBinaryName.toString // never null
- val oname = { // null when method-enclosed
- val on = outerName(innerSym)
- if (on == null) null else on.toString
- }
- val iname = { // null for anonymous inner class
- val in = innerName(innerSym)
- if (in == null) null else in.toString
- }
-
- InnerClassEntry(jname, oname, iname, flags)
- }
-
- // --------------------------------------------
- // ---------------- Java flags ----------------
- // --------------------------------------------
-
- /*
- * can-multi-thread
- */
- final def hasPublicBitSet(flags: Int) = ((flags & asm.Opcodes.ACC_PUBLIC) != 0)
-
- /*
- * must-single-thread
- */
- final def isRemote(s: Symbol) = (s hasAnnotation definitions.RemoteAttr)
-
- /*
- * Return the Java modifiers for the given symbol.
- * Java modifiers for classes:
- * - public, abstract, final, strictfp (not used)
- * for interfaces:
- * - the same as for classes, without 'final'
- * for fields:
- * - public, private (*)
- * - static, final
- * for methods:
- * - the same as for fields, plus:
- * - abstract, synchronized (not used), strictfp (not used), native (not used)
- *
- * (*) protected cannot be used, since inner classes 'see' protected members,
- * and they would fail verification after lifted.
- *
- * must-single-thread
- */
- def javaFlags(sym: Symbol): Int = {
- // constructors of module classes should be private
- // PP: why are they only being marked private at this stage and not earlier?
- val privateFlag =
- sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner))
-
- // Final: the only fields which can receive ACC_FINAL are eager vals.
- // Neither vars nor lazy vals can, because:
- //
- // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
- // "Another problem is that the specification allows aggressive
- // optimization of final fields. Within a thread, it is permissible to
- // reorder reads of a final field with those modifications of a final
- // field that do not take place in the constructor."
- //
- // A var or lazy val which is marked final still has meaning to the
- // scala compiler. The word final is heavily overloaded unfortunately;
- // for us it means "not overridable". At present you can't override
- // vars regardless; this may change.
- //
- // The logic does not check .isFinal (which checks flags for the FINAL flag,
- // and includes symbols marked lateFINAL) instead inspecting rawflags so
- // we can exclude lateFINAL. Such symbols are eligible for inlining, but to
- // avoid breaking proxy software which depends on subclassing, we do not
- // emit ACC_FINAL.
- // Nested objects won't receive ACC_FINAL in order to allow for their overriding.
-
- val finalFlag = (
- (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModule(sym))
- && !sym.enclClass.isInterface
- && !sym.isClassConstructor
- && !sym.isMutable // lazy vals and vars both
- )
-
- // Primitives are "abstract final" to prohibit instantiation
- // without having to provide any implementations, but that is an
- // illegal combination of modifiers at the bytecode level so
- // suppress final if abstract if present.
- import asm.Opcodes._
- mkFlags(
- if (privateFlag) ACC_PRIVATE else ACC_PUBLIC,
- if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0,
- if (sym.isInterface) ACC_INTERFACE else 0,
- if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0,
- if (sym.isStaticMember) ACC_STATIC else 0,
- if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
- if (sym.isArtifact) ACC_SYNTHETIC else 0,
- if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
- if (sym.hasEnumFlag) ACC_ENUM else 0,
- if (sym.isVarargsMethod) ACC_VARARGS else 0,
- if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0
- )
- }
-
- /*
- * must-single-thread
- */
- def javaFieldFlags(sym: Symbol) = {
- javaFlags(sym) | mkFlags(
- if (sym hasAnnotation definitions.TransientAttr) asm.Opcodes.ACC_TRANSIENT else 0,
- if (sym hasAnnotation definitions.VolatileAttr) asm.Opcodes.ACC_VOLATILE else 0,
- if (sym.isMutable) 0 else asm.Opcodes.ACC_FINAL
- )
- }
-
-} // end of class BCodeTypes
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index 5b0fa6f7a8..7bf61b4f51 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -1,37 +1,57 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
package scala.tools.nsc
package backend.jvm
-import scala.collection.immutable
-import scala.annotation.switch
import scala.tools.asm
import asm.Opcodes
-import scala.collection.mutable.ListBuffer
/**
- * BTypes is a backend component that defines the class BType, a number of basic instances and
- * some utilities.
- *
- * A BType is essentially an slice of the array `chrs` denoting the name of the type, and a field
- * denoting the kind (object, array, method, or one of the primitive types).
+ * The BTypes component defines The BType class hierarchy. BTypes encapsulates all type information
+ * that is required after building the ASM nodes. This includes optimizations, geneartion of
+ * InnerClass attributes and generation of stack map frames.
*
- * BTypes depends on Global just because it re-uses hash-consing of Name. It would be cleaner to
- * create an interface for BTypeName and extend it in scala.reflect.internal.Names#Name, that
- * would simplify testing BTypes (no Global needed).
+ * This representation is immutable and independent of the compiler data structures, hence it can
+ * be queried by concurrent threads.
*/
-abstract class BTypes[G <: Global](val __global_dont_use: G) {
- def chrs: Array[Char]
+abstract class BTypes {
+ /**
+ * A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
+ * construction.
+ *
+ * This map is used when computing stack map frames. The asm.ClassWriter invokes the method
+ * `getCommonSuperClass`. In this method we need to obtain the ClassBType for a given internal
+ * name. The method assumes that every class type that appears in the bytecode exists in the map.
+ *
+ * Concurrent because stack map frames are computed when in the class writer, which might run
+ * on multiple classes concurrently.
+ */
+ protected val classBTypeFromInternalNameMap: collection.concurrent.Map[String, ClassBType]
/**
- * Interface for names stored in `chrs`
+ * The string represented by the `offset` / `length` values of a ClassBType, see comment of that
+ * class.
*/
- type BTypeName <: __global_dont_use.Name
+ protected def internalNameString(offset: Int, lenght: Int): String
/**
- * Create a new name in `chrs`. Names are assumed to be hash-consed. Equality on BType will use
- * reference equality to compare the names.
+ * Obtain a previously constructed ClassBType for a given internal name.
*/
- def createNewName(s: String): BTypeName
+ def classBTypeFromInternalName(internalName: String) = classBTypeFromInternalNameMap(internalName)
+
+ // Some core BTypes are required here, in class BType, where no Global instance is available.
+ // The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
+ // implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
+ val coreBTypes: CoreBTypesProxyGlobalIndependent[this.type]
+ import coreBTypes._
+ /**
+ * A BType is either a primitve type, a ClassBType, an ArrayBType of one of these, or a MethodType
+ * referring to BTypes.
+ */
/*sealed*/ trait BType { // Not sealed for now due to SI-8546
final override def toString: String = this match {
case UNIT => "V"
@@ -43,9 +63,9 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
case FLOAT => "F"
case LONG => "J"
case DOUBLE => "D"
- case c @ ClassBType(_, _) => "L" + c.internalName + ";"
- case ArrayBType(component) => "[" + component
- case MethodBType(args, res) => "(" + args.mkString + ")" + res
+ case ClassBType(internalName) => "L" + internalName + ";"
+ case ArrayBType(component) => "[" + component
+ case MethodBType(args, res) => "(" + args.mkString + ")" + res
}
/**
@@ -73,18 +93,11 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
final def isMethod: Boolean = this.isInstanceOf[MethodBType]
final def isNonVoidPrimitiveType = isPrimitive && this != UNIT
- // TODO @lry should also include !isMethod in isNonSpecial? in this case it would be equivalent to isClass, so we could get rid of it.
- final def isNonSpecial = !isPrimitive && !isArray && !isPhantomType
- final def isNullType = this == RT_NULL || this == CT_NULL
- final def isNothingType = this == RT_NOTHING || this == CT_NOTHING
- final def isPhantomType = isNullType || isNothingType
-
- final def isBoxed = this match {
- case BOXED_UNIT | BOXED_BOOLEAN | BOXED_CHAR |
- BOXED_BYTE | BOXED_SHORT | BOXED_INT |
- BOXED_FLOAT | BOXED_LONG | BOXED_DOUBLE => true
- case _ => false
- }
+
+ final def isNullType = this == RT_NULL
+ final def isNothingType = this == RT_NOTHING
+
+ final def isBoxed = this.isClass && boxedClasses(this.asClassBType)
final def isIntSizedType = this == BOOL || this == CHAR || this == BYTE ||
this == SHORT || this == INT
@@ -94,6 +107,72 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
final def isNumericType = isIntegralType || isRealType
final def isWideType = size == 2
+ /*
+ * Subtype check `this <:< other` on BTypes that takes into account the JVM built-in numeric
+ * promotions (e.g. BYTE to INT). Its operation can be visualized more easily in terms of the
+ * Java bytecode type hierarchy.
+ */
+ final def conformsTo(other: BType): Boolean = {
+ assert(isRef || isPrimitive, s"conformsTo cannot handle $this")
+ assert(other.isRef || other.isPrimitive, s"conformsTo cannot handle $other")
+
+ this match {
+ case ArrayBType(component) =>
+ if (other == ObjectReference || other == jlCloneableReference || other == jioSerializableReference) true
+ else other match {
+ case ArrayBType(otherComponoent) => component.conformsTo(otherComponoent)
+ case _ => false
+ }
+
+ case classType: ClassBType =>
+ if (isBoxed) {
+ if (other.isBoxed) this == other
+ else if (other == ObjectReference) true
+ else other match {
+ case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType) // e.g., java/lang/Double conforms to java/lang/Number
+ case _ => false
+ }
+ } else if (isNullType) {
+ if (other.isNothingType) false
+ else if (other.isPrimitive) false
+ else true // Null conforms to all classes (except Nothing) and arrays.
+ } else if (isNothingType) {
+ true
+ } else other match {
+ case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType)
+ // case ArrayBType(_) => this.isNullType // documentation only, because `if (isNullType)` above covers this case
+ case _ =>
+ // isNothingType || // documentation only, because `if (isNothingType)` above covers this case
+ false
+ }
+
+ case UNIT =>
+ other == UNIT
+ case BOOL | BYTE | SHORT | CHAR =>
+ this == other || other == INT || other == LONG // TODO Actually, BOOL does NOT conform to LONG. Even with adapt().
+ case _ =>
+ assert(isPrimitive && other.isPrimitive, s"Expected primitive types $this - $other")
+ this == other
+ }
+ }
+
+ /**
+ * Compute the upper bound of two types.
+ * Takes promotions of numeric primitives into account.
+ */
+ final def maxType(other: BType): BType = this match {
+ case pt: PrimitiveBType => pt.maxValueType(other)
+
+ case _: ArrayBType | _: ClassBType =>
+ if (isNothingType) return other
+ if (other.isNothingType) return this
+ if (this == other) return this
+
+ assert(other.isRef, s"Cannot compute maxType: $this, $other")
+ // Approximate `lub`. The common type of two references is always ObjectReference.
+ ObjectReference
+ }
+
/**
* See documentation of [[typedOpcode]].
* The numbers are taken from asm.Type.VOID_TYPE ff., the values are those shifted by << 8.
@@ -160,50 +239,82 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
case FLOAT => asm.Type.FLOAT_TYPE
case LONG => asm.Type.LONG_TYPE
case DOUBLE => asm.Type.DOUBLE_TYPE
- case c @ ClassBType(_, _) => asm.Type.getObjectType(c.internalName) // (*)
- case a @ ArrayBType(_) => asm.Type.getObjectType(a.descriptor)
- case m @ MethodBType(_, _) => asm.Type.getMethodType(m.descriptor)
+ case ClassBType(internalName) => asm.Type.getObjectType(internalName) // see (*) above
+ case a: ArrayBType => asm.Type.getObjectType(a.descriptor)
+ case m: MethodBType => asm.Type.getMethodType(m.descriptor)
}
- def asRefBType : RefBType = this.asInstanceOf[RefBType]
- def asArrayBType: ArrayBType = this.asInstanceOf[ArrayBType]
- def asClassBType: ClassBType = this.asInstanceOf[ClassBType]
+ def asRefBType : RefBType = this.asInstanceOf[RefBType]
+ def asArrayBType : ArrayBType = this.asInstanceOf[ArrayBType]
+ def asClassBType : ClassBType = this.asInstanceOf[ClassBType]
+ def asPrimitiveBType : PrimitiveBType = this.asInstanceOf[PrimitiveBType]
}
- object BType {
+ sealed trait PrimitiveBType extends BType {
+
/**
- * @param chars The character array containing the descriptor
- * @param start The position where the descriptor starts
- * @return The BType and the index of the first character after the consumed descriptor
+ * The upper bound of two primitive types. The `other` type has to be either a primitive
+ * type or Nothing.
+ *
+ * The maxValueType of (Char, Byte) and of (Char, Short) is Int, to encompass the negative
+ * values of Byte and Short. See ticket #2087.
*/
- private[BTypes] def fromNonMethodDescriptor(chars: Array[Char], start: Int): (BType, Int) = {
- chars(start) match {
- case 'L' =>
- var i = start
- while (chars(i) != ';') { i += 1 }
- // Example: chars = "IILpkg/Cls;I"
- // ^ ^
- // start=2 i=10
- // `start + 1` to exclude the 'L', `i - start - 1` excludes the ';'
- (new ClassBType(new String(chars, start + 1, i - start - 1)), i + 1)
- case '[' =>
- val (res, next) = fromNonMethodDescriptor(chars, start + 1)
- (ArrayBType(res), next)
- case 'V' => (UNIT, start + 1)
- case 'Z' => (BOOL, start + 1)
- case 'C' => (CHAR, start + 1)
- case 'B' => (BYTE, start + 1)
- case 'S' => (SHORT, start + 1)
- case 'I' => (INT, start + 1)
- case 'F' => (FLOAT, start + 1)
- case 'J' => (LONG, start + 1)
- case 'D' => (DOUBLE, start + 1)
+ final def maxValueType(other: BType): BType = {
+
+ def uncomparable: Nothing = throw new AssertionError(s"Cannot compute maxValueType: $this, $other")
+
+ if (!other.isPrimitive && !other.isNothingType) uncomparable
+
+ if (other.isNothingType) return this
+ if (this == other) return this
+
+ this match {
+ case BYTE =>
+ if (other == CHAR) INT
+ else if (other.isNumericType) other
+ else uncomparable
+
+ case SHORT =>
+ other match {
+ case BYTE => SHORT
+ case CHAR => INT
+ case INT | LONG | FLOAT | DOUBLE => other
+ case _ => uncomparable
+ }
+
+ case CHAR =>
+ other match {
+ case BYTE | SHORT => INT
+ case INT | LONG | FLOAT | DOUBLE => other
+ case _ => uncomparable
+ }
+
+ case INT =>
+ other match {
+ case BYTE | SHORT | CHAR => INT
+ case LONG | FLOAT | DOUBLE => other
+ case _ => uncomparable
+ }
+
+ case LONG =>
+ if (other.isIntegralType) LONG
+ else if (other.isRealType) DOUBLE
+ else uncomparable
+
+ case FLOAT =>
+ if (other == DOUBLE) DOUBLE
+ else if (other.isNumericType) FLOAT
+ else uncomparable
+
+ case DOUBLE =>
+ if (other.isNumericType) DOUBLE
+ else uncomparable
+
+ case UNIT | BOOL => uncomparable
}
}
}
- sealed trait PrimitiveBType extends BType
-
case object UNIT extends PrimitiveBType
case object BOOL extends PrimitiveBType
case object CHAR extends PrimitiveBType
@@ -227,57 +338,408 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* This can be verified for example using javap or ASMifier.
*/
def classOrArrayType: String = this match {
- case c: ClassBType => c.internalName
- case a: ArrayBType => a.descriptor
+ case ClassBType(internalName) => internalName
+ case a: ArrayBType => a.descriptor
}
}
/**
- * Class or Interface type.
+ * InnerClass and EnclosingMethod attributes (EnclosingMethod is displayed as OUTERCLASS in asm).
+ *
+ * In this summary, "class" means "class or interface".
+ *
+ * JLS: http://docs.oracle.com/javase/specs/jls/se8/html/index.html
+ * JVMS: http://docs.oracle.com/javase/specs/jvms/se8/html/index.html
+ *
+ * Terminology
+ * -----------
+ *
+ * - Nested class (JLS 8): class whose declaration occurs within the body of another class
+ *
+ * - Top-level class (JLS 8): non-nested class
+ *
+ * - Inner class (JLS 8.1.3): nested class that is not (explicitly or implicitly) static
+ *
+ * - Member class (JLS 8.5): class directly enclosed in the body of a class (and not, for
+ * example, defined in a method). Member classes cannot be anonymous. May be static.
+ *
+ * - Local class (JLS 14.3): nested, non-anonymous class that is not a member of a class
+ * - cannot be static (therefore they are "inner" classes)
+ * - can be defined in a method, a constructor or in an initializer block
+ *
+ * - Initializer block (JLS 8.6 / 8.7): block of statements in a java class
+ * - static initializer: executed before constructor body
+ * - instance initializer: exectued when class is initialized (instance creation, static
+ * field access, ...)
+ *
+ * - A static nested class can be defined as
+ * - a static member class (explicitly static), or
+ * - a member class of an interface (implicitly static)
+ * - local classes are never static, even if they are defined in a static method.
+ *
+ * Note: it is NOT the case that all inner classes (non-static) have an outer pointer. Example:
+ * class C { static void foo { class D {} } }
+ * The class D is an inner class (non-static), but javac does not add an outer pointer to it.
+ *
+ * InnerClass
+ * ----------
+ *
+ * The JVMS 4.7.6 requires an entry for every class mentioned in a CONSTANT_Class_info in the
+ * constant pool (CP) that is not a member of a package (JLS 7.1).
+ *
+ * The JLS 13.1, points 9. / 10. requires: a class must reference (in the CP)
+ * - its immediately enclosing class
+ * - all of its member classes
+ * - all local and anonymous classes that are referenced (or declared) elsewhere (method,
+ * constructor, initializer block, field initializer)
+ *
+ * In a comment, the 4.7.6 spec says: this implies an entry in the InnerClass attribute for
+ * - All enclosing classes (except the outermost, which is top-level)
+ * - My comment: not sure how this is implied, below (*) a Java counter-example.
+ * In any case, the Java compiler seems to add all enclosing classes, even if they are not
+ * otherwise mentioned in the CP. So we should do the same.
+ * - All nested classes (including anonymous and local, but not transitively)
+ *
+ * Fields in the InnerClass entries:
+ * - inner class: the (nested) class C we are talking about
+ * - outer class: the class of which C is a member. Has to be null for non-members, i.e. for
+ * local and anonymous classes. NOTE: this co-incides with the presence of an
+ * EnclosingMethod attribute (see below)
+ * - inner name: A string with the simple name of the inner class. Null for anonymous classes.
+ * - flags: access property flags, details in JVMS, table in 4.7.6. Static flag: see
+ * discussion below.
+ *
+ *
+ * Note 1: when a nested class is present in the InnerClass attribute, all of its enclosing
+ * classes have to be present as well (by the rules above). Example:
+ *
+ * class Outer { class I1 { class I2 { } } }
+ * class User { Outer.I1.I2 foo() { } }
+ *
+ * The return type "Outer.I1.I2" puts "Outer$I1$I2" in the CP, therefore the class is added to the
+ * InnerClass attribute. For this entry, the "outer class" field will be "Outer$I1". This in turn
+ * adds "Outer$I1" to the CP, which requires adding that class to the InnerClass attribute.
+ * (For local / anonymous classes this would not be the case, since the "outer class" attribute
+ * would be empty. However, no class (other than the enclosing class) can refer to them, as they
+ * have no name.)
+ *
+ * In the current implementation of the Scala compiler, when adding a class to the InnerClass
+ * attribute, all of its enclosing classes will be added as well. Javac seems to do the same,
+ * see (*).
+ *
+ *
+ * Note 2: If a class name is mentioned only in a CONSTANT_Utf8_info, but not in a
+ * CONSTANT_Class_info, the JVMS does not require an entry in the InnerClass attribute. However,
+ * the Java compiler seems to add such classes anyway. For example, when using an annotation, the
+ * annotation class is stored as a CONSTANT_Utf8_info in the CP:
+ *
+ * @O.Ann void foo() { }
+ *
+ * adds "const #13 = Asciz LO$Ann;;" in the constant pool. The "RuntimeInvisibleAnnotations"
+ * attribute refers to that constant pool entry. Even though there is no other reference to
+ * `O.Ann`, the java compiler adds an entry for that class to the InnerClass attribute (which
+ * entails adding a CONSTANT_Class_info for the class).
+ *
+ *
+ *
+ * EnclosingMethod
+ * ---------------
+ *
+ * JVMS 4.7.7: the attribute must be present "if and only if it represents a local class
+ * or an anonymous class" (i.e. not for member classes).
+ *
+ * The attribute is mis-named, it should be called "EnclosingClass". It has to be defined for all
+ * local and anonymous classes, no matter if there is an enclosing method or not. Accordingly, the
+ * "class" field (see below) must be always defined, while the "method" field may be null.
+ *
+ * NOTE: When a EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
+ * field in the InnerClass table must be null.
+ *
+ * Fields:
+ * - class: the enclosing class
+ * - method: the enclosing method (or constructor). Null if the class is not enclosed by a
+ * method, i.e. for
+ * - local or anonymous classes defined in (static or non-static) initializer blocks
+ * - anonymous classes defined in initializer blocks or field initializers
+ *
+ * Note: the field is required for anonymous classes defined within local variable
+ * initializers (within a method), Java example below (**).
+ *
+ * For local and anonymous classes in initializer blocks or field initializers, and
+ * class-level anonymous classes, the scala compiler sets the "method" field to null.
+ *
+ *
+ * (*)
+ * public class Test {
+ * void foo() {
+ * class Foo1 {
+ * // constructor statement block
+ * {
+ * class Foo2 {
+ * class Foo3 { }
+ * }
+ * }
+ * }
+ * }
+ * }
+ *
+ * The class file Test$1Foo1$1Foo2$Foo3 has no reference to the class Test$1Foo1, however it
+ * still contains an InnerClass attribute for Test$1Foo1.
+ * Maybe this is just because the Java compiler follows the JVMS comment ("InnerClasses
+ * information for each enclosing class").
+ *
+ *
+ * (**)
+ * void foo() {
+ * // anonymous class defined in local variable initializer expression.
+ * Runnable x = true ? (new Runnable() {
+ * public void run() { return; }
+ * }) : null;
+ * }
+ *
+ * The EnclosingMethod attribute of the anonymous class mentions "foo" in the "method" field.
+ *
+ *
+ * Java Compatibility
+ * ------------------
+ *
+ * In the InnerClass entry for classes in top-level modules, the "outer class" is emitted as the
+ * mirror class (or the existing companion class), i.e. C1 is nested in T (not T$).
+ * For classes nested in a nested object, the "outer class" is the module class: C2 is nested in T$N$
+ * object T {
+ * class C1
+ * object N { class C2 }
+ * }
+ *
+ * Reason: java compat. It's a "best effort" "solution". If you want to use "C1" from Java, you
+ * can write "T.C1", and the Java compiler will translate that to the classfile T$C1.
+ *
+ * If we would emit the "outer class" of C1 as "T$", then in Java you'd need to write "T$.C1"
+ * because the java compiler looks at the InnerClass attribute to find if an inner class exists.
+ * However, the Java compiler would then translate the '.' to '$' and you'd get the class name
+ * "T$$C1". This class file obviously does not exist.
+ *
+ * Directly using the encoded class name "T$C1" in Java does not work: since the classfile
+ * describes a nested class, the Java compiler hides it from the classpath and will report
+ * "cannot find symbol T$C1". This means that the class T.N.C2 cannot be referenced from a
+ * Java source file in any way.
+ *
+ *
+ * STATIC flag
+ * -----------
+ *
+ * Java: static member classes have the static flag in the InnerClass attribute, for example B in
+ * class A { static class B { } }
+ *
+ * The spec is not very clear about when the static flag should be emitted. It says: "Marked or
+ * implicitly static in source."
+ *
+ * The presence of the static flag does NOT coincide with the absence of an "outer" field in the
+ * class. The java compiler never puts the static flag for local classes, even if they don't have
+ * an outer pointer:
+ *
+ * class A {
+ * void f() { class B {} }
+ * static void g() { calss C {} }
+ * }
*
- * Classes are represented using their name as a slice of the `chrs` array. This representation is
- * efficient because the JVM class name is initially created using `classSymbol.javaBinaryName`.
- * This already adds the necessary string to the `chrs` array, so it makes sense to reuse the same
- * name table in the backend.
+ * B has an outer pointer, C doesn't. Both B and C are NOT marked static in the InnerClass table.
*
- * Not a case class because that would expose the (Int, Int) constructor (didn't find a way to
- * make it private, also the factory in the companion).
+ * It seems sane to follow the same principle in the Scala compiler. So:
+ *
+ * package p
+ * object O1 {
+ * class C1 // static inner class
+ * object O2 { // static inner module
+ * def f = {
+ * class C2 { // non-static inner class, even though there's no outer pointer
+ * class C3 // non-static, has an outer pointer
+ * }
+ * }
+ * }
+ * }
+ *
+ * Mirror Classes
+ * --------------
+ *
+ * TODO: innerclass attributes on mirror class, bean info class
*/
- class ClassBType private(val offset: Int, val length: Int) extends RefBType {
- /**
- * Construct a ClassBType for a given (intenred) class name.
- *
- * @param n The class name as a slice of the `chrs` array, without the surrounding 'L' and ';'.
- * Note that `classSymbol.javaBinaryName` returns exactly such a name.
- */
- def this(n: BTypeName) = this(n.start, n.length)
+ /**
+ * A ClassBType represents a class or interface type. The necessary information to build a
+ * ClassBType is extracted from compiler symbols and types, see BTypesFromSymbols.
+ *
+ * The `offset` and `length` fields are used to represent the internal name of the class. They
+ * are indices into some character array. The internal name can be obtained through the method
+ * `internalNameString`, which is abstract in this component. Name creation is assumed to be
+ * hash-consed, so if two ClassBTypes have the same internal name, they NEED to have the same
+ * `offset` and `length`.
+ *
+ * The actual implementation in subclass BTypesFromSymbols uses the global `chrs` array from the
+ * name table. This representation is efficient because the JVM class name is obtained through
+ * `classSymbol.javaBinaryName`. This already adds the necessary string to the `chrs` array,
+ * so it makes sense to reuse the same name table in the backend.
+ *
+ * ClassBType is not a case class because we want a custom equals method, and because the
+ * extractor extracts the internalName, which is what you typically need.
+ */
+ final class ClassBType(val offset: Int, val length: Int) extends RefBType {
/**
- * Construct a ClassBType for a given java class name.
+ * Write-once variable allows initializing a cyclic graph of infos. This is required for
+ * nested classes. Example: for the definition `class A { class B }` we have
*
- * @param s A class name of the form "java/lang/String", without the surrounding 'L' and ';'.
+ * B.info.nestedInfo.outerClass == A
+ * A.info.memberClasses contains B
*/
- def this(s: String) = this({
- assert(!(s.head == 'L' && s.last == ';'), s"Descriptor instead of internal name: $s")
- createNewName(s)
- })
+ private var _info: ClassInfo = null
+
+ def info: ClassInfo = {
+ assert(_info != null, s"ClassBType.info not yet assigned: $this")
+ _info
+ }
+
+ def info_=(i: ClassInfo): Unit = {
+ assert(_info == null, s"Cannot set ClassBType.info multiple times: $this")
+ _info = i
+ checkInfoConsistency()
+ }
+
+ classBTypeFromInternalNameMap(internalName) = this
+
+ private def checkInfoConsistency(): Unit = {
+ // we assert some properties. however, some of the linked ClassBType (members, superClass,
+ // interfaces) may not yet have an `_info` (initialization of cyclic structures). so we do a
+ // best-effort verification.
+ def ifInit(c: ClassBType)(p: ClassBType => Boolean): Boolean = c._info == null || p(c)
+
+ def isJLO(t: ClassBType) = t.internalName == "java/lang/Object"
+
+ assert(!ClassBType.isInternalPhantomType(internalName), s"Cannot create ClassBType for phantom type $this")
+
+ assert(
+ if (info.superClass.isEmpty) { isJLO(this) || (isCompilingPrimitive && ClassBType.hasNoSuper(internalName)) }
+ else if (isInterface) isJLO(info.superClass.get)
+ else !isJLO(this) && ifInit(info.superClass.get)(!_.isInterface),
+ s"Invalid superClass in $this: ${info.superClass}"
+ )
+ assert(
+ info.interfaces.forall(c => ifInit(c)(_.isInterface)),
+ s"Invalid interfaces in $this: ${info.interfaces}"
+ )
+
+ assert(info.memberClasses.forall(c => ifInit(c)(_.isNestedClass)), info.memberClasses)
+ }
/**
* The internal name of a class is the string returned by java.lang.Class.getName, with all '.'
* replaced by '/'. For example "java/lang/String".
*/
- def internalName: String = new String(chrs, offset, length)
+ def internalName: String = internalNameString(offset, length)
/**
* @return The class name without the package prefix
*/
def simpleName: String = internalName.split("/").last
+ def isInterface = (info.flags & asm.Opcodes.ACC_INTERFACE) != 0
+
+ def superClassesTransitive: List[ClassBType] = info.superClass match {
+ case None => Nil
+ case Some(sc) => sc :: sc.superClassesTransitive
+ }
+
+ def isNestedClass = info.nestedInfo.isDefined
+
+ def enclosingNestedClassesChain: List[ClassBType] =
+ if (isNestedClass) this :: info.nestedInfo.get.enclosingClass.enclosingNestedClassesChain
+ else Nil
+
+ def innerClassAttributeEntry: Option[InnerClassEntry] = info.nestedInfo map {
+ case NestedInfo(_, outerName, innerName, isStaticNestedClass) =>
+ InnerClassEntry(
+ internalName,
+ outerName.orNull,
+ innerName.orNull,
+ GenBCode.mkFlags(
+ info.flags,
+ if (isStaticNestedClass) asm.Opcodes.ACC_STATIC else 0
+ ) & ClassBType.INNER_CLASSES_FLAGS
+ )
+ }
+
+ def isSubtypeOf(other: ClassBType): Boolean = {
+ if (this == other) return true
+
+ if (isInterface) {
+ if (other == ObjectReference) return true // interfaces conform to Object
+ if (!other.isInterface) return false // this is an interface, the other is some class other than object. interfaces cannot extend classes, so the result is false.
+ // else: this and other are both interfaces. continue to (*)
+ } else {
+ val sc = info.superClass
+ if (sc.isDefined && sc.get.isSubtypeOf(other)) return true // the superclass of this class conforms to other
+ if (!other.isInterface) return false // this and other are both classes, and the superclass of this does not conform
+ // else: this is a class, the other is an interface. continue to (*)
+ }
+
+ // (*) check if some interface of this class conforms to other.
+ info.interfaces.exists(_.isSubtypeOf(other))
+ }
+
+ /**
+ * Finding the least upper bound in agreement with the bytecode verifier
+ * Background:
+ * http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
+ * http://comments.gmane.org/gmane.comp.java.vm.languages/2293
+ * https://issues.scala-lang.org/browse/SI-3872
+ */
+ def jvmWiseLUB(other: ClassBType): ClassBType = {
+ def isNotNullOrNothing(c: ClassBType) = !c.isNullType && !c.isNothingType
+ assert(isNotNullOrNothing(this) && isNotNullOrNothing(other), s"jvmWiseLub for null or nothing: $this - $other")
+
+ val res: ClassBType = (this.isInterface, other.isInterface) match {
+ case (true, true) =>
+ // exercised by test/files/run/t4761.scala
+ if (other.isSubtypeOf(this)) this
+ else if (this.isSubtypeOf(other)) other
+ else ObjectReference
+
+ case (true, false) =>
+ if (other.isSubtypeOf(this)) this else ObjectReference
+
+ case (false, true) =>
+ if (this.isSubtypeOf(other)) other else ObjectReference
+
+ case _ =>
+ // TODO @lry I don't really understand the reasoning here.
+ // Both this and other are classes. The code takes (transitively) all superclasses and
+ // finds the first common one.
+ firstCommonSuffix(this :: this.superClassesTransitive, other :: other.superClassesTransitive)
+ }
+
+ assert(isNotNullOrNothing(res), s"jvmWiseLub computed: $res")
+ res
+ }
+
+ private def firstCommonSuffix(as: List[ClassBType], bs: List[ClassBType]): ClassBType = {
+ var chainA = as
+ var chainB = bs
+ var fcs: ClassBType = null
+ do {
+ if (chainB contains chainA.head) fcs = chainA.head
+ else if (chainA contains chainB.head) fcs = chainB.head
+ else {
+ chainA = chainA.tail
+ chainB = chainB.tail
+ }
+ } while (fcs == null)
+ fcs
+ }
+
/**
- * Custom equals / hashCode are needed because this is not a case class.
+ * Custom equals / hashCode: we only compare the name (offset / length)
*/
override def equals(o: Any): Boolean = (this eq o.asInstanceOf[Object]) || (o match {
- case ClassBType(`offset`, `length`) => true
+ case c: ClassBType => c.offset == this.offset && c.length == this.length
case _ => false
})
@@ -291,14 +753,92 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
}
object ClassBType {
- def apply(n: BTypeName): ClassBType = new ClassBType(n)
- def apply(s: String): ClassBType = new ClassBType(s)
-
- def unapply(c: ClassBType): Option[(Int, Int)] =
+ /**
+ * Pattern matching on a ClassBType extracts the `internalName` of the class.
+ */
+ def unapply(c: ClassBType): Option[String] =
if (c == null) None
- else Some((c.offset, c.length))
+ else Some(c.internalName)
+
+ /**
+ * Valid flags for InnerClass attribute entry.
+ * See http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6
+ */
+ private val INNER_CLASSES_FLAGS = {
+ asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED |
+ asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL | asm.Opcodes.ACC_INTERFACE |
+ asm.Opcodes.ACC_ABSTRACT | asm.Opcodes.ACC_SYNTHETIC | asm.Opcodes.ACC_ANNOTATION |
+ asm.Opcodes.ACC_ENUM
+ }
+
+ // Primitive classes have no super class. A ClassBType for those is only created when
+ // they are actually being compiled (e.g., when compiling scala/Boolean.scala).
+ private val hasNoSuper = Set(
+ "scala/Unit",
+ "scala/Boolean",
+ "scala/Char",
+ "scala/Byte",
+ "scala/Short",
+ "scala/Int",
+ "scala/Float",
+ "scala/Long",
+ "scala/Double"
+ )
+
+ private val isInternalPhantomType = Set(
+ "scala/Null",
+ "scala/Nothing"
+ )
}
+ /**
+ * The type info for a class. Used for symboltable-independent subtype checks in the backend.
+ *
+ * @param superClass The super class, not defined for class java/lang/Object.
+ * @param interfaces All transitively implemented interfaces, except for those inherited
+ * through the superclass.
+ * @param flags The java flags, obtained through `javaFlags`. Used also to derive
+ * the flags for InnerClass entries.
+ * @param memberClasses Classes nested in this class. Those need to be added to the
+ * InnerClass table, see the InnerClass spec summary above.
+ * @param nestedInfo If this describes a nested class, information for the InnerClass table.
+ */
+ case class ClassInfo(superClass: Option[ClassBType], interfaces: List[ClassBType], flags: Int,
+ memberClasses: List[ClassBType], nestedInfo: Option[NestedInfo])
+
+ /**
+ * Information required to add a class to an InnerClass table.
+ * The spec summary above explains what information is required for the InnerClass entry.
+ *
+ * @param enclosingClass The enclosing class, if it is also nested. When adding a class
+ * to the InnerClass table, enclosing nested classes are also added.
+ * @param outerName The outerName field in the InnerClass entry, may be None.
+ * @param innerName The innerName field, may be None.
+ * @param isStaticNestedClass True if this is a static nested class (not inner class) (*)
+ *
+ * (*) Note that the STATIC flag in ClassInfo.flags, obtained through javaFlags(classSym), is not
+ * correct for the InnerClass entry, see javaFlags. The static flag in the InnerClass describes
+ * a source-level propety: if the class is in a static context (does not have an outer pointer).
+ * This is checked when building the NestedInfo.
+ */
+ case class NestedInfo(enclosingClass: ClassBType,
+ outerName: Option[String],
+ innerName: Option[String],
+ isStaticNestedClass: Boolean)
+
+ /**
+ * This class holds the data for an entry in the InnerClass table. See the InnerClass summary
+ * above in this file.
+ *
+ * There's some overlap with the class NestedInfo, but it's not exactly the same and cleaner to
+ * keep separate.
+ * @param name The internal name of the class.
+ * @param outerName The internal name of the outer class, may be null.
+ * @param innerName The simple name of the inner class, may be null.
+ * @param flags The flags for this class in the InnerClass entry.
+ */
+ case class InnerClassEntry(name: String, outerName: String, innerName: String, flags: Int)
+
case class ArrayBType(componentType: BType) extends RefBType {
def dimension: Int = componentType match {
case a: ArrayBType => 1 + a.dimension
@@ -311,107 +851,23 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
}
}
- case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType {
- private def this(types: (List[BType], BType)) = this(types._1, types._2)
- def this(descriptor: String) = this(MethodBType.decomposeMethodDescriptor(descriptor))
- }
+ case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType
- object MethodBType {
- private def decomposeMethodDescriptor(descriptor: String): (List[BType], BType) = {
- val chars = descriptor.toCharArray
- assert(chars(0) == '(', s"Not a valid method descriptor: $descriptor")
- var i = 1
- val argTypes = new ListBuffer[BType]
- while (chars(i) != ')') {
- val (argType, next) = BType.fromNonMethodDescriptor(chars, i)
- argTypes += argType
- i = next
- }
- val (resType, _) = BType.fromNonMethodDescriptor(chars, i + 1) // `i + 1` to skip the ')'
- (argTypes.toList, resType)
- }
- def apply(descriptor: String) = {
- val (argTypes, resType) = decomposeMethodDescriptor(descriptor)
- new MethodBType(argTypes, resType)
- }
- }
-
- val BOXED_UNIT = ClassBType("java/lang/Void")
- val BOXED_BOOLEAN = ClassBType("java/lang/Boolean")
- val BOXED_BYTE = ClassBType("java/lang/Byte")
- val BOXED_SHORT = ClassBType("java/lang/Short")
- val BOXED_CHAR = ClassBType("java/lang/Character")
- val BOXED_INT = ClassBType("java/lang/Integer")
- val BOXED_LONG = ClassBType("java/lang/Long")
- val BOXED_FLOAT = ClassBType("java/lang/Float")
- val BOXED_DOUBLE = ClassBType("java/lang/Double")
-
- /*
- * RT_NOTHING and RT_NULL exist at run-time only. They are the bytecode-level manifestation (in
- * method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs.
- *
- * Therefore, when RT_NOTHING or RT_NULL are to be emitted, a mapping is needed: the internal
- * names of NothingClass and NullClass can't be emitted as-is.
+ /* Some definitions that are required for the implementation of BTypes. They are abstract because
+ * initializing them requires information from types / symbols, which is not accessible here in
+ * BTypes.
+ *
+ * They are defs (not vals) because they are implemented using vars (see comment on CoreBTypes).
*/
- val RT_NOTHING = ClassBType("scala/runtime/Nothing$")
- val RT_NULL = ClassBType("scala/runtime/Null$")
- val CT_NOTHING = ClassBType("scala/Nothing")
- val CT_NULL = ClassBType("scala/Null")
-
- val srBooleanRef = ClassBType("scala/runtime/BooleanRef")
- val srByteRef = ClassBType("scala/runtime/ByteRef")
- val srCharRef = ClassBType("scala/runtime/CharRef")
- val srIntRef = ClassBType("scala/runtime/IntRef")
- val srLongRef = ClassBType("scala/runtime/LongRef")
- val srFloatRef = ClassBType("scala/runtime/FloatRef")
- val srDoubleRef = ClassBType("scala/runtime/DoubleRef")
/**
- * Map from type kinds to the Java reference types.
- * Useful when pushing class literals onto the operand stack (ldc instruction taking a class
- * literal).
- * @see Predef.classOf
- * @see genConstant()
- *
- * TODO @lry rename to "boxedClassOfPrimitive" or so, check usages
+ * Just a named pair, used in CoreBTypes.asmBoxTo/asmUnboxTo.
*/
- val classLiteral = immutable.Map[BType, ClassBType](
- UNIT -> BOXED_UNIT,
- BOOL -> BOXED_BOOLEAN,
- BYTE -> BOXED_BYTE,
- SHORT -> BOXED_SHORT,
- CHAR -> BOXED_CHAR,
- INT -> BOXED_INT,
- LONG -> BOXED_LONG,
- FLOAT -> BOXED_FLOAT,
- DOUBLE -> BOXED_DOUBLE
- )
-
- case class MethodNameAndType(name: String, descriptor: String)
-
- val asmBoxTo: immutable.Map[BType, MethodNameAndType] = {
- Map(
- BOOL -> MethodNameAndType("boxToBoolean", "(Z)Ljava/lang/Boolean;" ) ,
- BYTE -> MethodNameAndType("boxToByte", "(B)Ljava/lang/Byte;" ) ,
- CHAR -> MethodNameAndType("boxToCharacter", "(C)Ljava/lang/Character;") ,
- SHORT -> MethodNameAndType("boxToShort", "(S)Ljava/lang/Short;" ) ,
- INT -> MethodNameAndType("boxToInteger", "(I)Ljava/lang/Integer;" ) ,
- LONG -> MethodNameAndType("boxToLong", "(J)Ljava/lang/Long;" ) ,
- FLOAT -> MethodNameAndType("boxToFloat", "(F)Ljava/lang/Float;" ) ,
- DOUBLE -> MethodNameAndType("boxToDouble", "(D)Ljava/lang/Double;" )
- )
- }
+ final case class MethodNameAndType(name: String, methodType: MethodBType)
- val asmUnboxTo: immutable.Map[BType, MethodNameAndType] = {
- Map(
- BOOL -> MethodNameAndType("unboxToBoolean", "(Ljava/lang/Object;)Z") ,
- BYTE -> MethodNameAndType("unboxToByte", "(Ljava/lang/Object;)B") ,
- CHAR -> MethodNameAndType("unboxToChar", "(Ljava/lang/Object;)C") ,
- SHORT -> MethodNameAndType("unboxToShort", "(Ljava/lang/Object;)S") ,
- INT -> MethodNameAndType("unboxToInt", "(Ljava/lang/Object;)I") ,
- LONG -> MethodNameAndType("unboxToLong", "(Ljava/lang/Object;)J") ,
- FLOAT -> MethodNameAndType("unboxToFloat", "(Ljava/lang/Object;)F") ,
- DOUBLE -> MethodNameAndType("unboxToDouble", "(Ljava/lang/Object;)D")
- )
- }
+ /**
+ * True if the current compilation unit is of a primitive class (scala.Boolean et al).
+ * Used only in assertions. Abstract here because its implementation depends on global.
+ */
+ def isCompilingPrimitive: Boolean
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
new file mode 100644
index 0000000000..0e2f938602
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -0,0 +1,396 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc
+package backend.jvm
+
+import scala.tools.asm
+
+/**
+ * This class mainly contains the method classBTypeFromSymbol, which extracts the necessary
+ * information from a symbol and its type to create the correpsonding ClassBType. It requires
+ * access to the compiler (global parameter).
+ *
+ * The mixin CoreBTypes defines core BTypes that are used in the backend. Building these BTypes
+ * uses classBTypeFromSymbol, hence requires access to the compiler (global).
+ *
+ * BTypesFromSymbols extends BTypes because the implementation of BTypes requires access to some
+ * of the core btypes. They are declared in BTypes as abstract members. Note that BTypes does
+ * not have access to the compiler instance.
+ */
+class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
+ import global._
+ import definitions._
+
+ val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global)
+ val bCodeAsmCommon: BCodeAsmCommon[global.type] = new BCodeAsmCommon(global)
+ import bCodeAsmCommon._
+
+ // Why the proxy, see documentation of class [[CoreBTypes]].
+ val coreBTypes = new CoreBTypesProxy[this.type](this)
+ import coreBTypes._
+
+ final def intializeCoreBTypes(): Unit = {
+ coreBTypes.setBTypes(new CoreBTypes[this.type](this))
+ }
+
+ def internalNameString(offset: Int, length: Int) = new String(global.chrs, offset, length)
+
+ protected val classBTypeFromInternalNameMap = {
+ global.perRunCaches.recordCache(collection.concurrent.TrieMap.empty[String, ClassBType])
+ }
+
+ /**
+ * Cache for the method classBTypeFromSymbol.
+ */
+ private val convertedClasses = perRunCaches.newMap[Symbol, ClassBType]()
+
+ // helpers that need access to global.
+ // TODO @lry create a separate component, they don't belong to BTypesFromSymbols
+
+ final val strMODULE_INSTANCE_FIELD = nme.MODULE_INSTANCE_FIELD.toString
+
+ private val primitiveCompilationUnits = Set(
+ "Unit.scala",
+ "Boolean.scala",
+ "Char.scala",
+ "Byte.scala",
+ "Short.scala",
+ "Int.scala",
+ "Float.scala",
+ "Long.scala",
+ "Double.scala"
+ )
+
+ /**
+ * True if the current compilation unit is of a primitive class (scala.Boolean et al).
+ * Used only in assertions.
+ */
+ def isCompilingPrimitive = {
+ primitiveCompilationUnits(currentUnit.source.file.name)
+ }
+
+ def isCompilingArray = {
+ currentUnit.source.file.name == "Array.scala"
+ }
+
+ // end helpers
+
+ /**
+ * The ClassBType for a class symbol `sym`.
+ */
+ final def classBTypeFromSymbol(classSym: Symbol): ClassBType = {
+ assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol")
+ assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym")
+ assert(
+ (!primitiveTypeMap.contains(classSym) || isCompilingPrimitive) &&
+ (classSym != NothingClass && classSym != NullClass),
+ s"Cannot create ClassBType for special class symbol ${classSym.fullName}")
+
+ convertedClasses.getOrElse(classSym, {
+ val internalName = classSym.javaBinaryName.toTypeName
+ // We first create and add the ClassBType to the hash map before computing its info. This
+ // allows initializing cylic dependencies, see the comment on variable ClassBType._info.
+ val classBType = new ClassBType(internalName.start, internalName.length)
+ convertedClasses(classSym) = classBType
+ setClassInfo(classSym, classBType)
+ })
+ }
+
+ private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = {
+ val superClassSym = if (classSym.isImplClass) ObjectClass else classSym.superClass
+ assert(
+ if (classSym == ObjectClass)
+ superClassSym == NoSymbol
+ else if (classSym.isInterface)
+ superClassSym == ObjectClass
+ else
+ // A ClassBType for a primitive class (scala.Boolean et al) is only created when compiling these classes.
+ ((superClassSym != NoSymbol) && !superClassSym.isInterface) || (isCompilingPrimitive && primitiveTypeMap.contains(classSym)),
+ s"Bad superClass for $classSym: $superClassSym"
+ )
+ val superClass = if (superClassSym == NoSymbol) None
+ else Some(classBTypeFromSymbol(superClassSym))
+
+ val interfaces = getSuperInterfaces(classSym).map(classBTypeFromSymbol)
+
+ val flags = javaFlags(classSym)
+
+ /* The InnerClass table of a class C must contain all nested classes of C, even if they are only
+ * declared but not otherwise referenced in C (from the bytecode or a method / field signature).
+ * We collect them here.
+ *
+ * Nested classes that are also referenced in C will be added to the innerClassBufferASM during
+ * code generation, but those duplicates will be eliminated when emitting the InnerClass
+ * attribute.
+ *
+ * Why doe we need to collect classes into innerClassBufferASM at all? To collect references to
+ * nested classes, but NOT nested in C, that are used within C.
+ */
+ val nestedClassSymbols = {
+ // The lambdalift phase lifts all nested classes to the enclosing class, so if we collect
+ // member classes right after lambdalift, we obtain all nested classes, including local and
+ // anonymous ones.
+ val nestedClasses = exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(classSym))
+
+ // If this is a top-level class, and it has a companion object, the member classes of the
+ // companion are added as members of the class. For example:
+ // class C { }
+ // object C {
+ // class D
+ // def f = { class E }
+ // }
+ // The class D is added as a member of class C. The reason is that the InnerClass attribute
+ // for D will containt class "C" and NOT the module class "C$" as the outer class of D.
+ // This is done by buildNestedInfo, the reason is Java compatibility, see comment in BTypes.
+ // For consistency, the InnerClass entry for D needs to be present in C - to Java it looks
+ // like D is a member of C, not C$.
+ val linkedClass = exitingPickler(classSym.linkedClassOfClass) // linkedCoC does not work properly in late phases
+ val companionModuleMembers = {
+ // phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes,
+ // not local classes of the companion module (E in the exmaple) that were lifted by lambdalift.
+ if (isTopLevelModuleClass(linkedClass)) exitingPickler(memberClassesOf(linkedClass))
+ else Nil
+ }
+
+ nestedClasses ++ companionModuleMembers
+ }
+
+ /**
+ * For nested java classes, the scala compiler creates both a class and a module (and therefore
+ * a module class) symbol. For example, in `class A { class B {} }`, the nestedClassSymbols
+ * for A contain both the class B and the module class B.
+ * Here we get rid of the module class B, making sure that the class B is present.
+ */
+ val nestedClassSymbolsNoJavaModuleClasses = nestedClassSymbols.filter(s => {
+ if (s.isJavaDefined && s.isModuleClass) {
+ // We could also search in nestedClassSymbols for s.linkedClassOfClass, but sometimes that
+ // returns NoSymbol, so it doesn't work.
+ val nb = nestedClassSymbols.count(mc => mc.name == s.name && mc.owner == s.owner)
+ assert(nb == 2, s"Java member module without member class: $s - $nestedClassSymbols")
+ false
+ } else true
+ })
+
+ val memberClasses = nestedClassSymbolsNoJavaModuleClasses.map(classBTypeFromSymbol)
+
+ val nestedInfo = buildNestedInfo(classSym)
+
+ classBType.info = ClassInfo(superClass, interfaces, flags, memberClasses, nestedInfo)
+ classBType
+ }
+
+ /**
+ * All interfaces implemented by a class, except for those inherited through the superclass.
+ *
+ * TODO @lry share code with GenASM
+ */
+ private def getSuperInterfaces(classSym: Symbol): List[Symbol] = {
+
+ // Additional interface parents based on annotations and other cues
+ def newParentForAnnotation(ann: AnnotationInfo): Symbol = ann.symbol match {
+ case RemoteAttr => RemoteInterfaceClass
+ case _ => NoSymbol
+ }
+
+ /**
+ * Drop redundant interfaces (which are implemented by some other parent) from the immediate
+ * parents. In other words, no two interfaces in the result are related by subtyping.
+ */
+ def dropRedundantInterfaces(lstIfaces: List[Symbol]): List[Symbol] = {
+ var rest = lstIfaces
+ var leaves = List.empty[Symbol]
+ while (!rest.isEmpty) {
+ val candidate = rest.head
+ val nonLeaf = leaves exists { lsym => lsym isSubClass candidate }
+ if (!nonLeaf) {
+ leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym })
+ }
+ rest = rest.tail
+ }
+
+ leaves
+ }
+
+ val superInterfaces0: List[Symbol] = classSym.mixinClasses
+ val superInterfaces = existingSymbols(superInterfaces0 ++ classSym.annotations.map(newParentForAnnotation)).distinct
+
+ assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}")
+ assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}")
+
+ dropRedundantInterfaces(superInterfaces)
+ }
+
+ private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = {
+ assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym")
+
+ val isNested = !innerClassSym.rawowner.isPackageClass
+ if (!isNested) None
+ else {
+ // See comment in BTypes, when is a class marked static in the InnerClass table.
+ val isStaticNestedClass = isOriginallyStaticOwner(innerClassSym.originalOwner)
+
+ // After lambdalift (which is where we are), the rawowoner field contains the enclosing class.
+ val enclosingClassSym = {
+ if (innerClassSym.isJavaDefined && innerClassSym.rawowner.isModuleClass) {
+ // Example java source: class C { static class D { } }
+ // The Scala compiler creates a class and a module symbol for C. Because D is a static
+ // nested class, the symbol for D is nested in the module class C (not in the class C).
+ // For the InnerClass attribute, we use the class symbol C, which represents the situation
+ // in the source code.
+
+ // Cannot use innerClassSym.isStatic: this method looks at the owner, which is a package
+ // at this pahse (after lambdalift, flatten).
+ assert(isOriginallyStaticOwner(innerClassSym.originalOwner), innerClassSym.originalOwner)
+
+ // phase travel for linkedCoC - does not always work in late phases
+ exitingPickler(innerClassSym.rawowner.linkedClassOfClass)
+ }
+ else innerClassSym.rawowner
+ }
+ val enclosingClass: ClassBType = classBTypeFromSymbol(enclosingClassSym)
+
+ val outerName: Option[String] = {
+ if (isAnonymousOrLocalClass(innerClassSym)) {
+ None
+ } else {
+ val outerName = innerClassSym.rawowner.javaBinaryName
+ // Java compatibility. See the big comment in BTypes that summarizes the InnerClass spec.
+ val outerNameModule = if (isTopLevelModuleClass(innerClassSym.rawowner)) outerName.dropModule
+ else outerName
+ Some(outerNameModule.toString)
+ }
+ }
+
+ val innerName: Option[String] = {
+ if (innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction) None
+ else Some(innerClassSym.rawname + innerClassSym.moduleSuffix) // moduleSuffix for module classes
+ }
+
+ Some(NestedInfo(enclosingClass, outerName, innerName, isStaticNestedClass))
+ }
+ }
+
+ /**
+ * True for module classes of package level objects. The backend will generate a mirror class for
+ * such objects.
+ */
+ final def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler {
+ // phase travel to pickler required for isNestedClass (looks at owner)
+ val r = sym.isModuleClass && !sym.isNestedClass
+ // The mixin phase adds the `lateMODULE` flag to trait implementation classes. Since the flag
+ // is late, it should not be visible here inside the time travel. We check this.
+ if (r) assert(!sym.isImplClass, s"isModuleClass should be false for impl class $sym")
+ r
+ }
+
+ /**
+ * True for module classes of modules that are top-level or owned only by objects. Module classes
+ * for such objects will get a MODULE$ flag and a corresponding static initializer.
+ */
+ final def isStaticModuleClass(sym: Symbol): Boolean = {
+ /* (1) Phase travel to to pickler is required to exclude implementation classes; they have the
+ * lateMODULEs after mixin, so isModuleClass would be true.
+ * (2) isStaticModuleClass is a source-level property. See comment on isOriginallyStaticOwner.
+ */
+ exitingPickler { // (1)
+ sym.isModuleClass &&
+ isOriginallyStaticOwner(sym.originalOwner) // (2)
+ }
+ }
+
+ // legacy, to be removed when the @remote annotation gets removed
+ final def isRemote(s: Symbol) = (s hasAnnotation definitions.RemoteAttr)
+ final def hasPublicBitSet(flags: Int) = ((flags & asm.Opcodes.ACC_PUBLIC) != 0)
+
+ /**
+ * Return the Java modifiers for the given symbol.
+ * Java modifiers for classes:
+ * - public, abstract, final, strictfp (not used)
+ * for interfaces:
+ * - the same as for classes, without 'final'
+ * for fields:
+ * - public, private (*)
+ * - static, final
+ * for methods:
+ * - the same as for fields, plus:
+ * - abstract, synchronized (not used), strictfp (not used), native (not used)
+ * for all:
+ * - deprecated
+ *
+ * (*) protected cannot be used, since inner classes 'see' protected members,
+ * and they would fail verification after lifted.
+ */
+ final def javaFlags(sym: Symbol): Int = {
+ // constructors of module classes should be private. introduced in b06edbc, probably to prevent
+ // creating module instances from java. for nested modules, the constructor needs to be public
+ // since they are created by the outer class and stored in a field. a java client can create
+ // new instances via outerClassInstance.new InnerModuleClass$().
+ // TODO: do this early, mark the symbol private.
+ val privateFlag =
+ sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass(sym.owner))
+
+ // Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also
+ // added to modules and module classes, not anymore since 296b706).
+ // Note that the presence of the `FINAL` flag on a symbol does not correspond 1:1 to emitting
+ // ACC_FINAL in bytecode.
+ //
+ // Top-level modules are marked ACC_FINAL in bytecode (even without the FINAL flag). Nested
+ // objects don't get the flag to allow overriding (under -Yoverride-objects, SI-5676).
+ //
+ // For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't:
+ // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
+ // "Another problem is that the specification allows aggressive
+ // optimization of final fields. Within a thread, it is permissible to
+ // reorder reads of a final field with those modifications of a final
+ // field that do not take place in the constructor."
+ //
+ // A var or lazy val which is marked final still has meaning to the
+ // scala compiler. The word final is heavily overloaded unfortunately;
+ // for us it means "not overridable". At present you can't override
+ // vars regardless; this may change.
+ //
+ // The logic does not check .isFinal (which checks flags for the FINAL flag,
+ // and includes symbols marked lateFINAL) instead inspecting rawflags so
+ // we can exclude lateFINAL. Such symbols are eligible for inlining, but to
+ // avoid breaking proxy software which depends on subclassing, we do not
+ // emit ACC_FINAL.
+
+ val finalFlag = (
+ (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym))
+ && !sym.enclClass.isInterface
+ && !sym.isClassConstructor
+ && !sym.isMutable // lazy vals and vars both
+ )
+
+ // Primitives are "abstract final" to prohibit instantiation
+ // without having to provide any implementations, but that is an
+ // illegal combination of modifiers at the bytecode level so
+ // suppress final if abstract if present.
+ import asm.Opcodes._
+ GenBCode.mkFlags(
+ if (privateFlag) ACC_PRIVATE else ACC_PUBLIC,
+ if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0,
+ if (sym.isInterface) ACC_INTERFACE else 0,
+ if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0,
+ if (sym.isStaticMember) ACC_STATIC else 0,
+ if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
+ if (sym.isArtifact) ACC_SYNTHETIC else 0,
+ if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
+ if (sym.hasEnumFlag) ACC_ENUM else 0,
+ if (sym.isVarargsMethod) ACC_VARARGS else 0,
+ if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0,
+ if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0
+ )
+ }
+
+ def javaFieldFlags(sym: Symbol) = {
+ javaFlags(sym) | GenBCode.mkFlags(
+ if (sym hasAnnotation TransientAttr) asm.Opcodes.ACC_TRANSIENT else 0,
+ if (sym hasAnnotation VolatileAttr) asm.Opcodes.ACC_VOLATILE else 0,
+ if (sym.isMutable) 0 else asm.Opcodes.ACC_FINAL
+ )
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
new file mode 100644
index 0000000000..fac3c93be2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
@@ -0,0 +1,293 @@
+package scala.tools.nsc
+package backend.jvm
+
+import scala.annotation.switch
+
+/**
+ * Core BTypes and some other definitions. The initialization of these definitions requies access
+ * to symbols / types (global).
+ *
+ * The symbols used to initialize the ClassBTypes may change from one compiler run to the next. To
+ * make sure the definitions are consistent with the symbols in the current run, the
+ * `intializeCoreBTypes` method in BTypesFromSymbols creates a new instance of CoreBTypes in each
+ * compiler run.
+ *
+ * The class BTypesFromSymbols does not directly reference CoreBTypes, but CoreBTypesProxy. The
+ * reason is that having a `var bTypes: CoreBTypes` would not allow `import bTypes._`. Instead, the
+ * proxy class holds a `CoreBTypes` in a variable field and forwards to this instance.
+ *
+ * The definitions in `CoreBTypes` need to be lazy vals to break an initialization cycle. When
+ * creating a new instance to assign to the proxy, the `classBTypeFromSymbol` invoked in the
+ * constructor will actucally go through the proxy. The lazy vals make sure the instance is assigned
+ * in the proxy before the fields are initialized.
+ *
+ * Note: if we did not re-create the core BTypes on each compiler run, BType.classBTypeFromInternalNameMap
+ * could not be a perRunCache anymore: the classes defeined here need to be in that map, they are
+ * added when the ClassBTypes are created. The per run cache removes them, so they would be missing
+ * in the second run.
+ */
+class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
+ import bTypes._
+ import global._
+ import rootMirror.{requiredClass, getClassIfDefined}
+ import definitions._
+
+ /**
+ * Maps primitive types to their corresponding PrimitiveBType. The map is defined lexically above
+ * the first use of `classBTypeFromSymbol` because that method looks at the map.
+ */
+ lazy val primitiveTypeMap: Map[Symbol, PrimitiveBType] = Map(
+ UnitClass -> UNIT,
+ BooleanClass -> BOOL,
+ CharClass -> CHAR,
+ ByteClass -> BYTE,
+ ShortClass -> SHORT,
+ IntClass -> INT,
+ LongClass -> LONG,
+ FloatClass -> FLOAT,
+ DoubleClass -> DOUBLE
+ )
+
+ lazy val BOXED_UNIT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Void])
+ lazy val BOXED_BOOLEAN : ClassBType = classBTypeFromSymbol(BoxedBooleanClass)
+ lazy val BOXED_BYTE : ClassBType = classBTypeFromSymbol(BoxedByteClass)
+ lazy val BOXED_SHORT : ClassBType = classBTypeFromSymbol(BoxedShortClass)
+ lazy val BOXED_CHAR : ClassBType = classBTypeFromSymbol(BoxedCharacterClass)
+ lazy val BOXED_INT : ClassBType = classBTypeFromSymbol(BoxedIntClass)
+ lazy val BOXED_LONG : ClassBType = classBTypeFromSymbol(BoxedLongClass)
+ lazy val BOXED_FLOAT : ClassBType = classBTypeFromSymbol(BoxedFloatClass)
+ lazy val BOXED_DOUBLE : ClassBType = classBTypeFromSymbol(BoxedDoubleClass)
+
+ /**
+ * Map from primitive types to their boxed class type. Useful when pushing class literals onto the
+ * operand stack (ldc instruction taking a class literal), see genConstant.
+ */
+ lazy val boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = Map(
+ UNIT -> BOXED_UNIT,
+ BOOL -> BOXED_BOOLEAN,
+ BYTE -> BOXED_BYTE,
+ SHORT -> BOXED_SHORT,
+ CHAR -> BOXED_CHAR,
+ INT -> BOXED_INT,
+ LONG -> BOXED_LONG,
+ FLOAT -> BOXED_FLOAT,
+ DOUBLE -> BOXED_DOUBLE
+ )
+
+ lazy val boxedClasses: Set[ClassBType] = boxedClassOfPrimitive.values.toSet
+
+ /**
+ * Maps the method symbol for a box method to the boxed type of the result. For example, the
+ * method symbol for `Byte.box()` is mapped to the ClassBType `java/lang/Byte`.
+ */
+ lazy val boxResultType: Map[Symbol, ClassBType] = {
+ for ((valueClassSym, boxMethodSym) <- currentRun.runDefinitions.boxMethod)
+ yield boxMethodSym -> boxedClassOfPrimitive(primitiveTypeMap(valueClassSym))
+ }
+
+ /**
+ * Maps the method symbol for an unbox method to the primitive type of the result.
+ * For example, the method symbol for `Byte.unbox()`) is mapped to the PrimitiveBType BYTE. */
+ lazy val unboxResultType: Map[Symbol, PrimitiveBType] = {
+ for ((valueClassSym, unboxMethodSym) <- currentRun.runDefinitions.unboxMethod)
+ yield unboxMethodSym -> primitiveTypeMap(valueClassSym)
+ }
+
+ /*
+ * RT_NOTHING and RT_NULL exist at run-time only. They are the bytecode-level manifestation (in
+ * method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs.
+ *
+ * Therefore, when RT_NOTHING or RT_NULL are to be emitted, a mapping is needed: the internal
+ * names of NothingClass and NullClass can't be emitted as-is.
+ * TODO @lry Once there's a 2.11.3 starr, use the commented argument list. The current starr crashes on the type literal `scala.runtime.Nothing$`
+ */
+ lazy val RT_NOTHING : ClassBType = classBTypeFromSymbol(rootMirror.getRequiredClass("scala.runtime.Nothing$")) // (requiredClass[scala.runtime.Nothing$])
+ lazy val RT_NULL : ClassBType = classBTypeFromSymbol(rootMirror.getRequiredClass("scala.runtime.Null$")) // (requiredClass[scala.runtime.Null$])
+
+ lazy val ObjectReference : ClassBType = classBTypeFromSymbol(ObjectClass)
+ lazy val objArrayReference : ArrayBType = ArrayBType(ObjectReference)
+
+ lazy val StringReference : ClassBType = classBTypeFromSymbol(StringClass)
+ lazy val StringBuilderReference : ClassBType = classBTypeFromSymbol(StringBuilderClass)
+ lazy val ThrowableReference : ClassBType = classBTypeFromSymbol(ThrowableClass)
+ lazy val jlCloneableReference : ClassBType = classBTypeFromSymbol(JavaCloneableClass) // java/lang/Cloneable
+ lazy val jlNPEReference : ClassBType = classBTypeFromSymbol(NullPointerExceptionClass) // java/lang/NullPointerException
+ lazy val jioSerializableReference : ClassBType = classBTypeFromSymbol(JavaSerializableClass) // java/io/Serializable
+ lazy val scalaSerializableReference : ClassBType = classBTypeFromSymbol(SerializableClass) // scala/Serializable
+ lazy val classCastExceptionReference : ClassBType = classBTypeFromSymbol(ClassCastExceptionClass) // java/lang/ClassCastException
+
+ lazy val srBooleanRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BooleanRef])
+ lazy val srByteRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.ByteRef])
+ lazy val srCharRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.CharRef])
+ lazy val srIntRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.IntRef])
+ lazy val srLongRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LongRef])
+ lazy val srFloatRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.FloatRef])
+ lazy val srDoubleRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.DoubleRef])
+
+ lazy val hashMethodSym: Symbol = getMember(ScalaRunTimeModule, nme.hash_)
+
+ // TODO @lry avoiding going through through missingHook for every line in the REPL: https://github.com/scala/scala/commit/8d962ed4ddd310cc784121c426a2e3f56a112540
+ lazy val AndroidParcelableInterface : Symbol = getClassIfDefined("android.os.Parcelable")
+ lazy val AndroidCreatorClass : Symbol = getClassIfDefined("android.os.Parcelable$Creator")
+
+ lazy val BeanInfoAttr: Symbol = requiredClass[scala.beans.BeanInfo]
+
+ /* The Object => String overload. */
+ lazy val String_valueOf: Symbol = {
+ getMember(StringModule, nme.valueOf) filter (sym => sym.info.paramTypes match {
+ case List(pt) => pt.typeSymbol == ObjectClass
+ case _ => false
+ })
+ }
+
+ // scala.FunctionX and scala.runtim.AbstractFunctionX
+ lazy val FunctionReference : Vector[ClassBType] = (0 to MaxFunctionArity).map(i => classBTypeFromSymbol(FunctionClass(i)))(collection.breakOut)
+ lazy val AbstractFunctionReference : Vector[ClassBType] = (0 to MaxFunctionArity).map(i => classBTypeFromSymbol(AbstractFunctionClass(i)))(collection.breakOut)
+ lazy val AbstractFunctionArityMap : Map[ClassBType, Int] = AbstractFunctionReference.zipWithIndex.toMap
+
+ lazy val PartialFunctionReference : ClassBType = classBTypeFromSymbol(PartialFunctionClass)
+ lazy val AbstractPartialFunctionReference : ClassBType = classBTypeFromSymbol(AbstractPartialFunctionClass)
+
+ lazy val BoxesRunTime: ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])
+
+ /**
+ * Methods in scala.runtime.BoxesRuntime
+ */
+ lazy val asmBoxTo : Map[BType, MethodNameAndType] = Map(
+ BOOL -> MethodNameAndType("boxToBoolean", MethodBType(List(BOOL), BOXED_BOOLEAN)),
+ BYTE -> MethodNameAndType("boxToByte", MethodBType(List(BYTE), BOXED_BYTE)),
+ CHAR -> MethodNameAndType("boxToCharacter", MethodBType(List(CHAR), BOXED_CHAR)),
+ SHORT -> MethodNameAndType("boxToShort", MethodBType(List(SHORT), BOXED_SHORT)),
+ INT -> MethodNameAndType("boxToInteger", MethodBType(List(INT), BOXED_INT)),
+ LONG -> MethodNameAndType("boxToLong", MethodBType(List(LONG), BOXED_LONG)),
+ FLOAT -> MethodNameAndType("boxToFloat", MethodBType(List(FLOAT), BOXED_FLOAT)),
+ DOUBLE -> MethodNameAndType("boxToDouble", MethodBType(List(DOUBLE), BOXED_DOUBLE))
+ )
+
+ lazy val asmUnboxTo: Map[BType, MethodNameAndType] = Map(
+ BOOL -> MethodNameAndType("unboxToBoolean", MethodBType(List(ObjectReference), BOOL)),
+ BYTE -> MethodNameAndType("unboxToByte", MethodBType(List(ObjectReference), BYTE)),
+ CHAR -> MethodNameAndType("unboxToChar", MethodBType(List(ObjectReference), CHAR)),
+ SHORT -> MethodNameAndType("unboxToShort", MethodBType(List(ObjectReference), SHORT)),
+ INT -> MethodNameAndType("unboxToInt", MethodBType(List(ObjectReference), INT)),
+ LONG -> MethodNameAndType("unboxToLong", MethodBType(List(ObjectReference), LONG)),
+ FLOAT -> MethodNameAndType("unboxToFloat", MethodBType(List(ObjectReference), FLOAT)),
+ DOUBLE -> MethodNameAndType("unboxToDouble", MethodBType(List(ObjectReference), DOUBLE))
+ )
+
+ lazy val typeOfArrayOp: Map[Int, BType] = {
+ import scalaPrimitives._
+ Map(
+ (List(ZARRAY_LENGTH, ZARRAY_GET, ZARRAY_SET) map (_ -> BOOL)) ++
+ (List(BARRAY_LENGTH, BARRAY_GET, BARRAY_SET) map (_ -> BYTE)) ++
+ (List(SARRAY_LENGTH, SARRAY_GET, SARRAY_SET) map (_ -> SHORT)) ++
+ (List(CARRAY_LENGTH, CARRAY_GET, CARRAY_SET) map (_ -> CHAR)) ++
+ (List(IARRAY_LENGTH, IARRAY_GET, IARRAY_SET) map (_ -> INT)) ++
+ (List(LARRAY_LENGTH, LARRAY_GET, LARRAY_SET) map (_ -> LONG)) ++
+ (List(FARRAY_LENGTH, FARRAY_GET, FARRAY_SET) map (_ -> FLOAT)) ++
+ (List(DARRAY_LENGTH, DARRAY_GET, DARRAY_SET) map (_ -> DOUBLE)) ++
+ (List(OARRAY_LENGTH, OARRAY_GET, OARRAY_SET) map (_ -> ObjectReference)) : _*
+ )
+ }
+}
+
+/**
+ * This trait make some core BTypes availalbe that don't depend on a Global instance. Some core
+ * BTypes are required to be accessible in the BTypes trait, which does not have access to Global.
+ *
+ * BTypes cannot refer to CoreBTypesProxy because some of its members depend on global, for example
+ * the type Symbol in
+ * def primitiveTypeMap: Map[Symbol, PrimitiveBType]
+ */
+trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
+ val bTypes: BTS
+ import bTypes._
+
+ def boxedClasses: Set[ClassBType]
+
+ def RT_NOTHING : ClassBType
+ def RT_NULL : ClassBType
+
+ def ObjectReference : ClassBType
+ def jlCloneableReference : ClassBType
+ def jioSerializableReference : ClassBType
+}
+
+/**
+ * See comment in class [[CoreBTypes]].
+ */
+final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) extends CoreBTypesProxyGlobalIndependent[BTFS] {
+ import bTypes._
+ import global._
+
+ private[this] var _coreBTypes: CoreBTypes[bTypes.type] = _
+ def setBTypes(coreBTypes: CoreBTypes[BTFS]): Unit = {
+ _coreBTypes = coreBTypes.asInstanceOf[CoreBTypes[bTypes.type]]
+ }
+
+ def primitiveTypeMap: Map[Symbol, PrimitiveBType] = _coreBTypes.primitiveTypeMap
+
+ def BOXED_UNIT : ClassBType = _coreBTypes.BOXED_UNIT
+ def BOXED_BOOLEAN : ClassBType = _coreBTypes.BOXED_BOOLEAN
+ def BOXED_BYTE : ClassBType = _coreBTypes.BOXED_BYTE
+ def BOXED_SHORT : ClassBType = _coreBTypes.BOXED_SHORT
+ def BOXED_CHAR : ClassBType = _coreBTypes.BOXED_CHAR
+ def BOXED_INT : ClassBType = _coreBTypes.BOXED_INT
+ def BOXED_LONG : ClassBType = _coreBTypes.BOXED_LONG
+ def BOXED_FLOAT : ClassBType = _coreBTypes.BOXED_FLOAT
+ def BOXED_DOUBLE : ClassBType = _coreBTypes.BOXED_DOUBLE
+
+ def boxedClasses: Set[ClassBType] = _coreBTypes.boxedClasses
+
+ def boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = _coreBTypes.boxedClassOfPrimitive
+
+ def boxResultType: Map[Symbol, ClassBType] = _coreBTypes.boxResultType
+
+ def unboxResultType: Map[Symbol, PrimitiveBType] = _coreBTypes.unboxResultType
+
+ def RT_NOTHING : ClassBType = _coreBTypes.RT_NOTHING
+ def RT_NULL : ClassBType = _coreBTypes.RT_NULL
+
+ def ObjectReference : ClassBType = _coreBTypes.ObjectReference
+ def objArrayReference : ArrayBType = _coreBTypes.objArrayReference
+
+ def StringReference : ClassBType = _coreBTypes.StringReference
+ def StringBuilderReference : ClassBType = _coreBTypes.StringBuilderReference
+ def ThrowableReference : ClassBType = _coreBTypes.ThrowableReference
+ def jlCloneableReference : ClassBType = _coreBTypes.jlCloneableReference
+ def jlNPEReference : ClassBType = _coreBTypes.jlNPEReference
+ def jioSerializableReference : ClassBType = _coreBTypes.jioSerializableReference
+ def scalaSerializableReference : ClassBType = _coreBTypes.scalaSerializableReference
+ def classCastExceptionReference : ClassBType = _coreBTypes.classCastExceptionReference
+
+ def srBooleanRef : ClassBType = _coreBTypes.srBooleanRef
+ def srByteRef : ClassBType = _coreBTypes.srByteRef
+ def srCharRef : ClassBType = _coreBTypes.srCharRef
+ def srIntRef : ClassBType = _coreBTypes.srIntRef
+ def srLongRef : ClassBType = _coreBTypes.srLongRef
+ def srFloatRef : ClassBType = _coreBTypes.srFloatRef
+ def srDoubleRef : ClassBType = _coreBTypes.srDoubleRef
+
+ def hashMethodSym: Symbol = _coreBTypes.hashMethodSym
+
+ def AndroidParcelableInterface : Symbol = _coreBTypes.AndroidParcelableInterface
+ def AndroidCreatorClass : Symbol = _coreBTypes.AndroidCreatorClass
+
+ def BeanInfoAttr: Symbol = _coreBTypes.BeanInfoAttr
+
+ def String_valueOf: Symbol = _coreBTypes.String_valueOf
+
+ def FunctionReference : Vector[ClassBType] = _coreBTypes.FunctionReference
+ def AbstractFunctionReference : Vector[ClassBType] = _coreBTypes.AbstractFunctionReference
+ def AbstractFunctionArityMap : Map[ClassBType, Int] = _coreBTypes.AbstractFunctionArityMap
+
+ def PartialFunctionReference : ClassBType = _coreBTypes.PartialFunctionReference
+ def AbstractPartialFunctionReference : ClassBType = _coreBTypes.AbstractPartialFunctionReference
+
+ def BoxesRunTime: ClassBType = _coreBTypes.BoxesRunTime
+
+ def asmBoxTo : Map[BType, MethodNameAndType] = _coreBTypes.asmBoxTo
+ def asmUnboxTo: Map[BType, MethodNameAndType] = _coreBTypes.asmUnboxTo
+
+ def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index b0fb3069c1..2593903b9d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -26,6 +26,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
import icodes.opcodes._
import definitions._
+ val bCodeAsmCommon: BCodeAsmCommon[global.type] = new BCodeAsmCommon(global)
+ import bCodeAsmCommon._
+
// Strangely I can't find this in the asm code
// 255, but reserving 1 for "this"
final val MaximumJvmParameters = 254
@@ -381,6 +384,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
case "jvm-1.5" => asm.Opcodes.V1_5
case "jvm-1.6" => asm.Opcodes.V1_6
case "jvm-1.7" => asm.Opcodes.V1_7
+ case "jvm-1.8" => asm.Opcodes.V1_8
}
private val majorVersion: Int = (classfileVersion & 0xFF)
@@ -620,7 +624,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
* That means non-member classes (anonymous). See Section 4.7.5 in the JVMS.
*/
def outerName(innerSym: Symbol): String = {
- if (innerSym.originalEnclosingMethod != NoSymbol)
+ if (isAnonymousOrLocalClass(innerSym))
null
else {
val outerName = javaName(innerSym.rawowner)
@@ -635,10 +639,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
else
innerSym.rawname + innerSym.moduleSuffix
- // add inner classes which might not have been referenced yet
- exitingErasure {
- for (sym <- List(csym, csym.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass)
- innerClassBuffer += m
+ // This collects all inner classes of csym, including local and anonymous: lambdalift makes
+ // them members of their enclosing class.
+ innerClassBuffer ++= exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(csym))
+
+ // Add members of the companion object (if top-level). why, see comment in BTypes.scala.
+ val linkedClass = exitingPickler(csym.linkedClassOfClass) // linkedCoC does not work properly in late phases
+ if (isTopLevelModule(linkedClass)) {
+ // phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes,
+ // not local classes that were lifted by lambdalift.
+ innerClassBuffer ++= exitingPickler(memberClassesOf(linkedClass))
}
val allInners: List[Symbol] = innerClassBuffer.toList filterNot deadCode.elidedClosures
@@ -652,7 +662,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// sort them so inner classes succeed their enclosing class to satisfy the Eclipse Java compiler
for (innerSym <- allInners sortBy (_.name.length)) { // TODO why not sortBy (_.name.toString()) ??
val flagsWithFinal: Int = mkFlags(
- if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0,
+ // See comment in BTypes, when is a class marked static in the InnerClass table.
+ if (isOriginallyStaticOwner(innerSym.originalOwner)) asm.Opcodes.ACC_STATIC else 0,
javaFlags(innerSym),
if(isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag
) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED)
@@ -1218,10 +1229,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
null /* SourceDebugExtension */)
}
- val enclM = getEnclosingMethodAttribute()
- if(enclM != null) {
- val EnclMethodEntry(className, methodName, methodType) = enclM
- jclass.visitOuterClass(className, methodName, methodType.getDescriptor)
+ enclosingMethodAttribute(clasz.symbol, javaName, javaType(_).getDescriptor) match {
+ case Some(EnclosingMethodEntry(className, methodName, methodDescriptor)) =>
+ jclass.visitOuterClass(className, methodName, methodDescriptor)
+ case _ => ()
}
// typestate: entering mode with valid call sequences:
@@ -1282,45 +1293,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol)
}
- /**
- * @param owner internal name of the enclosing class of the class.
- *
- * @param name the name of the method that contains the class.
-
- * @param methodType the method that contains the class.
- */
- case class EnclMethodEntry(owner: String, name: String, methodType: asm.Type)
-
- /**
- * @return null if the current class is not internal to a method
- *
- * Quoting from JVMS 4.7.7 The EnclosingMethod Attribute
- * A class must have an EnclosingMethod attribute if and only if it is a local class or an anonymous class.
- * A class may have no more than one EnclosingMethod attribute.
- *
- */
- private def getEnclosingMethodAttribute(): EnclMethodEntry = { // JVMS 4.7.7
- var res: EnclMethodEntry = null
- val clazz = clasz.symbol
- val sym = clazz.originalEnclosingMethod
- if (sym.isMethod) {
- debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass))
- res = EnclMethodEntry(javaName(sym.enclClass), javaName(sym), javaType(sym))
- } else if (clazz.isAnonymousClass) {
- val enclClass = clazz.rawowner
- assert(enclClass.isClass, enclClass)
- val sym = enclClass.primaryConstructor
- if (sym == NoSymbol) {
- log("Ran out of room looking for an enclosing method for %s: no constructor here.".format(enclClass))
- } else {
- debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass))
- res = EnclMethodEntry(javaName(enclClass), javaName(sym), javaType(sym))
- }
- }
-
- res
- }
-
def genField(f: IField) {
debuglog("Adding field: " + f.symbol.fullName)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index a401de05e5..0a7c894a69 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -46,6 +46,9 @@ import scala.tools.asm
abstract class GenBCode extends BCodeSyncAndTry {
import global._
+ import bTypes._
+ import coreBTypes._
+
val phaseName = "jvm"
override def newPhase(prev: Phase) = new BCodePhase(prev)
@@ -130,7 +133,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
return
}
else {
- try { visit(item) }
+ try { withCurrentUnit(item.cunit)(visit(item)) }
catch {
case ex: Throwable =>
ex.printStackTrace()
@@ -165,7 +168,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
// -------------- mirror class, if needed --------------
val mirrorC =
- if (isStaticModule(claszSymbol) && isTopLevelModule(claszSymbol)) {
+ if (isTopLevelModuleClass(claszSymbol)) {
if (claszSymbol.companionClass == NoSymbol) {
mirrorCodeGen.genMirrorClass(claszSymbol, cunit)
} else {
@@ -271,8 +274,8 @@ abstract class GenBCode extends BCodeSyncAndTry {
override def run() {
arrivalPos = 0 // just in case
- scalaPrimitives.init
- initBCodeTypes()
+ scalaPrimitives.init()
+ bTypes.intializeCoreBTypes()
// initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated.
bytecodeWriter = initBytecodeWriter(cleanup.getEntryPoints)
@@ -297,9 +300,6 @@ abstract class GenBCode extends BCodeSyncAndTry {
* (2) if requested, check-java-signatures, over and beyond the syntactic checks in `getGenericSignature()`
*
*/
-
- // clearing maps
- clearBCodeTypes()
}
/*
@@ -385,3 +385,10 @@ abstract class GenBCode extends BCodeSyncAndTry {
} // end of class BCodePhase
} // end of class GenBCode
+
+object GenBCode {
+ def mkFlags(args: Int*) = args.foldLeft(0)(_ | _)
+
+ final val PublicStatic = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC
+ final val PublicStaticFinal = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL
+}
diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
index bddcf6567c..ac86dfd665 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
@@ -861,7 +861,7 @@ trait JavaScanners extends ast.parser.ScannersCommon {
in = new JavaCharArrayReader(unit.source.content, !settings.nouescape.value, syntaxError)
init()
def error (pos: Int, msg: String) = reporter.error(pos, msg)
- def incompleteInputError(pos: Int, msg: String) = currentRun.reporting.incompleteInputError(pos, msg)
+ def incompleteInputError(pos: Int, msg: String) = currentRun.parsing.incompleteInputError(pos, msg)
def deprecationWarning(pos: Int, msg: String) = currentRun.reporting.deprecationWarning(pos, msg)
implicit def g2p(pos: Int): Position = Position.offset(unit.source, pos)
}
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
index 12f9aeba27..6e3d013e52 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala
@@ -33,7 +33,7 @@ trait Plugins { global: Global =>
}
val maybes = Plugin.loadAllFrom(paths, dirs, settings.disable.value)
val (goods, errors) = maybes partition (_.isSuccess)
- // Explicit parameterization of recover to suppress -Xlint warning about inferred Any
+ // Explicit parameterization of recover to avoid -Xlint warning about inferred Any
errors foreach (_.recover[Any] {
// legacy behavior ignores altogether, so at least warn devs
case e: MissingPluginException => if (global.isDeveloper) warning(e.getMessage)
diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
index 6c592ead0d..5e4914fa83 100644
--- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
@@ -30,6 +30,7 @@ abstract class AbstractReporter extends Reporter {
private def isVerbose = settings.verbose.value
private def noWarnings = settings.nowarnings.value
private def isPromptSet = settings.prompt.value
+ private def isDebug = settings.debug
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean) {
if (severity == INFO) {
@@ -46,7 +47,7 @@ abstract class AbstractReporter extends Reporter {
severity.count += 1
display(pos, msg, severity)
}
- else if (settings.debug) {
+ else if (isDebug) {
severity.count += 1
display(pos, "[ suppressed ] " + msg, severity)
}
@@ -57,6 +58,7 @@ abstract class AbstractReporter extends Reporter {
}
}
+
/** Logs a position and returns true if it was already logged.
* @note Two positions are considered identical for logging if they have the same point.
*/
diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
index 5b576a547d..3d688efae1 100644
--- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
@@ -13,8 +13,8 @@ import scala.reflect.internal.util._
* This describes the internal interface for issuing information, warnings and errors.
* The only abstract method in this class must be info0.
*
- * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter
- * This interface should be considered private to the compiler.
+ * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter,
+ * and remove this class.
*/
abstract class Reporter extends scala.reflect.internal.Reporter {
/** Informational messages. If `!force`, they may be suppressed. */
diff --git a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala
index 38a7525862..d0b8fd70ed 100644
--- a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala
@@ -29,7 +29,7 @@ trait AbsScalaSettings {
def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): ChoiceSetting
def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]): IntSetting
def MultiStringSetting(name: String, helpArg: String, descr: String): MultiStringSetting
- def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String]): MultiChoiceSetting
+ def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: Option[() => Unit])(helper: MultiChoiceSetting => String): MultiChoiceSetting
def OutputSetting(outputDirs: OutputDirs, default: String): OutputSetting
def PathSetting(name: String, descr: String, default: String): PathSetting
def PhasesSetting(name: String, descr: String, default: String): PhasesSetting
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index 54e444decf..f26192f88a 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -211,11 +211,10 @@ class MutableSettings(val errorFn: String => Unit)
add(new ChoiceSetting(name, helpArg, descr, choices, default))
def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]) = add(new IntSetting(name, descr, default, range, parser))
def MultiStringSetting(name: String, arg: String, descr: String) = add(new MultiStringSetting(name, arg, descr))
- def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String]): MultiChoiceSetting = {
- val fullChoix = choices.mkString(": ", ",", ".")
- val fullDescr = s"$descr$fullChoix"
- add(new MultiChoiceSetting(name, helpArg, fullDescr, choices))
- }
+ def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: Option[() => Unit] = None)(
+ helper: MultiChoiceSetting => String = _ => choices.mkString(f"$descr:%n", f"%n ", f"%n")
+ ) =
+ add(new MultiChoiceSetting(name, helpArg, descr, choices, default, helper))
def OutputSetting(outputDirs: OutputDirs, default: String) = add(new OutputSetting(outputDirs, default))
def PhasesSetting(name: String, descr: String, default: String = "") = add(new PhasesSetting(name, descr, default))
def StringSetting(name: String, arg: String, descr: String, default: String) = add(new StringSetting(name, arg, descr, default))
@@ -444,7 +443,7 @@ class MutableSettings(val errorFn: String => Unit)
value = s.equalsIgnoreCase("true")
}
override def tryToSetColon(args: List[String]) = args match {
- case Nil => tryToSet(Nil)
+ case Nil => tryToSet(Nil)
case List(x) =>
if (x.equalsIgnoreCase("true")) {
value = true
@@ -452,7 +451,8 @@ class MutableSettings(val errorFn: String => Unit)
} else if (x.equalsIgnoreCase("false")) {
value = false
Some(Nil)
- } else errorAndValue("'" + x + "' is not a valid choice for '" + name + "'", None)
+ } else errorAndValue(s"'$x' is not a valid choice for '$name'", None)
+ case _ => errorAndValue(s"'$name' accepts only one boolean value", None)
}
}
@@ -553,12 +553,58 @@ class MutableSettings(val errorFn: String => Unit)
}
}
+ /** A setting that receives any combination of enumerated values,
+ * including "_" to mean all values and "help" for verbose info.
+ * In non-colonated mode, stops consuming args at the first
+ * non-value, instead of at the next option, as for a multi-string.
+ */
class MultiChoiceSetting private[nsc](
name: String,
arg: String,
descr: String,
- override val choices: List[String])
- extends MultiStringSetting(name, arg, descr)
+ override val choices: List[String],
+ val default: Option[() => Unit],
+ helper: MultiChoiceSetting => String
+ ) extends MultiStringSetting(name, s"_,$arg,-$arg", s"$descr: `_' for all, `$name:help' to list") {
+
+ private def badChoice(s: String, n: String) = errorFn(s"'$s' is not a valid choice for '$name'")
+ private def choosing = choices.nonEmpty
+ private def isChoice(s: String) = (s == "_") || (choices contains (s stripPrefix "-"))
+
+ private var sawHelp = false
+ private var sawAll = false
+ private val adderAll = () => sawAll = true
+ private val noargs = () => errorFn(s"'$name' requires an option. See '$name:help'.")
+
+ override protected def tts(args: List[String], halting: Boolean) = {
+ val added = collection.mutable.ListBuffer.empty[String]
+ def tryArg(arg: String) = arg match {
+ case "_" if choosing => addAll()
+ case "help" if choosing => sawHelp = true
+ case s if !choosing || isChoice(s) => added += s
+ case s => badChoice(s, name)
+ }
+ def stoppingAt(arg: String) = (arg startsWith "-") || (choosing && !isChoice(arg))
+ def loop(args: List[String]): List[String] = args match {
+ case arg :: _ if halting && stoppingAt(arg) => args
+ case arg :: rest => tryArg(arg) ; loop(rest)
+ case Nil => Nil
+ }
+ val rest = loop(args)
+ if (rest.size == args.size)
+ (default getOrElse noargs)() // if no arg consumed, trigger default action or error
+ else
+ value ++= added.toList // update all new settings at once
+ Some(rest)
+ }
+
+ def isHelping: Boolean = sawHelp
+ def help: String = helper(this)
+ def addAll(): Unit = (default getOrElse adderAll)()
+
+ // the semantics is: s is enabled, i.e., either s or (_ but not -s)
+ override def contains(s: String) = isChoice(s) && (value contains s) || (sawAll && !(value contains s"-$s"))
+ }
/** A setting that accumulates all strings supplied to it,
* until it encounters one starting with a '-'.
@@ -570,24 +616,23 @@ class MutableSettings(val errorFn: String => Unit)
extends Setting(name, descr) with Clearable {
type T = List[String]
protected var v: T = Nil
- def appendToValue(str: String) { value ++= List(str) }
- def badChoice(s: String, n: String) = errorFn(s"'$s' is not a valid choice for '$name'")
-
- def tryToSet(args: List[String]) = {
- val (strings, rest) = args span (x => !x.startsWith("-"))
- strings foreach {
- case "_" if choices.nonEmpty => choices foreach appendToValue
- case s if choices.isEmpty || (choices contains s) => appendToValue(s)
- case s => badChoice(s, name)
+ def appendToValue(str: String) = value ++= List(str)
+
+ // try to set. halting means halt at first non-arg
+ protected def tts(args: List[String], halting: Boolean) = {
+ def loop(args: List[String]): List[String] = args match {
+ case arg :: rest => if (halting && (arg startsWith "-")) args else { appendToValue(arg) ; loop(rest) }
+ case Nil => Nil
}
- Some(rest)
+ Some(loop(args))
}
- override def tryToSetColon(args: List[String]) = tryToSet(args)
+ def tryToSet(args: List[String]) = tts(args, halting = true)
+ override def tryToSetColon(args: List[String]) = tts(args, halting = false)
override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) // used from ide
- def clear(): Unit = (v = Nil)
- def unparse: List[String] = value map (name + ":" + _)
- def contains(s: String) = value contains s
+ def clear(): Unit = (v = Nil)
+ def unparse: List[String] = value map (name + ":" + _)
+ def contains(s: String) = value contains s
withHelpSyntax(name + ":<" + arg + ">")
}
@@ -606,10 +651,8 @@ class MutableSettings(val errorFn: String => Unit)
protected var v: T = default
def indexOfChoice: Int = choices indexOf value
- private def usageErrorMessage = {
- "Usage: %s:<%s>\n where <%s> choices are %s (default: %s)\n".format(
- name, helpArg, helpArg, choices mkString ", ", default)
- }
+ private def usageErrorMessage = f"Usage: $name:<$helpArg>%n where <$helpArg> choices are ${choices mkString ", "} (default: $default)%n"
+
def tryToSet(args: List[String]) = errorAndValue(usageErrorMessage, None)
override def tryToSetColon(args: List[String]) = args match {
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index d22dcacad6..8e69598614 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -44,8 +44,11 @@ trait ScalaSettings extends AbsScalaSettings
/** If any of these settings is enabled, the compiler should print a message and exit. */
def infoSettings = List[Setting](version, help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph)
+ /** Any -multichoice:help? Nicer if any option could report that it had help to offer. */
+ private def multihelp = allSettings exists { case s: MultiChoiceSetting => s.isHelping case _ => false }
+
/** Is an info setting set? */
- def isInfo = infoSettings exists (_.isSetByUser)
+ def isInfo = (infoSettings exists (_.isSetByUser)) || multihelp
/** Disable a setting */
def disable(s: Setting) = allSettings -= s
@@ -67,8 +70,27 @@ trait ScalaSettings extends AbsScalaSettings
// Would be nice to build this dynamically from scala.languageFeature.
// The two requirements: delay error checking until you have symbols, and let compiler command build option-specific help.
val language = {
- val features = List("dynamics", "postfixOps", "reflectiveCalls", "implicitConversions", "higherKinds", "existentials", "experimental.macros")
- MultiChoiceSetting("-language", "feature", "Enable one or more language features", features)
+ val features = List(
+ "dynamics" -> "Allow direct or indirect subclasses of scala.Dynamic",
+ "postfixOps" -> "Allow postfix operator notation, such as `1 to 10 toList'",
+ "reflectiveCalls" -> "Allow reflective access to members of structural types",
+ "implicitConversions" -> "Allow definition of implicit functions called views",
+ "higherKinds" -> "Allow higher-kinded types", // "Ask Adriaan, but if you have to ask..."
+ "existentials" -> "Existential types (besides wildcard types) can be written and inferred",
+ "experimental.macros" -> "Allow macro defintion (besides implementation and application)"
+ )
+ val description = "Enable or disable language features"
+ MultiChoiceSetting(
+ name = "-language",
+ helpArg = "feature",
+ descr = description,
+ choices = features map (_._1)
+ ) { s =>
+ val helpline: ((String, String)) => String = {
+ case (name, text) => f" $name%-25s $text%n"
+ }
+ features map helpline mkString (f"$description:%n", "", f"%n")
+ }
}
/*
@@ -185,7 +207,6 @@ trait ScalaSettings extends AbsScalaSettings
val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.")
val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.").withDeprecationMessage(removalIn212)
val inferByName = BooleanSetting ("-Yinfer-by-name", "Allow inference of by-name types. This is a temporary option to ease transition. See SI-7899.").withDeprecationMessage(removalIn212)
- val Yinvalidate = StringSetting ("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "")
val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes")
val YdisableUnreachablePrevention = BooleanSetting("-Ydisable-unreachable-prevention", "Disable the prevention of unreachable blocks in code generation.")
val YnoLoadImplClass = BooleanSetting ("-Yno-load-impl-class", "Do not load $class.class files.")
diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala
index 37dfafb01c..d42c0dd730 100644
--- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala
@@ -39,7 +39,7 @@ trait StandardScalaSettings {
val optimise: BooleanSetting // depends on post hook which mutates other settings
val print = BooleanSetting ("-print", "Print program with Scala-specific features removed.")
val target = ChoiceSetting ("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.",
- List("jvm-1.5", "jvm-1.6", "jvm-1.7"), "jvm-1.6")
+ List("jvm-1.5", "jvm-1.6", "jvm-1.7", "jvm-1.8"), "jvm-1.6")
val unchecked = BooleanSetting ("-unchecked", "Enable additional warnings where generated code depends on assumptions.")
val uniqid = BooleanSetting ("-uniqid", "Uniquely tag all identifiers in debugging output.")
val usejavacp = BooleanSetting ("-usejavacp", "Utilize the java.class.path in classpath resolution.")
diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala
index 1509ad13b8..4c37633301 100644
--- a/src/compiler/scala/tools/nsc/settings/Warnings.scala
+++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala
@@ -17,12 +17,15 @@ trait Warnings {
// Warning semantics.
val fatalWarnings = BooleanSetting("-Xfatal-warnings", "Fail the compilation if there are any warnings.")
- // These warnings are all so noisy as to be useless in their
+ // These additional warnings are all so noisy as to be useless in their
// present form, but have the potential to offer useful info.
protected def allWarnings = lintWarnings ++ List(
warnDeadCode,
warnValueDiscard,
- warnNumericWiden
+ warnNumericWiden,
+ warnUnused, // SI-7712, SI-7707 warnUnused not quite ready for prime-time
+ warnUnusedImport, // currently considered too noisy for general use
+ warnValueOverrides // currently turned off as experimental
)
// These warnings should be pretty quiet unless you're doing
// something inadvisable.
@@ -31,27 +34,130 @@ trait Warnings {
warnNullaryOverride,
warnNullaryUnit,
warnAdaptedArgs,
- warnInferAny
- // warnUnused SI-7712, SI-7707 warnUnused not quite ready for prime-time
- // warnUnusedImport currently considered too noisy for general use
+ warnInferAny,
+ warnMissingInterpolator,
+ warnDocDetached,
+ warnPrivateShadow,
+ warnTypeParameterShadow,
+ warnPolyImplicitOverload,
+ warnOptionImplicit,
+ warnDelayedInit,
+ warnByNameRightAssociative,
+ warnPackageObjectClasses,
+ warnUnsoundMatch
)
- private lazy val warnSelectNullable = BooleanSetting("-Xcheck-null", "This option is obsolete and does nothing.")
+ // Individual warnings. They can be set with -Ywarn.
+ private def nonlintflag(name: String, text: String): BooleanSetting = BooleanSetting(name, text)
+
+ val warnDeadCode = nonlintflag("-Ywarn-dead-code",
+ "Warn when dead code is identified.")
+ val warnValueDiscard = nonlintflag("-Ywarn-value-discard",
+ "Warn when non-Unit expression results are unused.")
+ val warnNumericWiden = nonlintflag("-Ywarn-numeric-widen",
+ "Warn when numerics are widened.")
+ val warnUnused = nonlintflag("-Ywarn-unused",
+ "Warn when local and private vals, vars, defs, and types are are unused")
+ val warnUnusedImport = nonlintflag("-Ywarn-unused-import",
+ "Warn when imports are unused")
+
+ // Lint warnings that have no -Y avatar, created with new instead of the autoregistering factory method.
+ // They evaluate true if set to true or else are unset but -Xlint is true
+ private def lintflag(name: String, text: String): BooleanSetting =
+ new BooleanSetting(name, text) {
+ override def value = if (isSetByUser) super.value else xlint
+ }
+
+ val warnAdaptedArgs = lintflag("adapted-args",
+ "Warn if an argument list is modified to match the receiver.")
+ val warnNullaryUnit = lintflag("nullary-unit",
+ "Warn when nullary methods return Unit.")
+ val warnInaccessible = lintflag("inaccessible",
+ "Warn about inaccessible types in method signatures.")
+ val warnNullaryOverride = lintflag("nullary-override",
+ "Warn when non-nullary `def f()' overrides nullary `def f'.")
+ val warnInferAny = lintflag("infer-any",
+ "Warn when a type argument is inferred to be `Any`.")
+ val warnMissingInterpolator = lintflag("missing-interpolator",
+ "A string literal appears to be missing an interpolator id.")
+ val warnDocDetached = lintflag("doc-detached",
+ "A ScalaDoc comment appears to be detached from its element.")
+ val warnPrivateShadow = lintflag("private-shadow",
+ "A private field (or class parameter) shadows a superclass field.")
+ val warnTypeParameterShadow = lintflag("type-parameter-shadow",
+ "A local type parameter shadows a type already in scope.")
+ val warnPolyImplicitOverload = lintflag("poly-implicit-overload",
+ "Parameterized overloaded implicit methods are not visible as view bounds")
+ val warnOptionImplicit = lintflag("option-implicit",
+ "Option.apply used implicit view.")
+ val warnDelayedInit = lintflag("delayedinit-select",
+ "Selecting member of DelayedInit")
+ val warnByNameRightAssociative = lintflag("by-name-right-associative",
+ "By-name parameter of right associative operator")
+ val warnPackageObjectClasses = lintflag("package-object-classes",
+ "Class or object defined in package object")
+ val warnUnsoundMatch = lintflag("unsound-match",
+ "Pattern match may not be typesafe")
- // Individual warnings.
- val warnAdaptedArgs = BooleanSetting ("-Ywarn-adapted-args", "Warn if an argument list is modified to match the receiver.")
- val warnDeadCode = BooleanSetting ("-Ywarn-dead-code", "Warn when dead code is identified.")
- val warnValueDiscard = BooleanSetting ("-Ywarn-value-discard", "Warn when non-Unit expression results are unused.")
- val warnNumericWiden = BooleanSetting ("-Ywarn-numeric-widen", "Warn when numerics are widened.")
- val warnNullaryUnit = BooleanSetting ("-Ywarn-nullary-unit", "Warn when nullary methods return Unit.")
- val warnInaccessible = BooleanSetting ("-Ywarn-inaccessible", "Warn about inaccessible types in method signatures.")
- val warnNullaryOverride = BooleanSetting ("-Ywarn-nullary-override", "Warn when non-nullary overrides nullary, e.g. `def foo()` over `def foo`.")
- val warnInferAny = BooleanSetting ("-Ywarn-infer-any", "Warn when a type argument is inferred to be `Any`.")
- val warnUnused = BooleanSetting ("-Ywarn-unused", "Warn when local and private vals, vars, defs, and types are are unused")
- val warnUnusedImport = BooleanSetting ("-Ywarn-unused-import", "Warn when imports are unused")
-
- // Warning groups.
- val lint = BooleanSetting("-Xlint", "Enable recommended additional warnings.") enablingIfNotSetByUser lintWarnings
+ // Experimental lint warnings that are turned off, but which could be turned on programmatically.
+ // These warnings are said to blind those who dare enable them.
+ // They are not activated by -Xlint and can't be enabled on the command line.
+ val warnValueOverrides = {
+ val flag = lintflag("value-overrides", "Generated value class method overrides an implementation")
+ flag.value = false
+ flag
+ }
+
+ // The Xlint warning group.
+ private val xlint = new BooleanSetting("-Zunused", "True if -Xlint or -Xlint:_")
+ // On -Xlint or -Xlint:_, set xlint, otherwise set the lint warning unless already set true
+ val lint = {
+ val description = "Enable or disable specific warnings"
+ val choices = (lintWarnings map (_.name)).sorted
+ MultiChoiceSetting(
+ name = "-Xlint",
+ helpArg = "warning",
+ descr = description,
+ choices = choices,
+ default = Some(() => xlint.value = true)
+ ) { s =>
+ def helpline(n: String) = lintWarnings.find(_.name == n).map(w => f" ${w.name}%-25s ${w.helpDescription}%n")
+ choices flatMap (helpline(_)) mkString (f"$description:%n", "", f"%n")
+ } withPostSetHook { x =>
+ val Neg = "-"
+ def setPolitely(b: BooleanSetting, v: Boolean) = if (!b.isSetByUser || !b) b.value = v
+ def set(w: String, v: Boolean) = lintWarnings find (_.name == w) foreach (setPolitely(_, v))
+ def propagate(ss: List[String]): Unit = ss match {
+ case w :: rest => if (w startsWith Neg) set(w stripPrefix Neg, false) else set(w, true) ; propagate(rest)
+ case Nil => ()
+ }
+ propagate(x.value)
+ }
+ }
+
+ // Lint warnings that are currently -Y, but deprecated in that usage
+ @deprecated("Use warnAdaptedArgs", since="2.11.2")
+ val YwarnAdaptedArgs = BooleanSetting("-Ywarn-adapted-args",
+ "Warn if an argument list is modified to match the receiver.") enabling List(warnAdaptedArgs)
+ //withDeprecationMessage "Enable -Xlint:adapted-args"
+ @deprecated("Use warnNullaryUnit", since="2.11.2")
+ val YwarnNullaryUnit = BooleanSetting("-Ywarn-nullary-unit",
+ "Warn when nullary methods return Unit.") enabling List(warnNullaryUnit)
+ //withDeprecationMessage "Enable -Xlint:nullary-unit"
+ @deprecated("Use warnInaccessible", since="2.11.2")
+ val YwarnInaccessible = BooleanSetting("-Ywarn-inaccessible",
+ "Warn about inaccessible types in method signatures.") enabling List(warnInaccessible)
+ //withDeprecationMessage "Enable -Xlint:inaccessible"
+ @deprecated("Use warnNullaryOverride", since="2.11.2")
+ val YwarnNullaryOverride = BooleanSetting("-Ywarn-nullary-override",
+ "Warn when non-nullary `def f()' overrides nullary `def f'.") enabling List(warnNullaryOverride)
+ //withDeprecationMessage "Enable -Xlint:nullary-override"
+ @deprecated("Use warnInferAny", since="2.11.2")
+ val YwarnInferAny = BooleanSetting("-Ywarn-infer-any",
+ "Warn when a type argument is inferred to be `Any`.") enabling List(warnInferAny)
+ //withDeprecationMessage "Enable -Xlint:infer-any"
+
+ private lazy val warnSelectNullable = BooleanSetting("-Xcheck-null", "This option is obsolete and does nothing.")
// Backward compatibility.
@deprecated("Use fatalWarnings", "2.11.0") def Xwarnfatal = fatalWarnings // used by sbt
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
index 447fa66ae4..82c2a4d6ed 100644
--- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
+++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala
@@ -240,6 +240,12 @@ abstract class SymbolLoaders {
}
}
+ private def phaseBeforeRefchecks: Phase = {
+ var resPhase = phase
+ while (resPhase.refChecked) resPhase = resPhase.prev
+ resPhase
+ }
+
/**
* Load contents of a package
*/
@@ -248,19 +254,24 @@ abstract class SymbolLoaders {
protected def doComplete(root: Symbol) {
assert(root.isPackageClass, root)
- root.setInfo(new PackageClassInfoType(newScope, root))
-
- if (!root.isRoot) {
- for (classRep <- classpath.classes) {
- initializeFromClassPath(root, classRep)
- }
- }
- if (!root.isEmptyPackageClass) {
- for (pkg <- classpath.packages) {
- enterPackage(root, pkg.name, new PackageLoader(pkg))
+ // Time travel to a phase before refchecks avoids an initialization issue. `openPackageModule`
+ // creates a module symbol and invokes invokes `companionModule` while the `infos` field is
+ // still null. This calls `isModuleNotMethod`, which forces the `info` if run after refchecks.
+ enteringPhase(phaseBeforeRefchecks) {
+ root.setInfo(new PackageClassInfoType(newScope, root))
+
+ if (!root.isRoot) {
+ for (classRep <- classpath.classes) {
+ initializeFromClassPath(root, classRep)
+ }
}
+ if (!root.isEmptyPackageClass) {
+ for (pkg <- classpath.packages) {
+ enterPackage(root, pkg.name, new PackageLoader(pkg))
+ }
- openPackageModule(root)
+ openPackageModule(root)
+ }
}
}
}
@@ -290,7 +301,13 @@ abstract class SymbolLoaders {
protected def doComplete(root: Symbol) {
val start = if (Statistics.canEnable) Statistics.startTimer(classReadNanos) else null
- classfileParser.parse(classfile, root)
+
+ // Running the classfile parser after refchecks can lead to "illegal class file dependency"
+ // errors. More concretely, the classfile parser calls "sym.companionModule", which calls
+ // "isModuleNotMethod" on the companion. After refchecks, this method forces the info, which
+ // may run the classfile parser. This produces the error.
+ enteringPhase(phaseBeforeRefchecks)(classfileParser.parse(classfile, root))
+
if (root.associatedFile eq NoAbstractFile) {
root match {
// In fact, the ModuleSymbol forwards its setter to the module class
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index 1f8ab7e887..9cc8d779a0 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -37,6 +37,15 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
/** the following two members override abstract members in Transform */
val phaseName: String = "delambdafy"
+ override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = {
+ if (settings.Ydelambdafy.value == "method") new Phase(prev)
+ else new SkipPhase(prev)
+ }
+
+ class SkipPhase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) {
+ def apply(unit: global.CompilationUnit): Unit = ()
+ }
+
protected def newTransformer(unit: CompilationUnit): Transformer =
new DelambdafyTransformer(unit)
@@ -252,39 +261,39 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
captureProxies2 += ((capture, sym))
}
- // the Optional proxy that will hold a reference to the 'this'
- // object used by the lambda, if any. NoSymbol if there is no this proxy
- val thisProxy = {
- val target = targetMethod(originalFunction)
- if (thisReferringMethods contains target) {
- val sym = anonClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC)
- sym.info = oldClass.tpe
- sym
- } else NoSymbol
- }
+ // the Optional proxy that will hold a reference to the 'this'
+ // object used by the lambda, if any. NoSymbol if there is no this proxy
+ val thisProxy = {
+ val target = targetMethod(originalFunction)
+ if (thisReferringMethods contains target) {
+ val sym = anonClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC)
+ sym.info = oldClass.tpe
+ sym
+ } else NoSymbol
+ }
- val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, anonClass, originalFunction.symbol.pos, thisProxy)
+ val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, anonClass, originalFunction.symbol.pos, thisProxy)
- val accessorMethod = createAccessorMethod(thisProxy, originalFunction)
+ val accessorMethod = createAccessorMethod(thisProxy, originalFunction)
- val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function]
+ val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function]
- val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member =>
- anonClass.info.decls enter member
- ValDef(member, gen.mkZero(member.tpe)) setPos decapturedFunction.pos
- }
+ val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member =>
+ anonClass.info.decls enter member
+ ValDef(member, gen.mkZero(member.tpe)) setPos decapturedFunction.pos
+ }
- // constructor
- val constr = createConstructor(anonClass, members)
+ // constructor
+ val constr = createConstructor(anonClass, members)
- // apply method with same arguments and return type as original lambda.
- val applyMethodDef = createApplyMethod(anonClass, decapturedFunction, accessorMethod, thisProxy)
+ // apply method with same arguments and return type as original lambda.
+ val applyMethodDef = createApplyMethod(anonClass, decapturedFunction, accessorMethod, thisProxy)
- val bridgeMethod = createBridgeMethod(anonClass, originalFunction, applyMethodDef)
+ val bridgeMethod = createBridgeMethod(anonClass, originalFunction, applyMethodDef)
- def fulldef(sym: Symbol) =
- if (sym == NoSymbol) sym.toString
- else s"$sym: ${sym.tpe} in ${sym.owner}"
+ def fulldef(sym: Symbol) =
+ if (sym == NoSymbol) sym.toString
+ else s"$sym: ${sym.tpe} in ${sym.owner}"
bridgeMethod foreach (bm =>
// TODO SI-6260 maybe just create the apply method with the signature (Object => Object) in all cases
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index ec4deb6be0..3d8b2f02f3 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -468,8 +468,12 @@ abstract class Erasure extends AddInterfaces
if (!bridgeNeeded)
return
- val newFlags = (member.flags | BRIDGE | ARTIFACT) & ~(ACCESSOR | DEFERRED | LAZY | lateDEFERRED)
- val bridge = other.cloneSymbolImpl(root, newFlags) setPos root.pos
+ var newFlags = (member.flags | BRIDGE | ARTIFACT) & ~(ACCESSOR | DEFERRED | LAZY | lateDEFERRED)
+ // If `member` is a ModuleSymbol, the bridge should not also be a ModuleSymbol. Otherwise we
+ // end up with two module symbols with the same name in the same scope, which is surprising
+ // when implementing later phases.
+ if (member.isModule) newFlags = (newFlags | METHOD) & ~(MODULE | lateMETHOD | STABLE)
+ val bridge = other.cloneSymbolImpl(root, newFlags) setPos root.pos
debuglog("generating bridge from %s (%s): %s to %s: %s".format(
other, flagsToString(newFlags),
@@ -1133,7 +1137,7 @@ abstract class Erasure extends AddInterfaces
val tree2 = mixinTransformer.transform(tree1)
// debuglog("tree after addinterfaces: \n" + tree2)
- newTyper(rootContext(unit, tree, erasedTypes = true)).typed(tree2)
+ newTyper(rootContextPostTyper(unit, tree)).typed(tree2)
}
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala
index c3fbfae322..fa53ef48b5 100644
--- a/src/compiler/scala/tools/nsc/transform/Flatten.scala
+++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala
@@ -76,8 +76,17 @@ abstract class Flatten extends InfoTransform {
for (sym <- decls) {
if (sym.isTerm && !sym.isStaticModule) {
decls1 enter sym
- if (sym.isModule)
+ if (sym.isModule) {
+ // Nested, non-static moduls are transformed to methods.
+ assert(sym.isMethod, s"Non-static $sym should have the lateMETHOD flag from RefChecks")
+ // Note that module classes are not entered into the 'decls' of the ClassInfoType
+ // of the outer class, only the module symbols are. So the current loop does
+ // not visit module classes. Therefore we set the LIFTED flag here for module
+ // classes.
+ // TODO: should we also set the LIFTED flag for static, nested module classes?
+ // currently they don't get the flag, even though they are lifted to the package
sym.moduleClass setFlag LIFTED
+ }
} else if (sym.isClass)
liftSymbol(sym)
}
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index f85d8222f0..d69c9d9a65 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -449,6 +449,8 @@ abstract class LambdaLift extends InfoTransform {
if (sym.isClass) sym.owner = sym.owner.toInterface
if (sym.isMethod) sym setFlag LIFTED
liftedDefs(sym.owner) ::= tree
+ // TODO: this modifies the ClassInfotype of the enclosing class, which is associated with another phase (explicitouter).
+ // This breaks type history: in a phase travel to before lambda lift, the ClassInfoType will contain lifted classes.
sym.owner.info.decls enterUnique sym
debuglog("lifted: " + sym + " from " + oldOwner + " to " + sym.owner)
EmptyTree
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
index b71d14a04f..38671ebaae 100644
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -192,13 +192,15 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
def mkSlowPathDef(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree],
stats: List[Tree], retVal: Tree): Tree = {
+ // Q: is there a reason to first set owner to `clazz` (by using clazz.newMethod), and then
+ // changing it to lzyVal.owner very soon after? Could we just do lzyVal.owner.newMethod?
val defSym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE)
defSym setInfo MethodType(List(), lzyVal.tpe.resultType)
defSym.owner = lzyVal.owner
debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal")
if (bitmaps.contains(lzyVal))
bitmaps(lzyVal).map(_.owner = defSym)
- val rhs: Tree = (gen.mkSynchronizedCheck(clazz, cond, syncBody, stats)).changeOwner(currentOwner -> defSym)
+ val rhs: Tree = gen.mkSynchronizedCheck(clazz, cond, syncBody, stats).changeOwner(currentOwner -> defSym)
DefDef(defSym, addBitmapDefs(lzyVal, BLOCK(rhs, retVal)))
}
diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
index 3feadcd9b2..dc3313e2e4 100644
--- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
+++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
@@ -17,9 +17,9 @@ trait TypingTransformers {
abstract class TypingTransformer(unit: CompilationUnit) extends Transformer {
var localTyper: analyzer.Typer =
if (phase.erasedTypes)
- erasure.newTyper(erasure.rootContext(unit, EmptyTree, erasedTypes = true)).asInstanceOf[analyzer.Typer]
- else
- analyzer.newTyper(analyzer.rootContext(unit, EmptyTree, true))
+ erasure.newTyper(erasure.rootContextPostTyper(unit, EmptyTree)).asInstanceOf[analyzer.Typer]
+ else // TODO: AM: should some phases use a regular rootContext instead of a post-typer one??
+ analyzer.newTyper(analyzer.rootContextPostTyper(unit, EmptyTree))
protected var curTree: Tree = _
override final def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans)
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 2209aac00f..9b85f1b36a 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -69,7 +69,6 @@ abstract class UnCurry extends InfoTransform
private val byNameArgs = mutable.HashSet[Tree]()
private val noApply = mutable.HashSet[Tree]()
private val newMembers = mutable.Map[Symbol, mutable.Buffer[Tree]]()
- private val repeatedParams = mutable.Map[Symbol, List[ValDef]]()
/** Add a new synthetic member for `currentOwner` */
private def addNewMember(t: Tree): Unit =
@@ -428,7 +427,7 @@ abstract class UnCurry extends InfoTransform
treeCopy.ValDef(p, p.mods, p.name, p.tpt, EmptyTree)
})
- if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd)
+ if (dd.symbol hasAnnotation VarargsClass) validateVarargs(dd)
withNeedLift(needLift = false) {
if (dd.symbol.isClassConstructor) {
@@ -699,19 +698,12 @@ abstract class UnCurry extends InfoTransform
}
}
-
- /* Analyzes repeated params if method is annotated as `varargs`.
- * If the repeated params exist, it saves them into the `repeatedParams` map,
- * which is used later.
- */
- private def saveRepeatedParams(dd: DefDef): Unit =
+ private def validateVarargs(dd: DefDef): Unit =
if (dd.symbol.isConstructor)
reporter.error(dd.symbol.pos, "A constructor cannot be annotated with a `varargs` annotation.")
- else treeInfo.repeatedParams(dd) match {
- case Nil =>
- reporter.error(dd.symbol.pos, "A method without repeated parameters cannot be annotated with the `varargs` annotation.")
- case reps =>
- repeatedParams(dd.symbol) = reps
+ else {
+ val hasRepeated = mexists(dd.symbol.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))
+ if (!hasRepeated) reporter.error(dd.symbol.pos, "A method without repeated parameters cannot be annotated with the `varargs` annotation.")
}
/* Called during post transform, after the method argument lists have been flattened.
@@ -719,7 +711,7 @@ abstract class UnCurry extends InfoTransform
* varargs forwarder.
*/
private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef): DefDef = {
- if (!dd.symbol.hasAnnotation(VarargsClass) || !repeatedParams.contains(dd.symbol))
+ if (!dd.symbol.hasAnnotation(VarargsClass) || !enteringUncurry(mexists(dd.symbol.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))))
return flatdd
def toArrayType(tp: Type): Type = {
@@ -735,19 +727,18 @@ abstract class UnCurry extends InfoTransform
)
}
- val reps = repeatedParams(dd.symbol)
- val rpsymbols = reps.map(_.symbol).toSet
val theTyper = typer.atOwner(dd, currentClass)
- val flatparams = flatdd.vparamss.head
+ val flatparams = flatdd.symbol.paramss.head
+ val isRepeated = enteringUncurry(dd.symbol.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe)))
// create the type
- val forwformals = flatparams map {
- case p if rpsymbols(p.symbol) => toArrayType(p.symbol.tpe)
- case p => p.symbol.tpe
+ val forwformals = map2(flatparams, isRepeated) {
+ case (p, true) => toArrayType(p.tpe)
+ case (p, false)=> p.tpe
}
val forwresult = dd.symbol.tpe_*.finalResultType
val forwformsyms = map2(forwformals, flatparams)((tp, oldparam) =>
- currentClass.newValueParameter(oldparam.name, oldparam.symbol.pos).setInfo(tp)
+ currentClass.newValueParameter(oldparam.name.toTermName, oldparam.pos).setInfo(tp)
)
def mono = MethodType(forwformsyms, forwresult)
val forwtype = dd.symbol.tpe match {
@@ -761,13 +752,13 @@ abstract class UnCurry extends InfoTransform
// create the tree
val forwtree = theTyper.typedPos(dd.pos) {
- val locals = map2(forwParams, flatparams) {
- case (_, fp) if !rpsymbols(fp.symbol) => null
- case (argsym, fp) =>
+ val locals = map3(forwParams, flatparams, isRepeated) {
+ case (_, fp, false) => null
+ case (argsym, fp, true) =>
Block(Nil,
gen.mkCast(
gen.mkWrapArray(Ident(argsym), elementType(ArrayClass, argsym.tpe)),
- seqType(elementType(SeqClass, fp.symbol.tpe))
+ seqType(elementType(SeqClass, fp.tpe))
)
)
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
index 6f81cbe152..b2dc6e4e52 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
@@ -50,7 +50,7 @@ trait TreeAndTypeAnalysis extends Debugging {
// but the annotation didn't bubble up...
// This is a pretty poor approximation.
def unsoundAssumptionUsed = binder.name != nme.WILDCARD && !(pt <:< pat.tpe)
- if (settings.lint && unsoundAssumptionUsed)
+ if (settings.warnUnsoundMatch && unsoundAssumptionUsed)
reporter.warning(pat.pos,
sm"""The value matched by $pat is bound to ${binder.name}, which may be used under the
|unsound assumption that it has type ${pat.tpe}, whereas we can only safely
diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala
index 37c39c07be..2f4d228347 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala
@@ -82,7 +82,8 @@ trait Adaptations {
} else if (settings.warnAdaptedArgs)
context.warning(t.pos, adaptWarningMessage(s"Adapting argument list by creating a ${args.size}-tuple: this may not be what you want."))
- !settings.noAdaptedArgs || !(args.isEmpty && settings.future)
+ // return `true` if the adaptation should be kept
+ !(settings.noAdaptedArgs || (args.isEmpty && settings.future))
}
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 9715fdaf00..20e462bbce 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -27,6 +27,16 @@ trait ContextErrors {
override def toString() = "[Type error at:" + errPos + "] " + errMsg
}
+ abstract class AbsAmbiguousTypeError extends AbsTypeError
+
+ case class AmbiguousTypeError(errPos: Position, errMsg: String)
+ extends AbsAmbiguousTypeError
+
+ case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String)
+ extends AbsAmbiguousTypeError {
+ def errPos = underlyingTree.pos
+ }
+
sealed abstract class TreeTypeError extends AbsTypeError {
def underlyingTree: Tree
def errPos = underlyingTree.pos
@@ -38,9 +48,6 @@ trait ContextErrors {
case class AccessTypeError(underlyingTree: Tree, errMsg: String)
extends TreeTypeError
- case class AmbiguousTypeError(errPos: Position, errMsg: String)
- extends AbsTypeError
-
case class SymbolTypeError(underlyingSym: Symbol, errMsg: String)
extends AbsTypeError {
@@ -75,8 +82,6 @@ trait ContextErrors {
s"diverging implicit expansion for type ${pt}\nstarting with ${sym.fullLocationString}"
}
- case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String)
- extends TreeTypeError
case class PosAndMsgTypeError(errPos: Position, errMsg: String)
extends AbsTypeError
@@ -90,10 +95,6 @@ trait ContextErrors {
issueTypeError(SymbolTypeError(sym, msg))
}
- def issueAmbiguousTypeError(pre: Type, sym1: Symbol, sym2: Symbol, err: AmbiguousTypeError)(implicit context: Context) {
- context.issueAmbiguousError(pre, sym1, sym2, err)
- }
-
def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) }
def typeErrorMsg(found: Type, req: Type) = "type mismatch" + foundReqMsg(found, req)
@@ -123,6 +124,36 @@ trait ContextErrors {
import ErrorUtils._
+ private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = {
+ def debugDiagnostic = s"(internal diagnostic: $internalMessage)"
+ val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage
+ // TODO: clean this up! (This is a more explicit version of what the code use to do, to reveal the issue.)
+ throw new TypeError(analyzer.lastTreeToTyper.pos, message)
+ }
+
+ def MacroCantExpand210xMacrosError(internalMessage: String) =
+ MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage)
+
+ def MacroCantExpandIncompatibleMacrosError(internalMessage: String) =
+ MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage)
+
+ def NoImplicitFoundError(tree: Tree, param: Symbol)(implicit context: Context): Unit = {
+ def errMsg = {
+ val paramName = param.name
+ val paramTp = param.tpe
+ def evOrParam = (
+ if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX)
+ "evidence parameter of type"
+ else
+ s"parameter $paramName:")
+ paramTp.typeSymbolDirect match {
+ case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp)
+ case _ => s"could not find implicit value for $evOrParam $paramTp"
+ }
+ }
+ issueNormalTypeError(tree, errMsg)
+ }
+
trait TyperContextErrors {
self: Typer =>
@@ -141,24 +172,6 @@ trait ContextErrors {
setError(tree)
}
- def NoImplicitFoundError(tree: Tree, param: Symbol) = {
- def errMsg = {
- val paramName = param.name
- val paramTp = param.tpe
- def evOrParam = (
- if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX)
- "evidence parameter of type"
- else
- s"parameter $paramName:"
- )
- paramTp.typeSymbolDirect match {
- case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp)
- case _ => s"could not find implicit value for $evOrParam $paramTp"
- }
- }
- issueNormalTypeError(tree, errMsg)
- }
-
def AdaptTypeError(tree: Tree, found: Type, req: Type) = {
// SI-3971 unwrapping to the outermost Apply helps prevent confusion with the
// error message point.
@@ -733,17 +746,6 @@ trait ContextErrors {
NormalTypeError(expandee, "too many argument lists for " + fun)
}
- private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = {
- def debugDiagnostic = s"(internal diagnostic: $internalMessage)"
- val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage
- issueNormalTypeError(lastTreeToTyper, message)
- }
-
- def MacroCantExpand210xMacrosError(internalMessage: String) =
- MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage)
-
- def MacroCantExpandIncompatibleMacrosError(internalMessage: String) =
- MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage)
case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable
@@ -883,19 +885,21 @@ trait ContextErrors {
val WrongNumber, NoParams, ArgsDoNotConform = Value
}
- private def ambiguousErrorMsgPos(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) =
- if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) {
- val methodName = nme.defaultGetterToMethod(sym1.name)
- (sym1.enclClass.pos,
- "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName +
- " define default arguments")
- } else {
- (pos,
- ("ambiguous reference to overloaded definition,\n" +
- "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) +
- "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) +
- "\nmatch " + rest)
- )
+ private def issueAmbiguousTypeErrorUnlessErroneous(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String): Unit =
+ if (!(pre.isErroneous || sym1.isErroneous || sym2.isErroneous)) {
+ if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) {
+ val methodName = nme.defaultGetterToMethod(sym1.name)
+ context.issueAmbiguousError(AmbiguousTypeError(sym1.enclClass.pos,
+ "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName +
+ " define default arguments"))
+ } else {
+ context.issueAmbiguousError(AmbiguousTypeError(pos,
+ ("ambiguous reference to overloaded definition,\n" +
+ "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) +
+ "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) +
+ "\nmatch " + rest)
+ ))
+ }
}
def AccessError(tree: Tree, sym: Symbol, ctx: Context, explanation: String): AbsTypeError =
@@ -952,8 +956,7 @@ trait ContextErrors {
val msg0 =
"argument types " + argtpes.mkString("(", ",", ")") +
(if (pt == WildcardType) "" else " and expected result type " + pt)
- val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0)
- issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg))
+ issueAmbiguousTypeErrorUnlessErroneous(tree.pos, pre, best, firstCompeting, msg0)
setErrorOnLastTry(lastTry, tree)
} else setError(tree) // do not even try further attempts because they should all fail
// even if this is not the last attempt (because of the SO's possibility on the horizon)
@@ -966,8 +969,7 @@ trait ContextErrors {
}
def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type, lastTry: Boolean) = {
- val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt)
- issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg))
+ issueAmbiguousTypeErrorUnlessErroneous(tree.pos, pre, best, firstCompeting, "expected type " + pt)
setErrorOnLastTry(lastTry, tree)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 72ca9b879a..a79f162140 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -9,6 +9,7 @@ package typechecker
import scala.collection.{ immutable, mutable }
import scala.annotation.tailrec
import scala.reflect.internal.util.shortClassOfInstance
+import scala.tools.nsc.reporters.Reporter
/**
* @author Martin Odersky
@@ -98,7 +99,7 @@ trait Contexts { self: Analyzer =>
}
- def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = {
+ def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, throwing: Boolean = false, checking: Boolean = false): Context = {
val rootImportsContext = (startContext /: rootImports(unit))((c, sym) => c.make(gen.mkWildcardImport(sym)))
// there must be a scala.xml package when xml literals were parsed in this unit
@@ -113,18 +114,21 @@ trait Contexts { self: Analyzer =>
else rootImportsContext.make(gen.mkImport(ScalaXmlPackage, nme.TopScope, nme.dollarScope))
val c = contextWithXML.make(tree, unit = unit)
- if (erasedTypes) c.setThrowErrors() else c.setReportErrors()
- c(EnrichmentEnabled | ImplicitsEnabled) = !erasedTypes
+
+ c.initRootContext(throwing, checking)
c
}
+ def rootContextPostTyper(unit: CompilationUnit, tree: Tree = EmptyTree): Context =
+ rootContext(unit, tree, throwing = true)
+
def resetContexts() {
startContext.enclosingContextChain foreach { context =>
context.tree match {
case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol)
case _ =>
}
- context.reportBuffer.clearAll()
+ context.reporter.clearAll()
}
}
@@ -178,7 +182,8 @@ trait Contexts { self: Analyzer =>
* @param _outer The next outer context.
*/
class Context private[typechecker](val tree: Tree, val owner: Symbol, val scope: Scope,
- val unit: CompilationUnit, _outer: Context) {
+ val unit: CompilationUnit, _outer: Context,
+ private[this] var _reporter: ContextReporter = new ThrowingReporter) {
private def outerIsNoContext = _outer eq null
final def outer: Context = if (outerIsNoContext) NoContext else _outer
@@ -254,8 +259,6 @@ trait Contexts { self: Analyzer =>
def macrosEnabled = this(MacrosEnabled)
def enrichmentEnabled_=(value: Boolean) = this(EnrichmentEnabled) = value
def enrichmentEnabled = this(EnrichmentEnabled)
- def checking_=(value: Boolean) = this(Checking) = value
- def checking = this(Checking)
def retyping_=(value: Boolean) = this(ReTyping) = value
def retyping = this(ReTyping)
def inSecondTry = this(SecondTry)
@@ -265,8 +268,9 @@ trait Contexts { self: Analyzer =>
def defaultModeForTyped: Mode = if (inTypeConstructorAllowed) Mode.NOmode else Mode.EXPRmode
- /** These messages are printed when issuing an error */
- var diagnostic: List[String] = Nil
+ /** To enrich error messages involving default arguments.
+ When extending the notion, group diagnostics in an object. */
+ var diagUsedDefaults: Boolean = false
/** Saved type bounds for type parameters which are narrowed in a GADT. */
var savedTypeBounds: List[(Symbol, Type)] = List()
@@ -310,7 +314,7 @@ trait Contexts { self: Analyzer =>
*/
def savingUndeterminedTypeParams[A](reportAmbiguous: Boolean = ambiguousErrors)(body: => A): A = {
withMode() {
- this(AmbiguousErrors) = reportAmbiguous
+ setAmbiguousErrors(reportAmbiguous)
val saved = extractUndetparams()
try body
finally undetparams = saved
@@ -321,54 +325,59 @@ trait Contexts { self: Analyzer =>
// Error reporting policies and buffer.
//
- private var _reportBuffer: ReportBuffer = new ReportBuffer
- /** A buffer for errors and warnings, used with `this.bufferErrors == true` */
- def reportBuffer = _reportBuffer
- /** Discard the current report buffer, and replace with an empty one */
- def useFreshReportBuffer() = _reportBuffer = new ReportBuffer
- /** Discard the current report buffer, and replace with `other` */
- def restoreReportBuffer(other: ReportBuffer) = _reportBuffer = other
-
- /** The first error, if any, in the report buffer */
- def firstError: Option[AbsTypeError] = reportBuffer.firstError
- def errors: Seq[AbsTypeError] = reportBuffer.errors
- /** Does the report buffer contain any errors? */
- def hasErrors = reportBuffer.hasErrors
-
- def reportErrors = this(ReportErrors)
- def bufferErrors = this(BufferErrors)
+ // the reporter for this context
+ def reporter: ContextReporter = _reporter
+
+ // if set, errors will not be reporter/thrown
+ def bufferErrors = reporter.isBuffering
+ def reportErrors = !bufferErrors
+
+ // whether to *report* (which is separate from buffering/throwing) ambiguity errors
def ambiguousErrors = this(AmbiguousErrors)
- def throwErrors = contextMode.inNone(ReportErrors | BufferErrors)
-
- def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors)
- def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors)
- def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false
- def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report
-
- /** Append the given errors to the report buffer */
- def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors
- /** Clear all errors from the report buffer */
- def flushBuffer() { reportBuffer.clearAllErrors() }
- /** Return and clear all errors from the report buffer */
- def flushAndReturnBuffer(): immutable.Seq[AbsTypeError] = {
- val current = reportBuffer.errors
- reportBuffer.clearAllErrors()
- current
- }
- /** Issue and clear all warnings from the report buffer */
- def flushAndIssueWarnings() {
- reportBuffer.warnings foreach {
- case (pos, msg) => reporter.warning(pos, msg)
+ private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report
+
+ /**
+ * Try inference twice: once without views and once with views,
+ * unless views are already disabled.
+ */
+ abstract class TryTwice {
+ def tryOnce(isLastTry: Boolean): Unit
+
+ final def apply(): Unit = {
+ val doLastTry =
+ // do first try if implicits are enabled
+ if (implicitsEnabled) {
+ // We create a new BufferingReporter to
+ // distinguish errors that occurred before entering tryTwice
+ // and our first attempt in 'withImplicitsDisabled'. If the
+ // first attempt fails, we try with implicits on
+ // and the original reporter.
+ // immediate reporting of ambiguous errors is suppressed, so that they are buffered
+ inSilentMode {
+ try {
+ set(disable = ImplicitsEnabled | EnrichmentEnabled) // restored by inSilentMode
+ tryOnce(false)
+ reporter.hasErrors
+ } catch {
+ case ex: CyclicReference => throw ex
+ case ex: TypeError => true // recoverable cyclic references?
+ }
+ }
+ } else true
+
+ // do last try if try with implicits enabled failed
+ // (or if it was not attempted because they were disabled)
+ if (doLastTry)
+ tryOnce(true)
}
- reportBuffer.clearAllWarnings()
}
//
// Temporary mode adjustment
//
- @inline def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = {
+ @inline final def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = {
val saved = contextMode
set(enabled, disabled)
try op
@@ -402,12 +411,18 @@ trait Contexts { self: Analyzer =>
// See comment on FormerNonStickyModes.
@inline final def withOnlyStickyModes[T](op: => T): T = withMode(disabled = FormerNonStickyModes)(op)
- /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */
+ // inliner note: this has to be a simple method for inlining to work -- moved the `&& !reporter.hasErrors` out
@inline final def inSilentMode(expr: => Boolean): Boolean = {
- withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`.
- setBufferErrors()
- try expr && !hasErrors
- finally reportBuffer.clearAll()
+ val savedContextMode = contextMode
+ val savedReporter = reporter
+
+ setAmbiguousErrors(false)
+ _reporter = new BufferingReporter
+
+ try expr
+ finally {
+ contextMode = savedContextMode
+ _reporter = savedReporter
}
}
@@ -423,7 +438,8 @@ trait Contexts { self: Analyzer =>
* `Context#imports`.
*/
def make(tree: Tree = tree, owner: Symbol = owner,
- scope: Scope = scope, unit: CompilationUnit = unit): Context = {
+ scope: Scope = scope, unit: CompilationUnit = unit,
+ reporter: ContextReporter = this.reporter): Context = {
val isTemplateOrPackage = tree match {
case _: Template | _: PackageDef => true
case _ => false
@@ -446,16 +462,15 @@ trait Contexts { self: Analyzer =>
// The blank canvas
val c = if (isImport)
- new Context(tree, owner, scope, unit, this) with ImportContext
+ new Context(tree, owner, scope, unit, this, reporter) with ImportContext
else
- new Context(tree, owner, scope, unit, this)
+ new Context(tree, owner, scope, unit, this, reporter)
// Fields that are directly propagated
c.variance = variance
- c.diagnostic = diagnostic
+ c.diagUsedDefaults = diagUsedDefaults
c.openImplicits = openImplicits
c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below.
- c._reportBuffer = reportBuffer
// Fields that may take on a different value in the child
c.prefix = prefixInChild
@@ -470,22 +485,38 @@ trait Contexts { self: Analyzer =>
c
}
+ /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/
+ def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = {
+ _reporter =
+ if (checking) new CheckingReporter
+ else if (throwing) new ThrowingReporter
+ else new ImmediateReporter
+
+ setAmbiguousErrors(!throwing)
+ this(EnrichmentEnabled | ImplicitsEnabled) = !throwing
+ }
+
def make(tree: Tree, owner: Symbol, scope: Scope): Context =
// TODO SI-7345 Moving this optimization into the main overload of `make` causes all tests to fail.
- // even if it is extened to check that `unit == this.unit`. Why is this?
+ // even if it is extended to check that `unit == this.unit`. Why is this?
if (tree == this.tree && owner == this.owner && scope == this.scope) this
else make(tree, owner, scope, unit)
/** Make a child context that represents a new nested scope */
- def makeNewScope(tree: Tree, owner: Symbol): Context =
- make(tree, owner, newNestedScope(scope))
+ def makeNewScope(tree: Tree, owner: Symbol, reporter: ContextReporter = this.reporter): Context =
+ make(tree, owner, newNestedScope(scope), reporter = reporter)
/** Make a child context that buffers errors and warnings into a fresh report buffer. */
def makeSilent(reportAmbiguousErrors: Boolean = ambiguousErrors, newtree: Tree = tree): Context = {
- val c = make(newtree)
- c.setBufferErrors()
+ // A fresh buffer so as not to leak errors/warnings into `this`.
+ val c = make(newtree, reporter = new BufferingReporter)
c.setAmbiguousErrors(reportAmbiguousErrors)
- c._reportBuffer = new ReportBuffer // A fresh buffer so as not to leak errors/warnings into `this`.
+ c
+ }
+
+ def makeNonSilent(newtree: Tree): Context = {
+ val c = make(newtree, reporter = reporter.makeImmediate)
+ c.setAmbiguousErrors(true)
c
}
@@ -508,7 +539,9 @@ trait Contexts { self: Analyzer =>
*/
def makeConstructorContext = {
val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template])
- val argContext = baseContext.makeNewScope(tree, owner)
+ // must propagate reporter!
+ // (caught by neg/t3649 when refactoring reporting to be specified only by this.reporter and not also by this.contextMode)
+ val argContext = baseContext.makeNewScope(tree, owner, reporter = this.reporter)
argContext.contextMode = contextMode
argContext.inSelfSuperCall = true
def enterElems(c: Context) {
@@ -533,65 +566,16 @@ trait Contexts { self: Analyzer =>
// Error and warning issuance
//
- private def addDiagString(msg: String) = {
- val ds =
- if (diagnostic.isEmpty) ""
- else diagnostic.mkString("\n","\n", "")
- if (msg endsWith ds) msg else msg + ds
- }
-
- private def unitError(pos: Position, msg: String): Unit =
- if (checking) onTreeCheckerError(pos, msg) else reporter.error(pos, msg)
-
- @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) {
- // TODO: are errors allowed to have pos == NoPosition??
- // if not, Jason suggests doing: val pos = err.errPos.orElse( { devWarning("Que?"); context.tree.pos })
- if (settings.Yissuedebug) {
- log("issue error: " + err.errMsg)
- (new Exception).printStackTrace()
- }
- if (pf isDefinedAt err) pf(err)
- else if (bufferErrors) { reportBuffer += err }
- else throw new TypeError(err.errPos, err.errMsg)
- }
-
/** Issue/buffer/throw the given type error according to the current mode for error reporting. */
- def issue(err: AbsTypeError) {
- issueCommon(err) { case _ if reportErrors =>
- unitError(err.errPos, addDiagString(err.errMsg))
- }
- }
-
- /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */
- def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) {
- issueCommon(err) { case _ if ambiguousErrors =>
- if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous)
- unitError(err.errPos, err.errMsg)
- }
- }
-
+ private[typechecker] def issue(err: AbsTypeError) = reporter.issue(err)(this)
/** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */
- def issueAmbiguousError(err: AbsTypeError) {
- issueCommon(err) { case _ if ambiguousErrors => unitError(err.errPos, addDiagString(err.errMsg)) }
- }
-
- /** Issue/throw the given `err` according to the current mode for error reporting. */
- def error(pos: Position, err: Throwable) =
- if (reportErrors) unitError(pos, addDiagString(err.getMessage()))
- else throw err
-
+ private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reporter.issueAmbiguousError(err)(this)
/** Issue/throw the given error message according to the current mode for error reporting. */
- def error(pos: Position, msg: String) = {
- val msg1 = addDiagString(msg)
- if (reportErrors) unitError(pos, msg1)
- else throw new TypeError(pos, msg1)
- }
-
+ def error(pos: Position, msg: String) = reporter.error(pos, msg)
/** Issue/throw the given error message according to the current mode for error reporting. */
- def warning(pos: Position, msg: String, force: Boolean = false) {
- if (reportErrors || force) reporter.warning(pos, msg)
- else if (bufferErrors) reportBuffer += (pos -> msg)
- }
+ def warning(pos: Position, msg: String) = reporter.warning(pos, msg)
+ def echo(pos: Position, msg: String) = reporter.echo(pos, msg)
+
def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit =
currentRun.reporting.deprecationWarning(pos, sym, msg)
@@ -601,7 +585,6 @@ trait Contexts { self: Analyzer =>
def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit =
currentRun.reporting.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required)
- def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg)
// nextOuter determines which context is searched next for implicits
// (after `this`, which contributes `newImplicits` below.) In
@@ -1238,61 +1221,176 @@ trait Contexts { self: Analyzer =>
override final def toString = super.toString + " with " + s"ImportContext { $impInfo; outer.owner = ${outer.owner} }"
}
- /** A buffer for warnings and errors that are accumulated during speculative type checking. */
- final class ReportBuffer {
+ /** A reporter for use during type checking. It has multiple modes for handling errors.
+ *
+ * The default (immediate mode) is to send the error to the global reporter.
+ * When switched into buffering mode via makeBuffering, errors and warnings are buffered and not be reported
+ * (there's a special case for ambiguity errors for some reason: those are force to the reporter when context.ambiguousErrors,
+ * or else they are buffered -- TODO: can we simplify this?)
+ *
+ * When using the type checker after typers, an error results in a TypeError being thrown. TODO: get rid of this mode.
+ *
+ * To handle nested contexts, reporters share buffers. TODO: only buffer in BufferingReporter, emit immediately in ImmediateReporter
+ */
+ abstract class ContextReporter(private[this] var _errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, private[this] var _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends Reporter {
type Error = AbsTypeError
type Warning = (Position, String)
- private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results.
+ def issue(err: AbsTypeError)(implicit context: Context): Unit = handleError(err.errPos, addDiagString(err.errMsg))
- // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This
- // is replicated here out of conservatism.
- private var _errorBuffer: mutable.LinkedHashSet[Error] = _
- private def errorBuffer = {if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer}
- def errors: immutable.Seq[Error] = errorBuffer.toVector
+ protected def handleError(pos: Position, msg: String): Unit
+ protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = ()
+ protected def handleWarning(pos: Position, msg: String): Unit = reporter.warning(pos, msg)
- private var _warningBuffer: mutable.LinkedHashSet[Warning] = _
- private def warningBuffer = {if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer}
- def warnings: immutable.Seq[Warning] = warningBuffer.toVector
+ def makeImmediate: ContextReporter = this
+ def makeBuffering: ContextReporter = this
+ def isBuffering: Boolean = false
- def +=(error: AbsTypeError): this.type = {
- errorBuffer += error
- this
- }
- def ++=(errors: Traversable[AbsTypeError]): this.type = {
- errorBuffer ++= errors
- this
- }
- def +=(warning: Warning): this.type = {
- warningBuffer += warning
- this
+ /** Emit an ambiguous error according to context.ambiguousErrors
+ *
+ * - when true, use global.reporter regardless of whether we're buffering (TODO: can we change this?)
+ * - else, let this context reporter decide
+ */
+ final def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit =
+ if (context.ambiguousErrors) reporter.error(err.errPos, addDiagString(err.errMsg)) // force reporting... see TODO above
+ else handleSuppressedAmbiguous(err)
+
+ @inline final def withFreshErrorBuffer[T](expr: => T): T = {
+ val previousBuffer = _errorBuffer
+ _errorBuffer = newBuffer
+ val res = expr // expr will read _errorBuffer
+ _errorBuffer = previousBuffer
+ res
}
- def clearAll(): this.type = {
- clearAllErrors(); clearAllWarnings();
+ @inline final def propagatingErrorsTo[T](target: ContextReporter)(expr: => T): T = {
+ val res = expr // TODO: make sure we're okay skipping the try/finally overhead
+ if ((this ne target) && hasErrors) { // `this eq target` in e.g., test/files/neg/divergent-implicit.scala
+ // assert(target.errorBuffer ne _errorBuffer)
+ target ++= errors
+ // TODO: is clearAllErrors necessary? (no tests failed when dropping it)
+ // NOTE: even though `this ne target`, it may still be that `target.errorBuffer eq _errorBuffer`,
+ // so don't clear the buffer, but null out the reference so that a new one will be created when necessary (should be never??)
+ // (we should refactor error buffering to avoid mutation on shared buffers)
+ clearAllErrors()
+ }
+ res
}
- def clearAllErrors(): this.type = {
- errorBuffer.clear()
- this
- }
- def clearErrors(removeF: PartialFunction[AbsTypeError, Boolean]): this.type = {
- errorBuffer.retain(!PartialFunction.cond(_)(removeF))
- this
+ protected final def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit =
+ severity match {
+ case ERROR => handleError(pos, msg)
+ case WARNING => handleWarning(pos, msg)
+ case INFO => reporter.echo(pos, msg)
+ }
+
+ final override def hasErrors = super.hasErrors || errorBuffer.nonEmpty
+
+ // TODO: everything below should be pushed down to BufferingReporter (related to buffering)
+ // Implicit relies on this most heavily, but there you know reporter.isInstanceOf[BufferingReporter]
+ // can we encode this statically?
+
+ // have to pass in context because multiple contexts may share the same ReportBuffer
+ def reportFirstDivergentError(fun: Tree, param: Symbol, paramTp: Type)(implicit context: Context): Unit =
+ errors.collectFirst {
+ case dte: DivergentImplicitTypeError => dte
+ } match {
+ case Some(divergent) =>
+ // DivergentImplicit error has higher priority than "no implicit found"
+ // no need to issue the problem again if we are still in silent mode
+ if (context.reportErrors) {
+ context.issue(divergent.withPt(paramTp))
+ errorBuffer.retain {
+ case dte: DivergentImplicitTypeError => false
+ case _ => true
+ }
+ }
+ case _ =>
+ NoImplicitFoundError(fun, param)(context)
+ }
+
+ def retainDivergentErrorsExcept(saved: DivergentImplicitTypeError) =
+ errorBuffer.retain {
+ case err: DivergentImplicitTypeError => err ne saved
+ case _ => false
+ }
+
+ def propagateImplicitTypeErrorsTo(target: ContextReporter) = {
+ errors foreach {
+ case err@(_: DivergentImplicitTypeError | _: AmbiguousImplicitTypeError) =>
+ target.errorBuffer += err
+ case _ =>
+ }
+ // debuglog("propagateImplicitTypeErrorsTo: " + errors)
}
- def retainErrors(leaveF: PartialFunction[AbsTypeError, Boolean]): this.type = {
- errorBuffer.retain(PartialFunction.cond(_)(leaveF))
- this
+
+ protected def addDiagString(msg: String)(implicit context: Context): String = {
+ val diagUsedDefaultsMsg = "Error occurred in an application involving default arguments."
+ if (context.diagUsedDefaults && !(msg endsWith diagUsedDefaultsMsg)) msg + "\n" + diagUsedDefaultsMsg
+ else msg
}
- def clearAllWarnings(): this.type = {
- warningBuffer.clear()
- this
+
+ final def emitWarnings() = if (_warningBuffer != null) {
+ _warningBuffer foreach {
+ case (pos, msg) => reporter.warning(pos, msg)
+ }
+ _warningBuffer = null
}
- def hasErrors = errorBuffer.nonEmpty
- def firstError = errorBuffer.headOption
+ // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This
+ // is replicated here out of conservatism.
+ private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results.
+ final protected def errorBuffer = { if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer }
+ final protected def warningBuffer = { if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer }
+
+ final def errors: immutable.Seq[Error] = errorBuffer.toVector
+ final def warnings: immutable.Seq[Warning] = warningBuffer.toVector
+ final def firstError: Option[AbsTypeError] = errorBuffer.headOption
+
+ // TODO: remove ++= and clearAll* entirely in favor of more high-level combinators like withFreshErrorBuffer
+ final private[typechecker] def ++=(errors: Traversable[AbsTypeError]): Unit = errorBuffer ++= errors
+
+ // null references to buffers instead of clearing them,
+ // as the buffers may be shared between different reporters
+ final def clearAll(): Unit = { _errorBuffer = null; _warningBuffer = null }
+ final def clearAllErrors(): Unit = { _errorBuffer = null }
+ }
+
+ private[typechecker] class ImmediateReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) {
+ override def makeBuffering: ContextReporter = new BufferingReporter(errorBuffer, warningBuffer)
+ protected def handleError(pos: Position, msg: String): Unit = reporter.error(pos, msg)
+ }
+
+
+ private[typechecker] class BufferingReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) {
+ override def isBuffering = true
+
+ override def issue(err: AbsTypeError)(implicit context: Context): Unit = errorBuffer += err
+
+ // this used to throw new TypeError(pos, msg) -- buffering lets us report more errors (test/files/neg/macro-basic-mamdmi)
+ // the old throwing behavior was relied on by diagnostics in manifestOfType
+ protected def handleError(pos: Position, msg: String): Unit = errorBuffer += TypeErrorWrapper(new TypeError(pos, msg))
+ override protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = errorBuffer += err
+ override protected def handleWarning(pos: Position, msg: String): Unit = warningBuffer += ((pos, msg))
+
+ // TODO: emit all buffered errors, warnings
+ override def makeImmediate: ContextReporter = new ImmediateReporter(errorBuffer, warningBuffer)
}
+ /** Used after typer (specialization relies on TypeError being thrown, among other post-typer phases).
+ *
+ * TODO: get rid of it, use ImmediateReporter and a check for reporter.hasErrors where necessary
+ */
+ private[typechecker] class ThrowingReporter extends ContextReporter {
+ protected def handleError(pos: Position, msg: String): Unit = throw new TypeError(pos, msg)
+ }
+
+ /** Used during a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */
+ private[typechecker] class CheckingReporter extends ContextReporter {
+ protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg)
+ }
+
+
class ImportInfo(val tree: Import, val depth: Int) {
def pos = tree.pos
def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos
@@ -1385,8 +1483,6 @@ object ContextMode {
def apply(bits: Int): ContextMode = new ContextMode(bits)
final val NOmode: ContextMode = 0
- final val ReportErrors: ContextMode = 1 << 0
- final val BufferErrors: ContextMode = 1 << 1
final val AmbiguousErrors: ContextMode = 1 << 2
/** Are we in a secondary constructor after the this constructor call? */
@@ -1409,8 +1505,6 @@ object ContextMode {
/** To selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed */
final val EnrichmentEnabled: ContextMode = 1 << 8
- /** Are we in a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */
- final val Checking: ContextMode = 1 << 9
/** Are we retypechecking arguments independently from the function applied to them? See `Typer.tryTypedApply`
* TODO - iron out distinction/overlap with SecondTry.
@@ -1447,17 +1541,14 @@ object ContextMode {
PatternAlternative | StarPatterns | SuperInit | SecondTry | ReturnExpr | TypeConstructorAllowed
)
- final val DefaultMode: ContextMode = MacrosEnabled
+ final val DefaultMode: ContextMode = MacrosEnabled
private val contextModeNameMap = Map(
- ReportErrors -> "ReportErrors",
- BufferErrors -> "BufferErrors",
AmbiguousErrors -> "AmbiguousErrors",
ConstructorSuffix -> "ConstructorSuffix",
SelfSuperCall -> "SelfSuperCall",
ImplicitsEnabled -> "ImplicitsEnabled",
MacrosEnabled -> "MacrosEnabled",
- Checking -> "Checking",
ReTyping -> "ReTyping",
PatternAlternative -> "PatternAlternative",
StarPatterns -> "StarPatterns",
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 73c3e6f016..b85c8e6d42 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -71,13 +71,10 @@ trait Implicits {
typingStack.printTyping(tree, "typing implicit: %s %s".format(tree, context.undetparamsString))
val implicitSearchContext = context.makeImplicit(reportAmbiguous)
val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit
- if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.hasErrors) {
- context.updateBuffer(implicitSearchContext.reportBuffer.errors.collect {
- case dte: DivergentImplicitTypeError => dte
- case ate: AmbiguousImplicitTypeError => ate
- })
- debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors)
- }
+
+ if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.reporter.hasErrors)
+ implicitSearchContext.reporter.propagateImplicitTypeErrorsTo(context.reporter)
+
// SI-7944 undetermined type parameters that result from inference within typedImplicit land in
// `implicitSearchContext.undetparams`, *not* in `context.undetparams`
// Here, we copy them up to parent context (analogously to the way the errors are copied above),
@@ -99,7 +96,7 @@ trait Implicits {
def wrapper(inference: => SearchResult) = wrapper1(inference)
val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos))
if (result.isFailure && !silent) {
- val err = context.firstError
+ val err = context.reporter.firstError
val errPos = err.map(_.errPos).getOrElse(pos)
val errMsg = err.map(_.errMsg).getOrElse("implicit search has failed. to find out the reason, turn on -Xlog-implicits")
onError(errPos, errMsg)
@@ -635,7 +632,7 @@ trait Implicits {
}
case _ => fallback
}
- context.firstError match { // using match rather than foreach to avoid non local return.
+ context.reporter.firstError match { // using match rather than foreach to avoid non local return.
case Some(err) =>
log("implicit adapt failed: " + err.errMsg)
return fail(err.errMsg)
@@ -658,8 +655,8 @@ trait Implicits {
}
}
- if (context.hasErrors)
- fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg)
+ if (context.reporter.hasErrors)
+ fail("hasMatchingSymbol reported error: " + context.reporter.firstError.get.errMsg)
else if (itree3.isErroneous)
fail("error typechecking implicit candidate")
else if (isLocalToCallsite && !hasMatchingSymbol(itree2))
@@ -677,7 +674,7 @@ trait Implicits {
// #2421: check that we correctly instantiated type parameters outside of the implicit tree:
checkBounds(itree3, NoPrefix, NoSymbol, undetParams, targs, "inferred ")
- context.firstError match {
+ context.reporter.firstError match {
case Some(err) =>
return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg)
case None =>
@@ -716,7 +713,7 @@ trait Implicits {
case t => t
}
- context.firstError match {
+ context.reporter.firstError match {
case Some(err) =>
fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg)
case None =>
@@ -857,13 +854,11 @@ trait Implicits {
SearchFailure
} else {
if (search.isFailure) {
- // We don't want errors that occur during checking implicit info
+ // Discard the divergentError we saved (if any), as well as all errors that are not of type DivergentImplicitTypeError
+ // We don't want errors that occur while checking the implicit info
// to influence the check of further infos, but we should retain divergent implicit errors
// (except for the one we already squirreled away)
- val saved = divergentError.getOrElse(null)
- context.reportBuffer.retainErrors {
- case err: DivergentImplicitTypeError => err ne saved
- }
+ context.reporter.retainDivergentErrorsExcept(divergentError.getOrElse(null))
}
search
}
@@ -909,7 +904,7 @@ trait Implicits {
// the first `DivergentImplicitTypeError` that is being propagated
// from a nested implicit search; this one will be
// re-issued if this level of the search fails.
- DivergentImplicitRecovery(typedFirstPending, firstPending, context.errors) match {
+ DivergentImplicitRecovery(typedFirstPending, firstPending, context.reporter.errors) match {
case sr if sr.isDivergent => Nil
case sr if sr.isFailure => rankImplicits(otherPending, acc)
case newBest =>
@@ -1146,7 +1141,7 @@ trait Implicits {
try {
val tree1 = typedPos(pos.focus)(arg)
- context.firstError match {
+ context.reporter.firstError match {
case Some(err) => processMacroExpansionError(err.errPos, err.errMsg)
case None => new SearchResult(tree1, EmptyTreeTypeSubstituter, Nil)
}
@@ -1278,19 +1273,20 @@ trait Implicits {
if (tagInScope.isEmpty) mot(tp, Nil, Nil)
else {
if (ReflectRuntimeUniverse == NoSymbol) {
- // todo. write a test for this
- context.error(pos,
+ // TODO: write a test for this (the next error message is already checked by neg/interop_typetags_without_classtags_arenot_manifests.scala)
+ // TODO: this was using context.error, and implicit search always runs in silent mode, thus it was actually throwing a TypeError
+ // with the new strategy-based reporting, a BufferingReporter buffers instead of throwing
+ // it would be good to rework this logic to fit into the regular context.error mechanism
+ throw new TypeError(pos,
sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope.
|however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath.
|to proceed put scala-reflect.jar on your compilation classpath and recompile.""")
- return SearchFailure
}
if (resolveClassTag(pos, tp, allowMaterialization = true) == EmptyTree) {
- context.error(pos,
+ throw new TypeError(pos,
sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope.
|however typetag -> manifest conversion requires a class tag for the corresponding type to be present.
|to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""")
- return SearchFailure
}
val cm = typed(Ident(ReflectRuntimeCurrentMirror))
val internal = gen.mkAttributedSelect(gen.mkAttributedRef(ReflectRuntimeUniverse), UniverseInternal)
@@ -1346,52 +1342,66 @@ trait Implicits {
* If all fails return SearchFailure
*/
def bestImplicit: SearchResult = {
- val failstart = if (Statistics.canEnable) Statistics.startTimer(inscopeFailNanos) else null
- val succstart = if (Statistics.canEnable) Statistics.startTimer(inscopeSucceedNanos) else null
+ val stats = Statistics.canEnable
+ val failstart = if (stats) Statistics.startTimer(inscopeFailNanos) else null
+ val succstart = if (stats) Statistics.startTimer(inscopeSucceedNanos) else null
var result = searchImplicit(context.implicitss, isLocalToCallsite = true)
- if (result.isFailure) {
- if (Statistics.canEnable) Statistics.stopTimer(inscopeFailNanos, failstart)
- } else {
- if (Statistics.canEnable) Statistics.stopTimer(inscopeSucceedNanos, succstart)
- if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits)
+ if (stats) {
+ if (result.isFailure) Statistics.stopTimer(inscopeFailNanos, failstart)
+ else {
+ Statistics.stopTimer(inscopeSucceedNanos, succstart)
+ Statistics.incCounter(inscopeImplicitHits)
+ }
}
+
if (result.isFailure) {
- val previousErrs = context.flushAndReturnBuffer()
- val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null
- val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null
+ val failstart = if (stats) Statistics.startTimer(oftypeFailNanos) else null
+ val succstart = if (stats) Statistics.startTimer(oftypeSucceedNanos) else null
+
+ // SI-6667, never search companions after an ambiguous error in in-scope implicits
+ val wasAmbigious = result.isAmbiguousFailure
+
+ // TODO: encapsulate
+ val previousErrs = context.reporter.errors
+ context.reporter.clearAllErrors()
- val wasAmbigious = result.isAmbiguousFailure // SI-6667, never search companions after an ambiguous error in in-scope implicits
result = materializeImplicit(pt)
+
// `materializeImplicit` does some preprocessing for `pt`
// is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`?
if (result.isFailure && !wasAmbigious)
result = searchImplicit(implicitsOfExpectedType, isLocalToCallsite = false)
- if (result.isFailure) {
- context.updateBuffer(previousErrs)
- if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart)
- } else {
- if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart)
- if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits)
+ if (result.isFailure)
+ context.reporter ++= previousErrs
+
+ if (stats) {
+ if (result.isFailure) Statistics.stopTimer(oftypeFailNanos, failstart)
+ else {
+ Statistics.stopTimer(oftypeSucceedNanos, succstart)
+ Statistics.incCounter(oftypeImplicitHits)
+ }
}
}
if (result.isSuccess && isView) {
def maybeInvalidConversionError(msg: String) {
// We have to check context.ambiguousErrors even though we are calling "issueAmbiguousError"
// which ostensibly does exactly that before issuing the error. Why? I have no idea. Test is pos/t7690.
+ // AM: I would guess it's because ambiguous errors will be buffered in silent mode if they are not reported
if (context.ambiguousErrors)
context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg))
}
pt match {
case Function1(_, out) =>
- def prohibit(sym: Symbol) = if (sym.tpe <:< out) {
- maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}")
- result = SearchFailure
+ // must inline to avoid capturing result
+ def prohibit(sym: Symbol) = (sym.tpe <:< out) && {
+ maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}")
+ true
}
- prohibit(AnyRefClass)
- if (settings.isScala211) prohibit(AnyValClass)
+ if (prohibit(AnyRefClass) || (settings.isScala211 && prohibit(AnyValClass)))
+ result = SearchFailure
case _ => false
}
if (settings.isScala211 && isInvalidConversionSource(pt)) {
@@ -1399,8 +1409,9 @@ trait Implicits {
result = SearchFailure
}
}
- if (result.isFailure)
- debuglog("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType)
+
+ if (result.isFailure && settings.debug) // debuglog is not inlined for some reason
+ log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType)
result
}
@@ -1422,20 +1433,19 @@ trait Implicits {
val eligible = new ImplicitComputation(iss, isLocalToCallsite).eligible
eligible.toList.flatMap {
(ii: ImplicitInfo) =>
- // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit)
- // thus, start each type var off with a fresh for every typedImplicit
- resetTVars()
- // any previous errors should not affect us now
- context.flushBuffer()
-
- val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite)
- if (res.tree ne EmptyTree) List((res, tvars map (_.constr)))
- else Nil
+ // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit)
+ // thus, start each type var off with a fresh for every typedImplicit
+ resetTVars()
+ // any previous errors should not affect us now
+ context.reporter.clearAllErrors()
+ val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite)
+ if (res.tree ne EmptyTree) List((res, tvars map (_.constr)))
+ else Nil
+ }
}
- }
eligibleInfos(context.implicitss, isLocalToCallsite = true) ++
eligibleInfos(implicitsOfExpectedType, isLocalToCallsite = false)
- }
+ }
}
object ImplicitNotFoundMsg {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index a3f1da60ce..fb7651ffd6 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -199,8 +199,6 @@ trait Infer extends Checkable {
def getContext = context
- def issue(err: AbsTypeError): Unit = context.issue(err)
-
def explainTypes(tp1: Type, tp2: Type) = {
if (context.reportErrors)
withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2))
@@ -781,7 +779,7 @@ trait Infer extends Checkable {
def applicableExpectingPt(pt: Type): Boolean = {
val silent = context.makeSilent(reportAmbiguousErrors = false)
val result = newTyper(silent).infer.isApplicable(undetparams, ftpe, argtpes0, pt)
- if (silent.hasErrors && !pt.isWildcard)
+ if (silent.reporter.hasErrors && !pt.isWildcard)
applicableExpectingPt(WildcardType) // second try
else
result
@@ -1266,33 +1264,36 @@ trait Infer extends Checkable {
* If no alternative matches `pt`, take the parameterless one anyway.
*/
def inferExprAlternative(tree: Tree, pt: Type): Tree = {
- def tryOurBests(pre: Type, alts: List[Symbol], isSecondTry: Boolean): Unit = {
- val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt))
- val alts1 = if (alts0.isEmpty) alts else alts0
- val bests = bestAlternatives(alts1) { (sym1, sym2) =>
- val tp1 = pre memberType sym1
- val tp2 = pre memberType sym2
-
- ( (tp2 eq ErrorType)
- || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt)
- || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)
- )
- }
- // todo: missing test case for bests.isEmpty
- bests match {
- case best :: Nil => tree setSymbol best setType (pre memberType best)
- case best :: competing :: _ if alts0.nonEmpty =>
- // SI-6912 Don't give up and leave an OverloadedType on the tree.
- // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try
- // unless an error is issued. We're not issuing an error, in the assumption that it would be
- // spurious in light of the erroneous expected type
- if (pt.isErroneous) setError(tree)
- else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry)
- case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry)
+ val c = context
+ class InferTwice(pre: Type, alts: List[Symbol]) extends c.TryTwice {
+ def tryOnce(isSecondTry: Boolean): Unit = {
+ val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt))
+ val alts1 = if (alts0.isEmpty) alts else alts0
+ val bests = bestAlternatives(alts1) { (sym1, sym2) =>
+ val tp1 = pre memberType sym1
+ val tp2 = pre memberType sym2
+
+ ( (tp2 eq ErrorType)
+ || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt)
+ || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)
+ )
+ }
+ // todo: missing test case for bests.isEmpty
+ bests match {
+ case best :: Nil => tree setSymbol best setType (pre memberType best)
+ case best :: competing :: _ if alts0.nonEmpty =>
+ // SI-6912 Don't give up and leave an OverloadedType on the tree.
+ // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try
+ // unless an error is issued. We're not issuing an error, in the assumption that it would be
+ // spurious in light of the erroneous expected type
+ if (pt.isErroneous) setError(tree)
+ else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry)
+ case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry)
+ }
}
}
tree.tpe match {
- case OverloadedType(pre, alts) => tryTwice(tryOurBests(pre, alts, _)) ; tree
+ case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply() ; tree
case _ => tree
}
}
@@ -1370,70 +1371,41 @@ trait Infer extends Checkable {
* @pre tree.tpe is an OverloadedType.
*/
def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], argtpes0: List[Type], pt0: Type): Unit = {
- val OverloadedType(pre, alts) = tree.tpe
- var varargsStar = false
- val argtpes = argtpes0 mapConserve {
- case RepeatedType(tp) => varargsStar = true ; tp
- case tp => tp
- }
- def followType(sym: Symbol) = followApply(pre memberType sym)
- def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = {
- val applicable0 = alts filter (alt => context inSilentMode isApplicable(undetparams, followType(alt), argtpes, pt))
- val applicable = overloadsToConsiderBySpecificity(applicable0, argtpes, varargsStar)
- val ranked = bestAlternatives(applicable)((sym1, sym2) =>
- isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2)
- )
- ranked match {
- case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous
- case best :: Nil => tree setSymbol best setType (pre memberType best) // success
- case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed
- case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType
- }
- }
- // This potentially makes up to four attempts: tryTwice may execute
+ // This potentially makes up to four attempts: tryOnce may execute
// with and without views enabled, and bestForExpectedType will try again
// with pt = WildcardType if it fails with pt != WildcardType.
- tryTwice { isLastTry =>
- val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0
- debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt")
- bestForExpectedType(pt, isLastTry)
- }
- }
+ val c = context
+ class InferMethodAlternativeTwice extends c.TryTwice {
+ private[this] val OverloadedType(pre, alts) = tree.tpe
+ private[this] var varargsStar = false
+ private[this] val argtpes = argtpes0 mapConserve {
+ case RepeatedType(tp) => varargsStar = true ; tp
+ case tp => tp
+ }
- /** Try inference twice, once without views and once with views,
- * unless views are already disabled.
- */
- def tryTwice(infer: Boolean => Unit): Unit = {
- if (context.implicitsEnabled) {
- val savedContextMode = context.contextMode
- var fallback = false
- context.setBufferErrors()
- // We cache the current buffer because it is impossible to
- // distinguish errors that occurred before entering tryTwice
- // and our first attempt in 'withImplicitsDisabled'. If the
- // first attempt fails we try with implicits on *and* clean
- // buffer but that would also flush any pre-tryTwice valid
- // errors, hence some manual buffer tweaking is necessary.
- val errorsToRestore = context.flushAndReturnBuffer()
- try {
- context.withImplicitsDisabled(infer(false))
- if (context.hasErrors) {
- fallback = true
- context.contextMode = savedContextMode
- context.flushBuffer()
- infer(true)
+ private def followType(sym: Symbol) = followApply(pre memberType sym)
+ // separate method to help the inliner
+ private def isAltApplicable(pt: Type)(alt: Symbol) = context inSilentMode { isApplicable(undetparams, followType(alt), argtpes, pt) && !context.reporter.hasErrors }
+ private def rankAlternatives(sym1: Symbol, sym2: Symbol) = isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2)
+ private def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = {
+ val applicable = overloadsToConsiderBySpecificity(alts filter isAltApplicable(pt), argtpes, varargsStar)
+ val ranked = bestAlternatives(applicable)(rankAlternatives)
+ ranked match {
+ case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous
+ case best :: Nil => tree setSymbol best setType (pre memberType best) // success
+ case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed
+ case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType
}
- } catch {
- case ex: CyclicReference => throw ex
- case ex: TypeError => // recoverable cyclic references
- context.contextMode = savedContextMode
- if (!fallback) infer(true) else ()
- } finally {
- context.contextMode = savedContextMode
- context.updateBuffer(errorsToRestore)
+ }
+
+ private[this] val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0
+ def tryOnce(isLastTry: Boolean): Unit = {
+ debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt")
+ bestForExpectedType(pt, isLastTry)
}
}
- else infer(true)
+
+ (new InferMethodAlternativeTwice).apply()
}
/** Assign `tree` the type of all polymorphic alternatives
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 9c22688581..da7b8b09aa 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -226,7 +226,8 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
val Apply(_, pickledPayload) = wrapped
val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap
- import typer.TyperErrorGen._
+ // TODO: refactor error handling: fail always throws a TypeError,
+ // and uses global state (analyzer.lastTreeToTyper) to determine the position for the error
def fail(msg: String) = MacroCantExpandIncompatibleMacrosError(msg)
def unpickle[T](field: String, clazz: Class[T]): T = {
def failField(msg: String) = fail(s"$field $msg")
@@ -576,7 +577,10 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
// also see http://groups.google.com/group/scala-internals/browse_thread/thread/492560d941b315cc
val expanded1 = try onSuccess(duplicateAndKeepPositions(expanded)) finally popMacroContext()
if (!hasMacroExpansionAttachment(expanded1)) linkExpandeeAndExpanded(expandee, expanded1)
- if (settings.Ymacroexpand.value == settings.MacroExpand.Discard) expandee.setType(expanded1.tpe)
+ if (settings.Ymacroexpand.value == settings.MacroExpand.Discard) {
+ suppressMacroExpansion(expandee)
+ expandee.setType(expanded1.tpe)
+ }
else expanded1
case Fallback(fallback) => onFallback(fallback)
case Delayed(delayed) => onDelayed(delayed)
@@ -621,7 +625,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
// `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
// therefore we need to re-enable the conversions back temporarily
val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
- if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}")
+ if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reporter.errors}")
result
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 1328119aac..fdff2f3076 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -717,7 +717,7 @@ trait Namers extends MethodSynthesis {
m.updateAttachment(new ConstructorDefaultsAttachment(tree, null))
}
val owner = tree.symbol.owner
- if (settings.lint && owner.isPackageObjectClass && !mods.isImplicit) {
+ if (settings.warnPackageObjectClasses && owner.isPackageObjectClass && !mods.isImplicit) {
reporter.warning(tree.pos,
"it is not recommended to define classes/objects inside of package objects.\n" +
"If possible, define " + tree.symbol + " in " + owner.skipPackageObject + " instead."
@@ -1494,8 +1494,7 @@ trait Namers extends MethodSynthesis {
case defn: MemberDef =>
val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann =>
val ctx = typer.context
- val annCtx = ctx.make(ann)
- annCtx.setReportErrors()
+ val annCtx = ctx.makeNonSilent(ann)
// need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892.
AnnotationInfo lazily {
enteringTyper(newTyper(annCtx) typedAnnotation ann)
diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
index 43902d1c65..b6387fd56b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -379,18 +379,37 @@ trait NamesDefaults { self: Analyzer =>
def makeNamedTypes(syms: List[Symbol]) = syms map (sym => NamedType(sym.name, sym.tpe))
- def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOfNamedArg _): (List[Symbol], Boolean) = {
- val namedArgs = args.dropWhile(arg => {
- val n = argName(arg)
- n.isEmpty || params.forall(p => p.name != n.get)
- })
- val namedParams = params.drop(args.length - namedArgs.length)
- // missing: keep those with a name which doesn't exist in namedArgs
- val missingParams = namedParams.filter(p => namedArgs.forall(arg => {
+ /**
+ * Returns the parameter symbols of an invocation expression that are not defined by the list
+ * of arguments.
+ *
+ * @param args The list of arguments
+ * @param params The list of parameter sybols of the invoked method
+ * @param argName A function that extracts the name of an argument expression, if it is a named argument.
+ */
+ def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name]): (List[Symbol], Boolean) = {
+ // The argument list contains first a mix of positional args and named args that are on the
+ // right parameter position, and then a number or named args on different positions.
+
+ // collect all named arguments whose position does not match the parameter they define
+ val namedArgsOnChangedPosition = args.zip(params) dropWhile {
+ case (arg, param) =>
+ val n = argName(arg)
+ // drop the argument if
+ // - it's not named, or
+ // - it's named, but defines the parameter on its current position, or
+ // - it's named, but none of the parameter names matches (treated as a positional argument, an assignment expression)
+ n.isEmpty || n.get == param.name || params.forall(_.name != n.get)
+ } map (_._1)
+
+ val paramsWithoutPositionalArg = params.drop(args.length - namedArgsOnChangedPosition.length)
+
+ // missing parameters: those with a name which is not specified in one of the namedArgsOnChangedPosition
+ val missingParams = paramsWithoutPositionalArg.filter(p => namedArgsOnChangedPosition.forall { arg =>
val n = argName(arg)
n.isEmpty || n.get != p.name
- }))
- val allPositional = missingParams.length == namedParams.length
+ })
+ val allPositional = missingParams.length == paramsWithoutPositionalArg.length
(missingParams, allPositional)
}
@@ -407,7 +426,7 @@ trait NamesDefaults { self: Analyzer =>
previousArgss: List[List[Tree]], params: List[Symbol],
pos: scala.reflect.internal.util.Position, context: Context): (List[Tree], List[Symbol]) = {
if (givenArgs.length < params.length) {
- val (missing, positional) = missingParams(givenArgs, params)
+ val (missing, positional) = missingParams(givenArgs, params, nameOfNamedArg)
if (missing forall (_.hasDefault)) {
val defaultArgs = missing flatMap (p => {
val defGetter = defaultGetter(p, context)
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
index cf3f265f0c..da0e67a2a5 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
@@ -261,7 +261,7 @@ trait PatternTypers {
def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = {
def duplErrTree = setError(treeCopy.Apply(tree, fun0, args))
- def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree }
+ def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree }
if (args.length > MaxTupleArity)
return duplErrorTree(TooManyArgsPatternError(fun))
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 1b3da26bf2..1cb775959d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -167,7 +167,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
}
// This has become noisy with implicit classes.
- if (settings.lint && settings.developer) {
+ if (settings.warnPolyImplicitOverload && settings.developer) {
clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym =>
// implicit classes leave both a module symbol and a method symbol as residue
val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule)
@@ -954,7 +954,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
def apply(tp: Type) = mapOver(tp).normalize
}
- def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint) (fn, args) match {
+ def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.warnOptionImplicit) (fn, args) match {
case (tap@TypeApply(fun, targs), List(view: ApplyImplicitView)) if fun.symbol == currentRun.runDefinitions.Option_apply =>
reporter.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567
case _ =>
@@ -1320,7 +1320,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
&& !qual.tpe.isInstanceOf[ThisType]
&& sym.accessedOrSelf.isVal
)
- if (settings.lint.value && isLikelyUninitialized)
+ if (settings.warnDelayedInit && isLikelyUninitialized)
reporter.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value")
}
@@ -1387,7 +1387,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
private def checkByNameRightAssociativeDef(tree: DefDef) {
tree match {
case DefDef(_, name, _, params :: _, _, _) =>
- if (settings.lint && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol)))
+ if (settings.warnByNameRightAssociative && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol)))
reporter.warning(tree.pos,
"by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.")
case _ =>
@@ -1633,7 +1633,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
if (settings.warnNullaryUnit)
checkNullaryMethodReturnType(sym)
if (settings.warnInaccessible) {
- if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic)
+ if (!sym.isConstructor && !sym.isEffectivelyFinalOrNotOverridden && !sym.isSynthetic)
checkAccessibilityOfReferencedTypes(tree)
}
tree match {
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index d3a41b9570..38b00a015b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -247,7 +247,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
// also exists in a superclass, because they may be surprised
// to find out that a constructor parameter will shadow a
// field. See SI-4762.
- if (settings.lint) {
+ if (settings.warnPrivateShadow) {
if (sym.isPrivateLocal && sym.paramss.isEmpty) {
qual.symbol.ancestors foreach { parent =>
parent.info.decls filterNot (x => x.isPrivate || x.isLocalToThis) foreach { m2 =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index 9516f94135..d0237fb468 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -339,12 +339,11 @@ trait SyntheticMethods extends ast.TreeDSL {
!hasOverridingImplementation(m) || {
clazz.isDerivedValueClass && (m == Any_hashCode || m == Any_equals) && {
// Without a means to suppress this warning, I've thought better of it.
- //
- // if (settings.lint) {
- // (clazz.info nonPrivateMember m.name) filter (m => (m.owner != AnyClass) && (m.owner != clazz) && !m.isDeferred) andAlso { m =>
- // currentUnit.warning(clazz.pos, s"Implementation of ${m.name} inherited from ${m.owner} overridden in $clazz to enforce value class semantics")
- // }
- // }
+ if (settings.warnValueOverrides) {
+ (clazz.info nonPrivateMember m.name) filter (m => (m.owner != AnyClass) && (m.owner != clazz) && !m.isDeferred) andAlso { m =>
+ currentUnit.warning(clazz.pos, s"Implementation of ${m.name} inherited from ${m.owner} overridden in $clazz to enforce value class semantics")
+ }
+ }
true
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
index 399a4ca8d5..743bbe53bd 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
@@ -208,8 +208,7 @@ abstract class TreeCheckers extends Analyzer {
}
def check(unit: CompilationUnit) {
informProgress("checking "+unit)
- val context = rootContext(unit)
- context.checking = true
+ val context = rootContext(unit, checking = true)
tpeOfTree.clear()
SymbolTracker.check(phase, unit)
val checker = new TreeChecker(context)
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index 7440f69e93..8d29d28908 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -572,11 +572,11 @@ trait TypeDiagnostics {
} else f
}
def apply(tree: Tree): Tree = {
- // Error suppression will squash some of these warnings unless we circumvent it.
+ // Error suppression (in context.warning) would squash some of these warnings.
// It is presumed if you are using a -Y option you would really like to hear
- // the warnings you've requested.
+ // the warnings you've requested; thus, use reporter.warning.
if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && exprOK)
- context.warning(tree.pos, "dead code following this construct", force = true)
+ reporter.warning(tree.pos, "dead code following this construct")
tree
}
@@ -600,6 +600,23 @@ trait TypeDiagnostics {
)
}
+ // warn about class/method/type-members' type parameters that shadow types already in scope
+ def warnTypeParameterShadow(tparams: List[TypeDef], sym: Symbol): Unit =
+ if (settings.warnTypeParameterShadow && !isPastTyper && !sym.isSynthetic) {
+ def enclClassOrMethodOrTypeMember(c: Context): Context =
+ if (!c.owner.exists || c.owner.isClass || c.owner.isMethod || (c.owner.isType && !c.owner.isParameter)) c
+ else enclClassOrMethodOrTypeMember(c.outer)
+
+ val tt = tparams.filter(_.name != typeNames.WILDCARD).foreach { tp =>
+ // we don't care about type params shadowing other type params in the same declaration
+ enclClassOrMethodOrTypeMember(context).outer.lookupSymbol(tp.name, s => s != tp.symbol && s.hasRawInfo && reallyExists(s)) match {
+ case LookupSucceeded(_, sym2) => context.warning(tp.pos,
+ s"type parameter ${tp.name} defined in $sym shadows $sym2 defined in ${sym2.owner}. You may want to rename your type parameter, or possibly remove it.")
+ case _ =>
+ }
+ }
+ }
+
/** Report a type error.
*
* @param pos The position where to report the error
@@ -629,7 +646,7 @@ trait TypeDiagnostics {
throw new FatalError("cannot redefine root "+sym)
}
case _ =>
- context0.error(ex.pos, ex)
+ context0.error(ex.pos, ex.msg)
}
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 2c31ef2e8e..422b940cd3 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -155,21 +155,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
} else {
mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args
if (!param.hasDefault && !paramFailed) {
- context.reportBuffer.errors.collectFirst {
- case dte: DivergentImplicitTypeError => dte
- } match {
- case Some(divergent) =>
- // DivergentImplicit error has higher priority than "no implicit found"
- // no need to issue the problem again if we are still in silent mode
- if (context.reportErrors) {
- context.issue(divergent.withPt(paramTp))
- context.reportBuffer.clearErrors {
- case dte: DivergentImplicitTypeError => true
- }
- }
- case _ =>
- NoImplicitFoundError(fun, param)
- }
+ context.reporter.reportFirstDivergentError(fun, param, paramTp)(context)
paramFailed = true
}
/* else {
@@ -478,20 +464,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (cond) typerWithLocalContext(c)(f) else f(this)
@inline
- final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = {
- val res = f(newTyper(c))
- if (c.hasErrors)
- context.updateBuffer(c.flushAndReturnBuffer())
- res
- }
-
- @inline
- final def withSavedContext[T](c: Context)(f: => T) = {
- val savedErrors = c.flushAndReturnBuffer()
- val res = f
- c.updateBuffer(savedErrors)
- res
- }
+ final def typerWithLocalContext[T](c: Context)(f: Typer => T): T =
+ c.reporter.propagatingErrorsTo(context.reporter)(f(newTyper(c)))
/** The typer for a label definition. If this is part of a template we
* first have to enter the label definition.
@@ -684,6 +658,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (Statistics.canEnable) Statistics.stopCounter(subtypeFailed, subtypeStart)
if (Statistics.canEnable) Statistics.stopTimer(failedSilentNanos, failedSilentStart)
}
+ @inline def wrapResult(reporter: ContextReporter, result: T) =
+ if (reporter.hasErrors) {
+ stopStats()
+ SilentTypeError(reporter.errors: _*)
+ } else SilentResultValue(result)
+
try {
if (context.reportErrors ||
reportAmbiguousErrors != context.ambiguousErrors ||
@@ -697,20 +677,17 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
context.undetparams = context1.undetparams
context.savedTypeBounds = context1.savedTypeBounds
context.namedApplyBlockInfo = context1.namedApplyBlockInfo
- if (context1.hasErrors) {
- stopStats()
- SilentTypeError(context1.errors: _*)
- } else {
- // If we have a successful result, emit any warnings it created.
- context1.flushAndIssueWarnings()
- SilentResultValue(result)
- }
+
+ // If we have a successful result, emit any warnings it created.
+ if (!context1.reporter.hasErrors)
+ context1.reporter.emitWarnings()
+
+ wrapResult(context1.reporter, result)
} else {
assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer")
- withSavedContext(context){
- val res = op(this)
- val errorsToReport = context.flushAndReturnBuffer()
- if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head)
+
+ context.reporter.withFreshErrorBuffer {
+ wrapResult(context.reporter, op(this))
}
}
} catch {
@@ -816,14 +793,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
// avoid throwing spurious DivergentImplicit errors
- if (context.hasErrors)
+ if (context.reporter.hasErrors)
setError(tree)
else
withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree))(typer1 =>
if (original != EmptyTree && pt != WildcardType) (
typer1 silent { tpr =>
val withImplicitArgs = tpr.applyImplicitArgs(tree)
- if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway
+ if (tpr.context.reporter.hasErrors) tree // silent will wrap it in SilentTypeError anyway
else tpr.typed(withImplicitArgs, mode, pt)
}
orElse { _ =>
@@ -1057,7 +1034,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val silentContext = context.makeImplicit(context.ambiguousErrors)
val res = newTyper(silentContext).typed(
new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt)
- silentContext.firstError match {
+ silentContext.reporter.firstError match {
case Some(err) => context.issue(err)
case None => return res
}
@@ -1749,6 +1726,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
|you want, you must write the annotation class in Java.""".stripMargin)
}
+ warnTypeParameterShadow(tparams1, clazz)
+
if (!isPastTyper) {
for (ann <- clazz.getAnnotation(DeprecatedAttr)) {
val m = companionSymbolOf(clazz, context)
@@ -2151,6 +2130,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val tparams1 = ddef.tparams mapConserve typedTypeDef
val vparamss1 = ddef.vparamss mapConserve (_ mapConserve typedValDef)
+ warnTypeParameterShadow(tparams1, meth)
+
meth.annotations.map(_.completeInfo())
for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1)
@@ -2227,6 +2208,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val typedMods = typedModifiers(tdef.mods)
tdef.symbol.annotations.map(_.completeInfo())
+ warnTypeParameterShadow(tparams1, tdef.symbol)
+
// @specialized should not be pickled when compiling with -no-specialize
if (settings.nospecialization && currentRun.compiles(tdef.symbol)) {
tdef.symbol.removeAnnotation(definitions.SpecializedClass)
@@ -2990,7 +2973,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
ConstructorsOrderError(stat)
}
- if (treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos,
+ if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos,
"a pure expression does nothing in statement position; " +
"you may be omitting necessary parentheses"
)
@@ -3149,7 +3132,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = {
// TODO_NMT: check the assumption that args nonEmpty
def duplErrTree = setError(treeCopy.Apply(tree, fun0, args))
- def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree }
+ def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree }
def preSelectOverloaded(fun: Tree): Tree = {
if (fun.hasSymbolField && fun.symbol.isOverloaded) {
@@ -3229,7 +3212,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
(arg1, arg1.tpe.deconst)
}.unzip
}
- if (context.hasErrors)
+ if (context.reporter.hasErrors)
setError(tree)
else {
inferMethodAlternative(fun, undetparams, argTpes, pt)
@@ -3248,25 +3231,25 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* to that. This is the last thing which is tried (after
* default arguments)
*/
- def tryTupleApply: Tree = (
+ def tryTupleApply: Tree = {
if (eligibleForTupleConversion(paramTypes, argslen) && !phase.erasedTypes) {
val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args)))
// expected one argument, but got 0 or >1 ==> try applying to tuple
// the inner "doTypedApply" does "extractUndetparams" => restore when it fails
val savedUndetparams = context.undetparams
silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) map { t =>
- // Depending on user options, may warn or error here if
- // a Unit or tuple was inserted.
- val keepTree = (
- !mode.typingExprNotFun
- || t.symbol == null
- || checkValidAdaptation(t, args)
- )
- if (keepTree) t else EmptyTree
+ // Depending on user options, may warn or error here if
+ // a Unit or tuple was inserted.
+ val keepTree = (
+ !mode.typingExprNotFun // why? introduced in 4e488a60, doc welcome
+ || t.symbol == null // ditto
+ || checkValidAdaptation(t, args)
+ )
+ if (keepTree) t else EmptyTree
} orElse { _ => context.undetparams = savedUndetparams ; EmptyTree }
}
else EmptyTree
- )
+ }
/* Treats an application which uses named or default arguments.
* Also works if names + a vararg used: when names are used, the vararg
@@ -3357,8 +3340,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
duplErrTree
} else if (lencmp2 == 0) {
// useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]()
- val note = "Error occurred in an application involving default arguments."
- if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic
+ context.diagUsedDefaults = true
doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)
} else {
rollbackNamesDefaultsOwnerChanges()
@@ -4331,7 +4313,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
c.retyping = true
try {
val res = newTyper(c).typedArgs(args, mode)
- if (c.hasErrors) None else Some(res)
+ if (c.reporter.hasErrors) None else Some(res)
} catch {
case ex: CyclicReference =>
throw ex
@@ -4395,7 +4377,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ => ()
}
}
- typeErrors foreach issue
+ typeErrors foreach context.issue
setError(treeCopy.Apply(tree, fun, args))
}
@@ -4449,7 +4431,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
doTypedApply(tree, fun2, args, mode, pt)
case err: SilentTypeError =>
onError({
- err.reportableErrors foreach issue
+ err.reportableErrors foreach context.issue
args foreach (arg => typed(arg, mode, ErrorType))
setError(tree)
})
@@ -4686,7 +4668,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
else
// before failing due to access, try a dynamic call.
asDynamicCall getOrElse {
- issue(accessibleError.get)
+ context.issue(accessibleError.get)
setError(tree)
}
case _ =>
@@ -5099,7 +5081,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def isPlausible(m: Symbol) = m.alternatives exists (m => requiresNoArgs(m.info))
def maybeWarn(s: String): Unit = {
- def warn(message: String) = context.warning(lit.pos, s"$message Did you forget the interpolator?")
+ def warn(message: String) = context.warning(lit.pos, s"possible missing interpolator: $message")
def suspiciousSym(name: TermName) = context.lookupSymbol(name, _ => true).symbol
def suspiciousExpr = InterpolatorCodeRegex findFirstIn s
def suspiciousIdents = InterpolatorIdentRegex findAllIn s map (s => suspiciousSym(s drop 1))
@@ -5107,9 +5089,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// heuristics - no warning on e.g. a string with only "$asInstanceOf"
if (s contains ' ') (
if (suspiciousExpr.nonEmpty)
- warn("That looks like an interpolated expression!") // "${...}"
+ warn("detected an interpolated expression") // "${...}"
else
- suspiciousIdents find isPlausible foreach (sym => warn(s"`$$${sym.name}` looks like an interpolated identifier!")) // "$id"
+ suspiciousIdents find isPlausible foreach (sym => warn(s"detected interpolated identifier `$$${sym.name}`")) // "$id"
)
}
lit match {
@@ -5119,7 +5101,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
def typedLiteral(tree: Literal) = {
- if (settings.lint) warnMissingInterpolator(tree)
+ if (settings.warnMissingInterpolator) warnMissingInterpolator(tree)
tree setType (if (tree.value.tag == UnitTag) UnitTpe else ConstantType(tree.value))
}
diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala
index 57be1afdfe..b445f1e2bb 100644
--- a/src/compiler/scala/tools/reflect/FormatInterpolator.scala
+++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala
@@ -182,13 +182,23 @@ abstract class FormatInterpolator {
case (part, n) => copyPart(part, n)
}
- //q"{..$evals; ${fstring.toString}.format(..$ids)}"
- locally {
+ //q"{..$evals; new StringOps(${fstring.toString}).format(..$ids)}"
+ val format = fstring.toString
+ if (ids.isEmpty && !format.contains("%")) Literal(Constant(format))
+ else {
+ val scalaPackage = Select(Ident(nme.ROOTPKG), TermName("scala"))
+ val newStringOps = Select(
+ New(Select(Select(Select(scalaPackage,
+ TermName("collection")), TermName("immutable")), TypeName("StringOps"))),
+ termNames.CONSTRUCTOR
+ )
val expr =
Apply(
Select(
- Literal(Constant(fstring.toString)),
- newTermName("format")),
+ Apply(
+ newStringOps,
+ List(Literal(Constant(format)))),
+ TermName("format")),
ids.toList
)
val p = c.macroApplication.pos
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index 923297bafb..1643e0061f 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -142,7 +142,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works
phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled
globalPhase = run.typerPhase // amazing... looks like phase and globalPhase are different things, so we need to set them separately
- currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions
+ currentTyper.context.initRootContext() // need to manually set context mode, otherwise typer.silent will throw exceptions
reporter.reset()
val expr3 = withContext(transform(currentTyper, expr2))
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
index b68022afd9..392b7fc881 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -69,9 +69,16 @@ trait Parsers { self: Quasiquotes =>
override def makeTupleType(trees: List[Tree]): Tree = TupleTypePlaceholder(trees)
// q"{ $x }"
- override def makeBlock(stats: List[Tree]): Tree = stats match {
- case (head @ Ident(name)) :: Nil if isHole(name) => Block(Nil, head)
- case _ => super.makeBlock(stats)
+ override def makeBlock(stats: List[Tree]): Tree = method match {
+ case nme.apply =>
+ stats match {
+ // we don't want to eagerly flatten trees with placeholders as they
+ // might have to be wrapped into a block depending on their value
+ case (head @ Ident(name)) :: Nil if isHole(name) => Block(Nil, head)
+ case _ => gen.mkBlock(stats, doFlatten = true)
+ }
+ case nme.unapply => gen.mkBlock(stats, doFlatten = false)
+ case other => global.abort("unreachable")
}
// tq"$a => $b"