summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/ant/Scalac.scala2
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala2
-rw-r--r--src/compiler/scala/tools/nsc/GenericRunnerCommand.scala1
-rw-r--r--src/compiler/scala/tools/nsc/GenericRunnerSettings.scala8
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala8
-rw-r--r--src/compiler/scala/tools/nsc/Reporting.scala44
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala51
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala140
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala18
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala75
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala74
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala17
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala85
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala33
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala35
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala16
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala67
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala9
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala38
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala8
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala14
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala13
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala81
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala88
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala37
-rw-r--r--src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala12
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaParsers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaScanners.scala4
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala91
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala74
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala15
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala33
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala19
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Adaptations.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala48
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala7
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala42
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala110
-rw-r--r--src/compiler/scala/tools/nsc/util/StackTracing.scala2
-rw-r--r--src/compiler/scala/tools/reflect/FormatInterpolator.scala2
55 files changed, 954 insertions, 606 deletions
diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala
index 930163af36..e9d1dfe4d2 100644
--- a/src/compiler/scala/tools/ant/Scalac.scala
+++ b/src/compiler/scala/tools/ant/Scalac.scala
@@ -575,8 +575,6 @@ class Scalac extends ScalaMatchingTask with ScalacShared {
settings.classpath.value = asString(getClasspath)
if (!sourcepath.isEmpty)
settings.sourcepath.value = asString(getSourcepath)
- else if (origin.get.size() > 0)
- settings.sourcepath.value = origin.get.list()(0)
if (!bootclasspath.isEmpty)
settings.bootclasspath.value = asString(getBootclasspath)
if (!extdirs.isEmpty) settings.extdirs.value = asString(getExtdirs)
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index 34b07a2651..02d9b0d913 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -128,7 +128,7 @@ trait CompilationUnits { global: Global =>
final def warning(pos: Position, msg: String): Unit = reporter.warning(pos, msg)
@deprecated("Call global.currentRun.reporting.deprecationWarning directly instead.", "2.11.2")
- final def deprecationWarning(pos: Position, msg: String): Unit = currentRun.reporting.deprecationWarning(pos, msg)
+ final def deprecationWarning(pos: Position, msg: String, since: String): Unit = currentRun.reporting.deprecationWarning(pos, msg, since)
@deprecated("Call global.currentRun.reporting.uncheckedWarning directly instead.", "2.11.2")
final def uncheckedWarning(pos: Position, msg: String): Unit = currentRun.reporting.uncheckedWarning(pos, msg)
diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
index 24496fa013..bab612bad5 100644
--- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
+++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala
@@ -79,6 +79,7 @@ Other startup options:
-howtorun what to run <script|object|jar|guess> (default: guess)
-i <file> preload <file> before starting the repl
+ -I <file> preload <file>, enforcing line-by-line interpretation
-e <string> execute <string> as if entered in the repl
-save save the compiled script in a jar for future use
-nc no compilation daemon: do not use the fsc offline compiler
diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
index c82ed68da8..113c02e558 100644
--- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
+++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala
@@ -21,9 +21,15 @@ class GenericRunnerSettings(error: String => Unit) extends Settings(error) {
val loadfiles =
MultiStringSetting(
+ "-I",
+ "file",
+ "load a file line-by-line")
+
+ val pastefiles =
+ MultiStringSetting(
"-i",
"file",
- "load a file (assumes the code is given interactively)")
+ "paste a file")
val execute =
StringSetting(
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 7417d9c09d..9d6693c00f 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -1054,9 +1054,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
var currentUnit: CompilationUnit = NoCompilationUnit
// used in sbt
- def uncheckedWarnings: List[(Position, String)] = reporting.uncheckedWarnings
+ def uncheckedWarnings: List[(Position, String)] = reporting.uncheckedWarnings.map{case (pos, (msg, since)) => (pos, msg)}
// used in sbt
- def deprecationWarnings: List[(Position, String)] = reporting.deprecationWarnings
+ def deprecationWarnings: List[(Position, String)] = reporting.deprecationWarnings.map{case (pos, (msg, since)) => (pos, msg)}
private class SyncedCompilationBuffer { self =>
private val underlying = new mutable.ArrayBuffer[CompilationUnit]
@@ -1267,11 +1267,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
private def warnDeprecatedAndConflictingSettings(unit: CompilationUnit) {
// issue warnings for any usage of deprecated settings
settings.userSetSettings filter (_.isDeprecated) foreach { s =>
- currentRun.reporting.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get)
+ currentRun.reporting.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get, "")
}
val supportedTarget = "jvm-1.8"
if (settings.target.value != supportedTarget) {
- currentRun.reporting.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated and has no effect, setting to " + supportedTarget)
+ currentRun.reporting.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated and has no effect, setting to " + supportedTarget, "2.12.0")
settings.target.value = supportedTarget
}
settings.conflictWarning.foreach(reporter.warning(NoPosition, _))
diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala
index 5bdbf4bb6a..01c583bea3 100644
--- a/src/compiler/scala/tools/nsc/Reporting.scala
+++ b/src/compiler/scala/tools/nsc/Reporting.scala
@@ -30,17 +30,33 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
def this(what: String, booleanSetting: Settings#BooleanSetting) {
this(what, () => booleanSetting, booleanSetting)
}
- val warnings = mutable.LinkedHashMap[Position, String]()
- def warn(pos: Position, msg: String) =
+ val warnings = mutable.LinkedHashMap[Position, (String, String)]()
+ def warn(pos: Position, msg: String, since: String = "") =
if (doReport()) reporter.warning(pos, msg)
- else if (!(warnings contains pos)) warnings += ((pos, msg))
+ else if (!(warnings contains pos)) warnings += ((pos, (msg, since)))
def summarize() =
if (warnings.nonEmpty && (setting.isDefault || doReport())) {
- val numWarnings = warnings.size
- val warningVerb = if (numWarnings == 1) "was" else "were"
- val warningCount = countElementsAsString(numWarnings, s"$what warning")
-
- reporter.warning(NoPosition, s"there $warningVerb $warningCount; re-run with ${setting.name} for details")
+ val sinceAndAmount = mutable.TreeMap[String, Int]()
+ warnings.valuesIterator.foreach { case (_, since) =>
+ val value = sinceAndAmount.get(since)
+ if (value.isDefined) sinceAndAmount += ((since, value.get + 1))
+ else sinceAndAmount += ((since, 1))
+ }
+ val deprecationSummary = sinceAndAmount.size > 1
+ sinceAndAmount.foreach { case (since, amount) =>
+ val numWarnings = amount
+ val warningsSince = if (since.nonEmpty) s" (since $since)" else ""
+ val warningVerb = if (numWarnings == 1) "was" else "were"
+ val warningCount = countElementsAsString(numWarnings, s"$what warning")
+ val rerun = if (deprecationSummary) "" else s"; re-run with ${setting.name} for details"
+ reporter.warning(NoPosition, s"there $warningVerb $warningCount$warningsSince$rerun")
+ }
+ if (deprecationSummary) {
+ val numWarnings = warnings.size
+ val warningVerb = if (numWarnings == 1) "was" else "were"
+ val warningCount = countElementsAsString(numWarnings, s"$what warning")
+ reporter.warning(NoPosition, s"there $warningVerb $warningCount in total; re-run with ${setting.name} for details")
+ }
}
}
@@ -49,11 +65,11 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
private val _deprecationWarnings = new ConditionalWarning("deprecation", settings.deprecation)
private val _uncheckedWarnings = new ConditionalWarning("unchecked", settings.unchecked)
private val _featureWarnings = new ConditionalWarning("feature", settings.feature)
- private val _inlinerWarnings = new ConditionalWarning("inliner", () => !settings.YoptWarningsSummaryOnly, settings.YoptWarnings)
+ private val _inlinerWarnings = new ConditionalWarning("inliner", () => !settings.optWarningsSummaryOnly, settings.optWarnings)
private val _allConditionalWarnings = List(_deprecationWarnings, _uncheckedWarnings, _featureWarnings, _inlinerWarnings)
// TODO: remove in favor of the overload that takes a Symbol, give that argument a default (NoSymbol)
- def deprecationWarning(pos: Position, msg: String): Unit = _deprecationWarnings.warn(pos, msg)
+ def deprecationWarning(pos: Position, msg: String, since: String): Unit = _deprecationWarnings.warn(pos, msg, since)
def uncheckedWarning(pos: Position, msg: String): Unit = _uncheckedWarnings.warn(pos, msg)
def featureWarning(pos: Position, msg: String): Unit = _featureWarnings.warn(pos, msg)
def inlinerWarning(pos: Position, msg: String): Unit = _inlinerWarnings.warn(pos, msg)
@@ -66,10 +82,12 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
def allConditionalWarnings = _allConditionalWarnings flatMap (_.warnings)
// behold! the symbol that caused the deprecation warning (may not be deprecated itself)
- def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit = _deprecationWarnings.warn(pos, msg)
+ def deprecationWarning(pos: Position, sym: Symbol, msg: String, since: String): Unit = _deprecationWarnings.warn(pos, msg, since)
def deprecationWarning(pos: Position, sym: Symbol): Unit = {
- val suffix = sym.deprecationMessage match { case Some(msg) => ": "+ msg case _ => "" }
- deprecationWarning(pos, sym, s"$sym${sym.locationString} is deprecated$suffix")
+ val version = sym.deprecationVersion.getOrElse("")
+ val since = if (version.isEmpty) version else s" (since $version)"
+ val message = sym.deprecationMessage match { case Some(msg) => s": $msg" case _ => "" }
+ deprecationWarning(pos, sym, s"$sym${sym.locationString} is deprecated$since$message", version)
}
private[this] var reportedFeature = Set[Symbol]()
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 0786ceb7c2..bc89609a59 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -238,7 +238,8 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
* (outside the synchronized block).
*
* The idiom works only if the condition is using a volatile field.
- * @see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
+ *
+ * @see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
*/
def mkSynchronizedCheck(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree =
mkSynchronizedCheck(mkAttributedThis(clazz), cond, syncBody, stats)
@@ -274,8 +275,19 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
}
// used to create the lifted method that holds a function's body
- def mkLiftedFunctionBodyMethod(localTyper: analyzer.Typer)(owner: Symbol, fun: Function) =
- mkMethodForFunctionBody(localTyper)(owner, fun, nme.ANON_FUN_NAME)(additionalFlags = ARTIFACT)
+ def mkLiftedFunctionBodyMethod(localTyper: global.analyzer.Typer)(owner: global.Symbol, fun: global.Function) = {
+ def nonLocalEnclosingMember(sym: Symbol): Symbol = {
+ if (sym.isLocalDummy) sym.enclClass.primaryConstructor
+ else if (sym.isLocalToBlock) nonLocalEnclosingMember(sym.originalOwner)
+ else sym
+ }
+ val ownerName = nonLocalEnclosingMember(fun.symbol.originalOwner).name match {
+ case nme.CONSTRUCTOR => nme.NEWkw // do as javac does for the suffix, prefer "new" to "$lessinit$greater$1"
+ case x => x.dropLocal
+ }
+ val newName = nme.ANON_FUN_NAME.append(nme.NAME_JOIN_STRING).append(ownerName)
+ mkMethodForFunctionBody(localTyper)(owner, fun, newName)(additionalFlags = ARTIFACT)
+ }
/**
@@ -310,6 +322,39 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
newDefDef(methSym, moveToMethod(useMethodParams(fun.body)))(tpt = TypeTree(resTp))
}
+ /**
+ * Create a new `DefDef` based on `orig` with an explicit self parameter.
+ *
+ * Details:
+ * - Must by run after erasure
+ * - If `maybeClone` is the identity function, this runs "in place"
+ * and mutates the symbol of `orig`. `orig` should be discarded
+ * - Symbol owners and returns are substituted, as are parameter symbols
+ * - Recursive calls are not rewritten. This is correct if we assume
+ * that we either:
+ * - are in "in-place" mode, but can guarantee that no recursive calls exists
+ * - are associating the RHS with a cloned symbol, but intend for the original
+ * method to remain and for recursive calls to target it.
+ */
+ final def mkStatic(orig: DefDef, newName: Name, maybeClone: Symbol => Symbol): DefDef = {
+ assert(phase.erasedTypes, phase)
+ assert(!orig.symbol.hasFlag(SYNCHRONIZED), orig.symbol.defString)
+ val origSym = orig.symbol
+ val origParams = orig.symbol.info.params
+ val newSym = maybeClone(orig.symbol)
+ newSym.setName(newName)
+ newSym.setFlag(STATIC)
+ // Add an explicit self parameter
+ val selfParamSym = newSym.newSyntheticValueParam(newSym.owner.typeConstructor, nme.SELF).setFlag(ARTIFACT)
+ newSym.updateInfo(newSym.info match {
+ case mt @ MethodType(params, res) => copyMethodType(mt, selfParamSym :: params, res)
+ })
+ val selfParam = ValDef(selfParamSym)
+ val rhs = orig.rhs.substituteThis(newSym.owner, atPos(newSym.pos)(gen.mkAttributedIdent(selfParamSym)))
+ .substituteSymbols(origParams, newSym.info.params.drop(1)).changeOwner(origSym -> newSym)
+ treeCopy.DefDef(orig, orig.mods, orig.name, orig.tparams, (selfParam :: orig.vparamss.head) :: Nil, orig.tpt, rhs).setSymbol(newSym)
+ }
+
// TODO: the rewrite to AbstractFunction is superfluous once we compile FunctionN to a SAM type (aka functional interface)
def functionClassType(fun: Function): Type =
if (isFunctionType(fun.tpe)) abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.body.tpe.deconst)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index 9c0174d89b..cf66e0a7dc 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -39,7 +39,7 @@ trait ParsersCommon extends ScannersCommon { self =>
*/
abstract class ParserCommon {
val in: ScannerCommon
- def deprecationWarning(off: Offset, msg: String): Unit
+ def deprecationWarning(off: Offset, msg: String, since: String): Unit
def accept(token: Token): Int
/** Methods inParensOrError and similar take a second argument which, should
@@ -154,7 +154,7 @@ self =>
// suppress warnings; silent abort on errors
def warning(offset: Offset, msg: String): Unit = ()
- def deprecationWarning(offset: Offset, msg: String): Unit = ()
+ def deprecationWarning(offset: Offset, msg: String, since: 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)
@@ -206,8 +206,8 @@ self =>
override def warning(offset: Offset, msg: String): Unit =
reporter.warning(o2p(offset), msg)
- override def deprecationWarning(offset: Offset, msg: String): Unit =
- currentRun.reporting.deprecationWarning(o2p(offset), msg)
+ override def deprecationWarning(offset: Offset, msg: String, since: String): Unit =
+ currentRun.reporting.deprecationWarning(o2p(offset), msg, since)
private var smartParsing = false
@inline private def withSmartParsing[T](body: => T): T = {
@@ -364,12 +364,15 @@ self =>
val stmts = parseStats()
def mainModuleName = newTermName(settings.script.value)
+
/* If there is only a single object template in the file and it has a
* suitable main method, we will use it rather than building another object
* around it. Since objects are loaded lazily the whole script would have
* been a no-op, so we're not taking much liberty.
*/
- def searchForMain(): Option[Tree] = {
+ def searchForMain(): Tree = {
+ import PartialFunction.cond
+
/* Have to be fairly liberal about what constitutes a main method since
* nothing has been typed yet - for instance we can't assume the parameter
* type will look exactly like "Array[String]" as it could have been renamed
@@ -379,11 +382,15 @@ self =>
case DefDef(_, nme.main, Nil, List(_), _, _) => true
case _ => false
}
- /* For now we require there only be one top level object. */
+ def isApp(t: Tree) = t match {
+ case Template(parents, _, _) => parents.exists(cond(_) { case Ident(tpnme.App) => true })
+ case _ => false
+ }
+ /* We allow only one main module. */
var seenModule = false
- val newStmts = stmts collect {
- case t @ Import(_, _) => t
- case md @ ModuleDef(mods, name, template) if !seenModule && (md exists isMainMethod) =>
+ var disallowed = EmptyTree: Tree
+ val newStmts = stmts.map {
+ case md @ ModuleDef(mods, name, template) if !seenModule && (isApp(template) || md.exists(isMainMethod)) =>
seenModule = true
/* This slightly hacky situation arises because we have no way to communicate
* back to the scriptrunner what the name of the program is. Even if we were
@@ -394,50 +401,63 @@ self =>
*/
if (name == mainModuleName) md
else treeCopy.ModuleDef(md, mods, mainModuleName, template)
- case _ =>
+ case md @ ModuleDef(_, _, _) => md
+ case cd @ ClassDef(_, _, _, _) => cd
+ case t @ Import(_, _) => t
+ case t =>
/* If we see anything but the above, fail. */
- return None
+ if (disallowed.isEmpty) disallowed = t
+ EmptyTree
+ }
+ if (disallowed.isEmpty) makeEmptyPackage(0, newStmts)
+ else {
+ if (seenModule)
+ warning(disallowed.pos.point, "Script has a main object but statement is disallowed")
+ EmptyTree
}
- Some(makeEmptyPackage(0, newStmts))
}
- if (mainModuleName == newTermName(ScriptRunner.defaultScriptMain))
- searchForMain() foreach { return _ }
+ def mainModule: Tree =
+ if (mainModuleName == newTermName(ScriptRunner.defaultScriptMain)) searchForMain() else EmptyTree
- /* Here we are building an AST representing the following source fiction,
- * where `moduleName` is from -Xscript (defaults to "Main") and <stmts> are
- * the result of parsing the script file.
- *
- * {{{
- * object moduleName {
- * def main(args: Array[String]): Unit =
- * new AnyRef {
- * stmts
- * }
- * }
- * }}}
- */
- def emptyInit = DefDef(
- NoMods,
- nme.CONSTRUCTOR,
- Nil,
- ListOfNil,
- TypeTree(),
- Block(List(Apply(gen.mkSuperInitCall, Nil)), literalUnit)
- )
+ def repackaged: Tree = {
+ /* Here we are building an AST representing the following source fiction,
+ * where `moduleName` is from -Xscript (defaults to "Main") and <stmts> are
+ * the result of parsing the script file.
+ *
+ * {{{
+ * object moduleName {
+ * def main(args: Array[String]): Unit =
+ * new AnyRef {
+ * stmts
+ * }
+ * }
+ * }}}
+ */
+ def emptyInit = DefDef(
+ NoMods,
+ nme.CONSTRUCTOR,
+ Nil,
+ ListOfNil,
+ TypeTree(),
+ Block(List(Apply(gen.mkSuperInitCall, Nil)), literalUnit)
+ )
- // def main
- def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String)))
- def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.args, mainParamType, EmptyTree))
- def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), gen.mkAnonymousNew(stmts))
+ // def main
+ def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String)))
+ def mainParameter = List(ValDef(Modifiers(Flags.PARAM), nme.args, mainParamType, EmptyTree))
+ def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), gen.mkAnonymousNew(stmts))
- // object Main
- def moduleName = newTermName(ScriptRunner scriptMain settings)
- def moduleBody = Template(atInPos(scalaAnyRefConstr) :: Nil, noSelfType, List(emptyInit, mainDef))
- def moduleDef = ModuleDef(NoMods, moduleName, moduleBody)
+ // object Main
+ def moduleName = newTermName(ScriptRunner scriptMain settings)
+ def moduleBody = Template(atInPos(scalaAnyRefConstr) :: Nil, noSelfType, List(emptyInit, mainDef))
+ def moduleDef = ModuleDef(NoMods, moduleName, moduleBody)
- // package <empty> { ... }
- makeEmptyPackage(0, moduleDef :: Nil)
+ // package <empty> { ... }
+ makeEmptyPackage(0, moduleDef :: Nil)
+ }
+
+ mainModule orElse repackaged
}
/* --------------- PLACEHOLDERS ------------------------------------------- */
@@ -1822,7 +1842,7 @@ self =>
val hasEq = in.token == EQUALS
if (hasVal) {
- if (hasEq) deprecationWarning(in.offset, "val keyword in for comprehension is deprecated")
+ if (hasEq) deprecationWarning(in.offset, "val keyword in for comprehension is deprecated", "2.10.0")
else syntaxError(in.offset, "val in for comprehension must be followed by assignment")
}
@@ -1901,19 +1921,20 @@ self =>
}
/** {{{
- * Pattern1 ::= varid `:' TypePat
+ * Pattern1 ::= boundvarid `:' TypePat
* | `_' `:' TypePat
* | Pattern2
- * SeqPattern1 ::= varid `:' TypePat
+ * SeqPattern1 ::= boundvarid `:' TypePat
* | `_' `:' TypePat
* | [SeqPattern2]
* }}}
*/
def pattern1(): Tree = pattern2() match {
case p @ Ident(name) if in.token == COLON =>
- if (treeInfo.isVarPattern(p))
+ if (nme.isVariableName(name)) {
+ p.removeAttachment[BackquotedIdentifierAttachment.type]
atPos(p.pos.start, in.skipToken())(Typed(p, compoundType()))
- else {
+ } else {
syntaxError(in.offset, "Pattern variables must start with a lower-case letter. (SLS 8.1.1.)")
p
}
@@ -1921,10 +1942,9 @@ self =>
}
/** {{{
- * Pattern2 ::= varid [ @ Pattern3 ]
+ * Pattern2 ::= id @ Pattern3
+ * | `_' @ Pattern3
* | Pattern3
- * SeqPattern2 ::= varid [ @ SeqPattern3 ]
- * | SeqPattern3
* }}}
*/
def pattern2(): Tree = {
@@ -1935,7 +1955,7 @@ self =>
case Ident(nme.WILDCARD) =>
in.nextToken()
pattern3()
- case Ident(name) if treeInfo.isVarPattern(p) =>
+ case Ident(name) =>
in.nextToken()
atPos(p.pos.start) { Bind(name, pattern3()) }
case _ => p
@@ -1964,8 +1984,8 @@ self =>
case _ => EmptyTree
}
def loop(top: Tree): Tree = reducePatternStack(base, top) match {
- case next if isIdentExcept(raw.BAR) => pushOpInfo(next) ; loop(simplePattern(badPattern3))
- case next => next
+ case next if isIdent && !isRawBar => pushOpInfo(next) ; loop(simplePattern(badPattern3))
+ case next => next
}
checkWildStar orElse stripParens(loop(top))
}
@@ -2358,7 +2378,7 @@ self =>
while (in.token == VIEWBOUND) {
val msg = "Use an implicit parameter instead.\nExample: Instead of `def f[A <% Int](a: A)` use `def f[A](a: A)(implicit ev: A => Int)`."
if (settings.future)
- deprecationWarning(in.offset, s"View bounds are deprecated. $msg")
+ deprecationWarning(in.offset, s"View bounds are deprecated. $msg", "2.12.0")
contextBoundBuf += atPos(in.skipToken())(makeFunctionTypeTree(List(Ident(pname)), typ()))
}
while (in.token == COLON) {
@@ -2652,14 +2672,14 @@ self =>
if (isStatSep || in.token == RBRACE) {
if (restype.isEmpty) {
if (settings.future)
- deprecationWarning(in.lastOffset, s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit`.")
+ deprecationWarning(in.lastOffset, s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit`.", "2.12.0")
restype = scalaUnitConstr
}
newmods |= Flags.DEFERRED
EmptyTree
} else if (restype.isEmpty && in.token == LBRACE) {
if (settings.future)
- deprecationWarning(in.offset, s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit =`.")
+ deprecationWarning(in.offset, s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit =`.", "2.12.0")
restype = scalaUnitConstr
blockExpr()
} else {
@@ -2921,7 +2941,7 @@ self =>
case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred =>
copyValDef(vdef)(mods = mods | Flags.PRESUPER)
case tdef @ TypeDef(mods, name, tparams, rhs) =>
- deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.")
+ deprecationWarning(tdef.pos.point, "early type members are deprecated. Move them to the regular body: the semantics are the same.", "2.11.0")
treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs)
case docdef @ DocDef(comm, rhs) =>
treeCopy.DocDef(docdef, comm, rhs)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 3d8f5a2dd3..891858ba7b 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -35,7 +35,7 @@ trait ScannersCommon {
// things to fill in, in addition to buf, decodeUni which come from CharArrayReader
def error(off: Offset, msg: String): Unit
def incompleteInputError(off: Offset, msg: String): Unit
- def deprecationWarning(off: Offset, msg: String): Unit
+ def deprecationWarning(off: Offset, msg: String, since: String): Unit
}
def createKeywordArray(keywords: Seq[(Name, Token)], defaultToken: Token): (Token, Array[Token]) = {
@@ -208,7 +208,7 @@ trait Scanners extends ScannersCommon {
if (name == nme.MACROkw)
syntaxError(s"$name is now a reserved word; usage as an identifier is disallowed")
else if (emitIdentifierDeprecationWarnings)
- deprecationWarning(s"$name is now a reserved word; usage as an identifier is deprecated")
+ deprecationWarning(s"$name is a reserved word (since 2.10.0); usage as an identifier is deprecated", "2.10.0")
}
}
}
@@ -824,7 +824,7 @@ trait Scanners extends ScannersCommon {
if (settings.future)
syntaxError(start, msg("unsupported"))
else
- deprecationWarning(start, msg("deprecated"))
+ deprecationWarning(start, msg("deprecated"), "2.11.0")
putChar(oct.toChar)
} else {
ch match {
@@ -1034,7 +1034,7 @@ trait Scanners extends ScannersCommon {
/** generate an error at the current token offset */
def syntaxError(msg: String): Unit = syntaxError(offset, msg)
- def deprecationWarning(msg: String): Unit = deprecationWarning(offset, msg)
+ def deprecationWarning(msg: String, since: String): Unit = deprecationWarning(offset, msg, since)
/** signal an error where the input ended in the middle of a token */
def incompleteInputError(msg: String): Unit = {
@@ -1204,8 +1204,8 @@ trait Scanners extends ScannersCommon {
override val decodeUni: Boolean = !settings.nouescape
// suppress warnings, throw exception on errors
- def deprecationWarning(off: Offset, msg: String): Unit = ()
- def error (off: Offset, msg: String): Unit = throw new MalformedInput(off, msg)
+ def deprecationWarning(off: Offset, msg: String, since: String): Unit = ()
+ def error(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg)
def incompleteInputError(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg)
}
@@ -1214,9 +1214,9 @@ trait Scanners extends ScannersCommon {
class UnitScanner(val unit: CompilationUnit, patches: List[BracePatch]) extends SourceFileScanner(unit.source) {
def this(unit: CompilationUnit) = this(unit, List())
- 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.parsing.incompleteInputError(unit.position(off), msg)
+ override def deprecationWarning(off: Offset, msg: String, since: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg, since)
+ override def error(off: Offset, msg: String) = reporter.error(unit.position(off), msg)
+ override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg)
private var bracePatches: List[BracePatch] = patches
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index 1e9a1762eb..7866fcf2dc 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -107,7 +107,7 @@ abstract class TreeBuilder {
def makeCatchFromExpr(catchExpr: Tree): CaseDef = {
val binder = freshTermName()
val pat = Bind(binder, Typed(Ident(nme.WILDCARD), Ident(tpnme.Throwable)))
- val catchDef = ValDef(Modifiers(ARTIFACT), freshTermName("catchExpr"), TypeTree(), catchExpr)
+ val catchDef = ValDef(Modifiers(ARTIFACT), freshTermName("catchExpr$"), TypeTree(), catchExpr)
val catchFn = Ident(catchDef.name)
val body = atPos(catchExpr.pos.makeTransparent)(Block(
List(catchDef),
diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
index 00771b6b8c..dfd5b07a3b 100644
--- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
+++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
@@ -61,8 +61,8 @@ abstract class ScalaPrimitives {
final val NE = 43 // x != y
final val LT = 44 // x < y
final val LE = 45 // x <= y
- final val GE = 46 // x > y
- final val GT = 47 // x >= y
+ final val GT = 46 // x > y
+ final val GE = 47 // x >= y
// Boolean unary operations
final val ZNOT = 50 // !x
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index 5d152ef0e8..55fe47bde6 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -11,10 +11,10 @@ package jvm
import scala.annotation.switch
import scala.reflect.internal.Flags
-
import scala.tools.asm
import GenBCode._
import BackendReporting._
+import scala.tools.asm.Opcodes
import scala.tools.asm.tree.MethodInsnNode
import scala.tools.nsc.backend.jvm.BCodeHelpers.{InvokeStyle, TestOp}
@@ -637,7 +637,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val nativeKind = tpeTK(expr)
genLoad(expr, nativeKind)
val MethodNameAndType(mname, methodType) = srBoxesRuntimeBoxToMethods(nativeKind)
- bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos)
+ bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, itf = false, app.pos)
generatedType = boxResultType(fun.symbol)
case Apply(fun, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) =>
@@ -645,7 +645,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val boxType = unboxResultType(fun.symbol)
generatedType = boxType
val MethodNameAndType(mname, methodType) = srBoxesRuntimeUnboxToMethods(boxType)
- bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, app.pos)
+ bc.invokestatic(srBoxesRunTimeRef.internalName, mname, methodType.descriptor, itf = false, app.pos)
case app @ Apply(fun, args) =>
val sym = fun.symbol
@@ -1058,31 +1058,40 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
receiverClass.info // ensure types the type is up to date; erasure may add lateINTERFACE to traits
- val receiverName = internalName(receiverClass)
-
- // super calls are only allowed to direct parents
- if (style.isSuper && receiverClass.isTraitOrInterface && !cnode.interfaces.contains(receiverName)) {
- thisBType.info.get.inlineInfo.lateInterfaces += receiverName
- cnode.interfaces.add(receiverName)
- }
+ val receiverBType = classBTypeFromSymbol(receiverClass)
+ val receiverName = receiverBType.internalName
def needsInterfaceCall(sym: Symbol) = {
sym.isTraitOrInterface ||
sym.isJavaDefined && sym.isNonBottomSubClass(definitions.ClassfileAnnotationClass)
}
- val jname = method.javaSimpleName.toString
- val bmType = methodBTypeFromSymbol(method)
- val mdescr = bmType.descriptor
+ val jname = method.javaSimpleName.toString
+ val bmType = methodBTypeFromSymbol(method)
+ val mdescr = bmType.descriptor
+ val isInterface = receiverBType.isInterface.get
import InvokeStyle._
- style match {
- case Static => bc.invokestatic (receiverName, jname, mdescr, pos)
- case Special => bc.invokespecial (receiverName, jname, mdescr, pos)
- case Virtual =>
- if (needsInterfaceCall(receiverClass)) bc.invokeinterface(receiverName, jname, mdescr, pos)
- else bc.invokevirtual (receiverName, jname, mdescr, pos)
- case Super => bc.invokespecial (receiverName, jname, mdescr, pos)
+ if (style == Super) {
+ assert(receiverClass == methodOwner, s"for super call, expecting $receiverClass == $methodOwner")
+ if (receiverClass.isTrait && !receiverClass.isJavaDefined) {
+ val staticDesc = MethodBType(typeToBType(method.owner.info) :: bmType.argumentTypes, bmType.returnType).descriptor
+ val staticName = traitImplMethodName(method).toString
+ bc.invokestatic(receiverName, staticName, staticDesc, isInterface, pos)
+ } else {
+ if (receiverClass.isTraitOrInterface) {
+ // An earlier check in Mixin reports an error in this case, so it doesn't reach the backend
+ assert(cnode.interfaces.contains(receiverName), s"cannot invokespecial $receiverName.$jname, the interface is not a direct parent.")
+ }
+ bc.invokespecial(receiverName, jname, mdescr, isInterface, pos)
+ }
+ } else {
+ val opc = style match {
+ case Static => Opcodes.INVOKESTATIC
+ case Special => Opcodes.INVOKESPECIAL
+ case Virtual => if (isInterface) Opcodes.INVOKEINTERFACE else Opcodes.INVOKEVIRTUAL
+ }
+ bc.emitInvoke(opc, receiverName, jname, mdescr, isInterface, pos)
}
bmType.returnType
@@ -1110,22 +1119,19 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
/* Emit code to compare the two top-most stack values using the 'op' operator. */
- private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label) {
- if (targetIfNoJump == success) genCJUMP(failure, success, op.negate, tk, targetIfNoJump)
+ private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label, negated: Boolean = false) {
+ if (targetIfNoJump == success) genCJUMP(failure, success, op.negate, tk, targetIfNoJump, negated = !negated)
else {
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
bc.emitIF_ICMP(op, success)
} else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
bc.emitIF_ACMP(op, success)
} else {
+ def useCmpG = if (negated) op == TestOp.GT || op == TestOp.GE else op == TestOp.LT || op == TestOp.LE
(tk: @unchecked) match {
case LONG => emit(asm.Opcodes.LCMP)
- case FLOAT =>
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.FCMPG)
- else emit(asm.Opcodes.FCMPL)
- case DOUBLE =>
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.DCMPG)
- else emit(asm.Opcodes.DCMPL)
+ case FLOAT => emit(if (useCmpG) asm.Opcodes.FCMPG else asm.Opcodes.FCMPL)
+ case DOUBLE => emit(if (useCmpG) asm.Opcodes.DCMPG else asm.Opcodes.DCMPL)
}
bc.emitIF(op, success)
}
@@ -1134,8 +1140,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
/* Emits code to compare (and consume) stack-top and zero using the 'op' operator */
- private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label) {
- if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate, tk, targetIfNoJump)
+ private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label, negated: Boolean = false) {
+ if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate, tk, targetIfNoJump, negated = !negated)
else {
if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
bc.emitIF(op, success)
@@ -1145,18 +1151,17 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case TestOp.NE => bc emitIFNONNULL success
}
} else {
+ def useCmpG = if (negated) op == TestOp.GT || op == TestOp.GE else op == TestOp.LT || op == TestOp.LE
(tk: @unchecked) match {
case LONG =>
emit(asm.Opcodes.LCONST_0)
emit(asm.Opcodes.LCMP)
case FLOAT =>
emit(asm.Opcodes.FCONST_0)
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.FCMPG)
- else emit(asm.Opcodes.FCMPL)
+ emit(if (useCmpG) asm.Opcodes.FCMPG else asm.Opcodes.FCMPL)
case DOUBLE =>
emit(asm.Opcodes.DCONST_0)
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.DCMPG)
- else emit(asm.Opcodes.DCMPL)
+ emit(if (useCmpG) asm.Opcodes.DCMPG else asm.Opcodes.DCMPL)
}
bc.emitIF(op, success)
}
@@ -1171,8 +1176,8 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case scalaPrimitives.NE => TestOp.NE
case scalaPrimitives.LT => TestOp.LT
case scalaPrimitives.LE => TestOp.LE
- case scalaPrimitives.GE => TestOp.GE
case scalaPrimitives.GT => TestOp.GT
+ case scalaPrimitives.GE => TestOp.GE
}
/** Some useful equality helpers. */
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index a32c21795d..df3c2cb3d5 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -11,6 +11,7 @@ import scala.tools.asm
import scala.tools.nsc.io.AbstractFile
import GenBCode._
import BackendReporting._
+import scala.reflect.internal.Flags
/*
* Traits encapsulating functionality to convert Scala AST Trees into ASM ClassNodes.
@@ -49,6 +50,14 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
}
}
+ def needsStaticImplMethod(sym: Symbol) = sym.hasAttachment[global.mixer.NeedStaticImpl.type]
+
+ final def traitImplMethodName(sym: Symbol): Name = {
+ val name = sym.javaSimpleName
+ if (sym.isMixinConstructor) name
+ else name.append(nme.NAME_JOIN_STRING)
+ }
+
/**
* 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.
@@ -76,7 +85,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
val origOwner = sym.originalOwner
// phase travel necessary: after flatten, the name includes the name of outer classes.
// if some outer name contains $anon, a non-anon class is considered anon.
- if (delambdafyInline() && sym.rawowner.isAnonymousFunction) {
+ if (delambdafyInline() && exitingPickler(sym.rawowner.isAnonymousFunction)) {
// SI-9105: special handling for anonymous functions under delambdafy:inline.
//
// class C { def t = () => { def f { class Z } } }
@@ -230,58 +239,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
sym.isErroneous
}
- /**
- * Build the [[InlineInfo]] for a class symbol.
- */
- def buildInlineInfoFromClassSymbol(classSym: Symbol, classSymToInternalName: Symbol => InternalName, methodSymToDescriptor: Symbol => String): InlineInfo = {
- val isEffectivelyFinal = classSym.isEffectivelyFinal
-
- val sam = {
- if (classSym.isEffectivelyFinal) None
- else {
- // Phase travel necessary. For example, nullary methods (getter of an abstract val) get an
- // empty parameter list in later phases and would therefore be picked as SAM.
- val samSym = exitingPickler(definitions.samOf(classSym.tpe))
- if (samSym == NoSymbol) None
- else Some(samSym.javaSimpleName.toString + methodSymToDescriptor(samSym))
- }
- }
-
- var warning = Option.empty[ClassSymbolInfoFailureSI9111]
-
- // Primitive methods cannot be inlined, so there's no point in building a MethodInlineInfo. Also, some
- // primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]].
- val methodInlineInfos = classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).flatMap({
- case methodSym =>
- if (completeSilentlyAndCheckErroneous(methodSym)) {
- // Happens due to SI-9111. Just don't provide any MethodInlineInfo for that method, we don't need fail the compiler.
- if (!classSym.isJavaDefined) devWarning("SI-9111 should only be possible for Java classes")
- warning = Some(ClassSymbolInfoFailureSI9111(classSym.fullName))
- None
- } else {
- val name = methodSym.javaSimpleName.toString // same as in genDefDef
- val signature = name + methodSymToDescriptor(methodSym)
-
- // In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the
- // method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded
- // so they are not marked final in the InlineInfo attribute.
- //
- // However, due to https://github.com/scala/scala-dev/issues/126, this currently does not
- // work, the abstract accessor for O will be marked effectivelyFinal.
- val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !methodSym.isDeferred
-
- val info = MethodInlineInfo(
- effectivelyFinal = effectivelyFinal,
- annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
- annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass)
- )
- Some((signature, info))
- }
- }).toMap
-
- InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning)
- }
-
/*
* must-single-thread
*/
@@ -568,15 +525,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
/**
* The class internal name for a given class symbol.
*/
- 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 the
- // ClassBType is created from the main class (instead of the module class).
- // The two symbols have the same name, so the resulting internalName is the same.
- // Phase travel (exitingPickler) required for SI-6613 - linkedCoC is only reliable in early phases (nesting)
- val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym
- classBTypeFromSymbol(classSym).internalName
- }
+ final def internalName(sym: Symbol): String = classBTypeFromSymbol(sym).internalName
} // end of trait BCInnerClassGen
trait BCAnnotGen extends BCInnerClassGen {
@@ -1336,6 +1285,7 @@ object BCodeHelpers {
}
object TestOp {
+ // the order here / op numbers are important to get the correct result when calling opcodeIF
val EQ = new TestOp(0)
val NE = new TestOp(1)
val LT = new TestOp(2)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index 0a95bc5e39..e3d45a9b3e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
@@ -190,6 +190,7 @@ abstract class BCodeIdiomatic extends SubComponent {
JavaStringBuilderClassName,
INSTANCE_CONSTRUCTOR_NAME,
"()V",
+ itf = false,
pos
)
}
@@ -373,29 +374,26 @@ abstract class BCodeIdiomatic extends SubComponent {
final def rem(tk: BType) { emitPrimitive(JCodeMethodN.remOpcodes, tk) } // can-multi-thread
// can-multi-thread
- final def invokespecial(owner: String, name: String, desc: String, pos: Position) {
- addInvoke(Opcodes.INVOKESPECIAL, owner, name, desc, false, pos)
+ final def invokespecial(owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
+ emitInvoke(Opcodes.INVOKESPECIAL, owner, name, desc, itf, pos)
}
// can-multi-thread
- final def invokestatic(owner: String, name: String, desc: String, pos: Position) {
- addInvoke(Opcodes.INVOKESTATIC, owner, name, desc, false, pos)
+ final def invokestatic(owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
+ emitInvoke(Opcodes.INVOKESTATIC, owner, name, desc, itf, pos)
}
// can-multi-thread
- final def invokeinterface(owner: String, name: String, desc: String, pos: Position) {
- addInvoke(Opcodes.INVOKEINTERFACE, owner, name, desc, true, pos)
+ final def invokeinterface(owner: String, name: String, desc: String, pos: Position): Unit = {
+ emitInvoke(Opcodes.INVOKEINTERFACE, owner, name, desc, itf = true, pos)
}
// can-multi-thread
- final def invokevirtual(owner: String, name: String, desc: String, pos: Position) {
- addInvoke(Opcodes.INVOKEVIRTUAL, owner, name, desc, false, pos)
+ final def invokevirtual(owner: String, name: String, desc: String, pos: Position): Unit = {
+ emitInvoke(Opcodes.INVOKEVIRTUAL, owner, name, desc, itf = false, pos)
}
- private def addInvoke(opcode: Int, owner: String, name: String, desc: String, itf: Boolean, pos: Position) = {
+ def emitInvoke(opcode: Int, owner: String, name: String, desc: String, itf: Boolean, pos: Position): Unit = {
val node = new MethodInsnNode(opcode, owner, name, desc, itf)
jmethod.instructions.add(node)
- if (settings.YoptInlinerEnabled) callsitePositions(node) = pos
- }
- final def invokedynamic(owner: String, name: String, desc: String) {
- jmethod.visitMethodInsn(Opcodes.INVOKEDYNAMIC, owner, name, desc)
+ if (settings.optInlinerEnabled) callsitePositions(node) = pos
}
// can-multi-thread
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index bddc41e5c6..1bff8519ec 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -488,7 +488,22 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
case ValDef(mods, name, tpt, rhs) => () // fields are added in `genPlainClass()`, via `addClassFields()`
- case dd : DefDef => genDefDef(dd)
+ case dd : DefDef =>
+ val sym = dd.symbol
+ if (needsStaticImplMethod(sym)) {
+ val staticDefDef = global.gen.mkStatic(dd, traitImplMethodName(sym), _.cloneSymbol)
+ val forwarderDefDef = {
+ val forwarderBody = Apply(global.gen.mkAttributedRef(staticDefDef.symbol), This(sym.owner).setType(sym.owner.typeConstructor) :: dd.vparamss.head.map(p => global.gen.mkAttributedIdent(p.symbol))).setType(sym.info.resultType)
+ // we don't want to the optimizer to inline the static method into the forwarder. Instead,
+ // the backend has a special case to transitively inline into a callsite of the forwarder
+ // when the forwarder itself is inlined.
+ forwarderBody.updateAttachment(NoInlineCallsiteAttachment)
+ deriveDefDef(dd)(_ => global.atPos(dd.pos)(forwarderBody))
+ }
+ genDefDef(staticDefDef)
+ if (!sym.isMixinConstructor)
+ genDefDef(forwarderDefDef)
+ } else genDefDef(dd)
case Template(_, _, body) => body foreach gen
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index 2637d21050..7b2686e7a9 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -225,8 +225,7 @@ abstract class BTypes {
val inlineInfo = inlineInfoFromClassfile(classNode)
- val classfileInterfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
- val interfaces = classfileInterfaces.filterNot(i => inlineInfo.lateInterfaces.contains(i.internalName))
+ val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
classBType
@@ -271,7 +270,7 @@ abstract class BTypes {
// The InlineInfo is built from the classfile (not from the symbol) for all classes that are NOT
// being compiled. For those classes, the info is only needed if the inliner is enabled, othewise
// we can save the memory.
- if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo
+ if (!compilerSettings.optInlinerEnabled) BTypes.EmptyInlineInfo
else fromClassfileAttribute getOrElse fromClassfileWithoutAttribute
}
@@ -1147,25 +1146,6 @@ object BTypes {
sam: Option[String],
methodInfos: Map[String, MethodInlineInfo],
warning: Option[ClassInlineInfoWarning]) {
- /**
- * A super call (invokespecial) to a default method T.m is only allowed if the interface T is
- * a direct parent of the class. Super calls are introduced for example in Mixin when generating
- * forwarder methods:
- *
- * trait T { override def clone(): Object = "hi" }
- * trait U extends T
- * class C extends U
- *
- * The class C gets a forwarder that invokes T.clone(). During code generation the interface T
- * is added as direct parent to class C. Note that T is not a (direct) parent in the frontend
- * type of class C.
- *
- * All interfaces that are added to a class during code generation are added to this buffer and
- * stored in the InlineInfo classfile attribute. This ensures that the ClassBTypes for a
- * specific class is the same no matter if it's constructed from a Symbol or from a classfile.
- * This is tested in BTypesFromClassfileTest.
- */
- val lateInterfaces: ListBuffer[InternalName] = ListBuffer.empty
}
val EmptyInlineInfo = InlineInfo(false, None, Map.empty, None)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index d10b6c8dba..1a4590e7d1 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -97,11 +97,19 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
* for the Nothing / Null. If used for example as a parameter type, we use the runtime classes
* in the classfile method signature.
*/
- final def classBTypeFromSymbol(classSym: Symbol): ClassBType = {
+ final def classBTypeFromSymbol(sym: Symbol): ClassBType = {
+ // 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 the
+ // ClassBType is created from the main class (instead of the module class).
+ // The two symbols have the same name, so the resulting internalName is the same.
+ // Phase travel (exitingPickler) required for SI-6613 - linkedCoC is only reliable in early phases (nesting)
+ val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym
+
assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol")
assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym")
assertClassNotArrayNotPrimitive(classSym)
assert(!primitiveTypeToBType.contains(classSym) || isCompilingPrimitive, s"Cannot create ClassBType for primitive class symbol $classSym")
+
if (classSym == NothingClass) srNothingRef
else if (classSym == NullClass) srNullRef
else {
@@ -234,12 +242,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val allParents = classParents ++ classSym.annotations.flatMap(newParentForAnnotation)
+ val minimizedParents = if (classSym.isJavaDefined) allParents else erasure.minimizeParents(allParents)
// We keep the superClass when computing minimizeParents to eliminate more interfaces.
// Example: T can be eliminated from D
// trait T
// class C extends T
// class D extends C with T
- val interfaces = erasure.minimizeParents(allParents) match {
+ val interfaces = minimizedParents match {
case superClass :: ifs if !isInterfaceOrTrait(superClass.typeSymbol) =>
ifs
case ifs =>
@@ -508,13 +517,13 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
* classfile attribute.
*/
private def buildInlineInfo(classSym: Symbol, internalName: InternalName): InlineInfo = {
- def buildFromSymbol = buildInlineInfoFromClassSymbol(classSym, classBTypeFromSymbol(_).internalName, methodBTypeFromSymbol(_).descriptor)
+ def buildFromSymbol = buildInlineInfoFromClassSymbol(classSym)
// phase travel required, see implementation of `compiles`. for nested classes, it checks if the
// enclosingTopLevelClass is being compiled. after flatten, all classes are considered top-level,
// so `compiles` would return `false`.
if (exitingPickler(currentRun.compiles(classSym))) buildFromSymbol // InlineInfo required for classes being compiled, we have to create the classfile attribute
- else if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled.
+ else if (!compilerSettings.optInlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled.
else {
// For classes not being compiled, the InlineInfo is read from the classfile attribute. This
// fixes an issue with mixed-in methods: the mixin phase enters mixin methods only to class
@@ -530,6 +539,74 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
}
/**
+ * Build the [[InlineInfo]] for a class symbol.
+ */
+ def buildInlineInfoFromClassSymbol(classSym: Symbol): InlineInfo = {
+ val isEffectivelyFinal = classSym.isEffectivelyFinal
+
+ val sam = {
+ if (classSym.isEffectivelyFinal) None
+ else {
+ // Phase travel necessary. For example, nullary methods (getter of an abstract val) get an
+ // empty parameter list in later phases and would therefore be picked as SAM.
+ val samSym = exitingPickler(definitions.samOf(classSym.tpe))
+ if (samSym == NoSymbol) None
+ else Some(samSym.javaSimpleName.toString + methodBTypeFromSymbol(samSym).descriptor)
+ }
+ }
+
+ var warning = Option.empty[ClassSymbolInfoFailureSI9111]
+
+ // Primitive methods cannot be inlined, so there's no point in building a MethodInlineInfo. Also, some
+ // primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]].
+ val methodInlineInfos = classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).flatMap({
+ case methodSym =>
+ if (completeSilentlyAndCheckErroneous(methodSym)) {
+ // Happens due to SI-9111. Just don't provide any MethodInlineInfo for that method, we don't need fail the compiler.
+ if (!classSym.isJavaDefined) devWarning("SI-9111 should only be possible for Java classes")
+ warning = Some(ClassSymbolInfoFailureSI9111(classSym.fullName))
+ Nil
+ } else {
+ val name = methodSym.javaSimpleName.toString // same as in genDefDef
+ val signature = name + methodBTypeFromSymbol(methodSym).descriptor
+
+ // In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the
+ // method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded
+ // so they are not marked final in the InlineInfo attribute.
+ //
+ // However, due to https://github.com/scala/scala-dev/issues/126, this currently does not
+ // work, the abstract accessor for O will be marked effectivelyFinal.
+ val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !methodSym.isDeferred
+
+ val info = MethodInlineInfo(
+ effectivelyFinal = effectivelyFinal,
+ annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
+ annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass))
+
+ if (needsStaticImplMethod(methodSym)) {
+ val staticName = traitImplMethodName(methodSym).toString
+ val selfParam = methodSym.newSyntheticValueParam(methodSym.owner.typeConstructor, nme.SELF)
+ val staticMethodType = methodSym.info match {
+ case mt @ MethodType(params, res) => copyMethodType(mt, selfParam :: params, res)
+ }
+ val staticMethodSignature = staticName + methodBTypeFromMethodType(staticMethodType, isConstructor = false)
+ val staticMethodInfo = MethodInlineInfo(
+ effectivelyFinal = true,
+ annotatedInline = info.annotatedInline,
+ annotatedNoInline = info.annotatedNoInline)
+ if (methodSym.isMixinConstructor)
+ List((staticMethodSignature, staticMethodInfo))
+ else
+ List((signature, info), (staticMethodSignature, staticMethodInfo))
+ } else
+ List((signature, info))
+ }
+ }).toMap
+
+ InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning)
+ }
+
+ /**
* For top-level objects without a companion class, the compiler generates a mirror class with
* static forwarders (Java compat). There's no symbol for the mirror class, but we still need a
* ClassBType (its info.nestedClasses will hold the InnerClass entries, see comment in BTypes).
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
index fd558587e9..7b640ac54f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
@@ -108,15 +108,15 @@ object BackendReporting {
def emitWarning(settings: ScalaSettings): Boolean = this match {
case ClassNotFound(_, javaDefined) =>
- if (javaDefined) settings.YoptWarningNoInlineMixed
- else settings.YoptWarningNoInlineMissingBytecode
+ if (javaDefined) settings.optWarningNoInlineMixed
+ else settings.optWarningNoInlineMissingBytecode
case m @ MethodNotFound(_, _, _, missing) =>
if (m.isArrayMethod) false
- else settings.YoptWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
+ else settings.optWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
case FieldNotFound(_, _, _, missing) =>
- settings.YoptWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
+ settings.optWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
}
}
@@ -137,7 +137,7 @@ object BackendReporting {
def emitWarning(settings: ScalaSettings): Boolean = this match {
case NoClassBTypeInfoMissingBytecode(cause) => cause.emitWarning(settings)
- case NoClassBTypeInfoClassSymbolInfoFailedSI9111(_) => settings.YoptWarningNoInlineMissingBytecode
+ case NoClassBTypeInfoClassSymbolInfoFailedSI9111(_) => settings.optWarningNoInlineMissingBytecode
}
}
@@ -170,7 +170,7 @@ object BackendReporting {
case MethodInlineInfoIncomplete(_, _, _, cause) => cause.emitWarning(settings)
case MethodInlineInfoMissing(_, _, _, Some(cause)) => cause.emitWarning(settings)
- case MethodInlineInfoMissing(_, _, _, None) => settings.YoptWarningNoInlineMissingBytecode
+ case MethodInlineInfoMissing(_, _, _, None) => settings.optWarningNoInlineMissingBytecode
case MethodInlineInfoError(_, _, _, cause) => cause.emitWarning(settings)
}
@@ -216,7 +216,7 @@ object BackendReporting {
def emitWarning(settings: ScalaSettings): Boolean = this match {
case _: IllegalAccessInstruction | _: MethodWithHandlerCalledOnNonEmptyStack | _: SynchronizedMethod | _: StrictfpMismatch | _: ResultingMethodTooLarge =>
- settings.YoptWarnings.contains(settings.YoptWarningsChoices.anyInlineFailed)
+ settings.optWarnings.contains(settings.optWarningsChoices.anyInlineFailed)
case IllegalAccessCheckFailed(_, _, _, _, _, cause) =>
cause.emitWarning(settings)
@@ -238,7 +238,7 @@ object BackendReporting {
// but at the place where it's created (in findIllegalAccess) we don't have the necessary data (calleeName, calleeDescriptor).
case object UnknownInvokeDynamicInstruction extends OptimizerWarning {
override def toString = "The callee contains an InvokeDynamic instruction with an unknown bootstrap method (not a LambdaMetaFactory)."
- def emitWarning(settings: ScalaSettings): Boolean = settings.YoptWarnings.contains(settings.YoptWarningsChoices.anyInlineFailed)
+ def emitWarning(settings: ScalaSettings): Boolean = settings.optWarnings.contains(settings.optWarningsChoices.anyInlineFailed)
}
/**
@@ -250,7 +250,7 @@ object BackendReporting {
override def emitWarning(settings: ScalaSettings): Boolean = this match {
case RewriteClosureAccessCheckFailed(_, cause) => cause.emitWarning(settings)
- case RewriteClosureIllegalAccess(_, _) => settings.YoptWarnings.contains(settings.YoptWarningsChoices.anyInlineFailed)
+ case RewriteClosureIllegalAccess(_, _) => settings.optWarnings.contains(settings.optWarningsChoices.anyInlineFailed)
}
override def toString: String = this match {
@@ -282,10 +282,10 @@ object BackendReporting {
}
def emitWarning(settings: ScalaSettings): Boolean = this match {
- case NoInlineInfoAttribute(_) => settings.YoptWarningNoInlineMissingScalaInlineInfoAttr
+ case NoInlineInfoAttribute(_) => settings.optWarningNoInlineMissingScalaInlineInfoAttr
case ClassNotFoundWhenBuildingInlineInfoFromSymbol(cause) => cause.emitWarning(settings)
- case ClassSymbolInfoFailureSI9111(_) => settings.YoptWarningNoInlineMissingBytecode
- case UnknownScalaInlineInfoVersion(_, _) => settings.YoptWarningNoInlineMissingScalaInlineInfoAttr
+ case ClassSymbolInfoFailureSI9111(_) => settings.optWarningNoInlineMissingBytecode
+ case UnknownScalaInlineInfoVersion(_, _) => settings.optWarningNoInlineMissingScalaInlineInfoAttr
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
index 1feca56923..d65380aa1f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
@@ -217,26 +217,6 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
nonOverloadedConstructors(tupleClassSymbols)
}
- // enumeration of specialized classes is temporary, while we still use the java-defined JFunctionN.
- // once we switch to ordinary FunctionN, we can use specializedSubclasses just like for tuples.
- private def specializedJFunctionSymbols(base: String): Seq[Symbol] = {
- def primitives = Seq("B", "S", "I", "J", "C", "F", "D", "Z", "V")
- def ijfd = Iterator("I", "J", "F", "D")
- def ijfdzv = Iterator("I", "J", "F", "D", "Z", "V")
- def ijd = Iterator("I", "J", "D")
- val classNames = {
- primitives.map(base + "0$mc" + _ + "$sp") // Function0
- } ++ {
- // return type specializations appear first in the name string (alphabetical sorting)
- for (r <- ijfdzv; a <- ijfd) yield base + "1$mc" + r + a + "$sp" // Function1
- } ++ {
- for (r <- ijfdzv; a <- ijd; b <- ijd) yield base + "2$mc" + r + a + b + "$sp" // Function2
- }
- classNames map getRequiredClass
- }
-
- lazy val functionRefs: Set[InternalName] = (FunctionClass.seq ++ specializedJFunctionSymbols("scala.runtime.java8.JFunction")).map(classBTypeFromSymbol(_).internalName).toSet
-
lazy val typeOfArrayOp: Map[Int, BType] = {
import scalaPrimitives._
Map(
@@ -342,8 +322,6 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
def srRefConstructors : Map[InternalName, MethodNameAndType]
def tupleClassConstructors : Map[InternalName, MethodNameAndType]
- def functionRefs: Set[InternalName]
-
def lambdaMetaFactoryBootstrapHandle : asm.Handle
def lambdaDeserializeBootstrapHandle : asm.Handle
}
@@ -410,8 +388,6 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
def srRefConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.srRefConstructors
def tupleClassConstructors : Map[InternalName, MethodNameAndType] = _coreBTypes.tupleClassConstructors
- def functionRefs: Set[InternalName] = _coreBTypes.functionRefs
-
def srSymbolLiteral : ClassBType = _coreBTypes.srSymbolLiteral
def srStructuralCallSite : ClassBType = _coreBTypes.srStructuralCallSite
def srLambdaDeserialize : ClassBType = _coreBTypes.srLambdaDeserialize
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index 3520d57599..584b11d4ed 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -77,15 +77,16 @@ abstract class GenBCode extends BCodeSyncAndTry {
/* ---------------- q2 ---------------- */
- case class Item2(arrivalPos: Int,
- mirror: asm.tree.ClassNode,
- plain: asm.tree.ClassNode,
- bean: asm.tree.ClassNode,
- outFolder: scala.tools.nsc.io.AbstractFile) {
+ case class Item2(arrivalPos: Int,
+ mirror: asm.tree.ClassNode,
+ plain: asm.tree.ClassNode,
+ bean: asm.tree.ClassNode,
+ sourceFilePath: String,
+ outFolder: scala.tools.nsc.io.AbstractFile) {
def isPoison = { arrivalPos == Int.MaxValue }
}
- private val poison2 = Item2(Int.MaxValue, null, null, null, null)
+ private val poison2 = Item2(Int.MaxValue, null, null, null, null, null)
private val q2 = new _root_.java.util.LinkedList[Item2]
/* ---------------- q3 ---------------- */
@@ -205,6 +206,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
val item2 =
Item2(arrivalPos,
mirrorC, plainC, beanC,
+ cunit.source.file.canonicalPath,
outF)
q2 add item2 // at the very end of this method so that no Worker2 thread starts mutating before we're done.
@@ -225,19 +227,20 @@ abstract class GenBCode extends BCodeSyncAndTry {
// add classes to the bytecode repo before building the call graph: the latter needs to
// look up classes and methods in the code repo.
- if (settings.YoptAddToBytecodeRepository) q2.asScala foreach {
- case Item2(_, mirror, plain, bean, _) =>
- if (mirror != null) byteCodeRepository.add(mirror, ByteCodeRepository.CompilationUnit)
- if (plain != null) byteCodeRepository.add(plain, ByteCodeRepository.CompilationUnit)
- if (bean != null) byteCodeRepository.add(bean, ByteCodeRepository.CompilationUnit)
+ if (settings.optAddToBytecodeRepository) q2.asScala foreach {
+ case Item2(_, mirror, plain, bean, sourceFilePath, _) =>
+ val someSourceFilePath = Some(sourceFilePath)
+ if (mirror != null) byteCodeRepository.add(mirror, someSourceFilePath)
+ if (plain != null) byteCodeRepository.add(plain, someSourceFilePath)
+ if (bean != null) byteCodeRepository.add(bean, someSourceFilePath)
}
- if (settings.YoptBuildCallGraph) q2.asScala foreach { item =>
+ if (settings.optBuildCallGraph) q2.asScala foreach { item =>
// skip call graph for mirror / bean: wd don't inline into tem, and they are not used in the plain class
if (item.plain != null) callGraph.addClass(item.plain)
}
- if (settings.YoptInlinerEnabled)
+ if (settings.optInlinerEnabled)
bTypes.inliner.runInliner()
- if (settings.YoptClosureInvocations)
+ if (settings.optClosureInvocations)
closureOptimizer.rewriteClosureApplyInvocations()
}
@@ -286,7 +289,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
cw.toByteArray
}
- val Item2(arrivalPos, mirror, plain, bean, outFolder) = item
+ val Item2(arrivalPos, mirror, plain, bean, _, outFolder) = item
val mirrorC = if (mirror == null) null else SubItem3(mirror.name, getByteArray(mirror))
val plainC = SubItem3(plain.name, getByteArray(plain))
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
index f94642389d..83615abc31 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
@@ -2,17 +2,18 @@ package scala.tools.nsc
package backend.jvm
package analysis
+import java.lang.invoke.LambdaMetafactory
+
import scala.annotation.switch
-import scala.tools.asm.{Handle, Type}
+import scala.collection.JavaConverters._
+import scala.collection.mutable
import scala.tools.asm.Opcodes._
import scala.tools.asm.tree._
-import scala.tools.asm.tree.analysis.{Frame, BasicInterpreter, Analyzer, Value}
-import GenBCode._
+import scala.tools.asm.tree.analysis._
+import scala.tools.asm.{Handle, Type}
import scala.tools.nsc.backend.jvm.BTypes._
+import scala.tools.nsc.backend.jvm.GenBCode._
import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._
-import java.lang.invoke.LambdaMetafactory
-import scala.collection.mutable
-import scala.collection.JavaConverters._
/**
* This component hosts tools and utilities used in the backend that require access to a `BTypes`
@@ -32,7 +33,12 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
*/
class AsmAnalyzer[V <: Value](methodNode: MethodNode, classInternalName: InternalName, val analyzer: Analyzer[V] = new Analyzer(new BasicInterpreter)) {
computeMaxLocalsMaxStack(methodNode)
- analyzer.analyze(classInternalName, methodNode)
+ try {
+ analyzer.analyze(classInternalName, methodNode)
+ } catch {
+ case ae: AnalyzerException =>
+ throw new AnalyzerException(null, "While processing " + classInternalName + "." + methodNode.name, ae)
+ }
def frameAt(instruction: AbstractInsnNode): Frame[V] = analyzer.frameAt(instruction, methodNode)
}
@@ -98,7 +104,7 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
* a boolean indicating if the instruction list contains an instantiation of a serializable SAM
* type.
*/
- def cloneInstructions(methodNode: MethodNode, labelMap: Map[LabelNode, LabelNode]): (InsnList, Map[AbstractInsnNode, AbstractInsnNode], Boolean) = {
+ def cloneInstructions(methodNode: MethodNode, labelMap: Map[LabelNode, LabelNode], keepLineNumbers: Boolean): (InsnList, Map[AbstractInsnNode, AbstractInsnNode], Boolean) = {
val javaLabelMap = labelMap.asJava
val result = new InsnList
var map = Map.empty[AbstractInsnNode, AbstractInsnNode]
@@ -112,18 +118,19 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
}
case _ =>
}
- val cloned = ins.clone(javaLabelMap)
- result add cloned
- map += ((ins, cloned))
+ if (keepLineNumbers || !ins.isInstanceOf[LineNumberNode]) {
+ val cloned = ins.clone(javaLabelMap)
+ result add cloned
+ map += ((ins, cloned))
+ }
}
(result, map, hasSerializableClosureInstantiation)
}
def getBoxedUnit: FieldInsnNode = new FieldInsnNode(GETSTATIC, srBoxedUnitRef.internalName, "UNIT", srBoxedUnitRef.descriptor)
- private val anonfunAdaptedName = """.*\$anonfun\$\d+\$adapted""".r
+ private val anonfunAdaptedName = """.*\$anonfun\$.*\$\d+\$adapted""".r
def hasAdaptedImplMethod(closureInit: ClosureInstantiation): Boolean = {
- isBuiltinFunctionType(Type.getReturnType(closureInit.lambdaMetaFactoryCall.indy.desc).getInternalName) &&
anonfunAdaptedName.pattern.matcher(closureInit.lambdaMetaFactoryCall.implMethod.getName).matches
}
@@ -248,8 +255,6 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
}
}
- def isBuiltinFunctionType(internalName: InternalName): Boolean = functionRefs(internalName)
-
/**
* Visit the class node and collect all referenced nested classes.
*/
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala
index 30e73f8ac2..01afd0d2ef 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/NullnessAnalyzer.scala
@@ -5,8 +5,8 @@ package analysis
import java.util
import scala.annotation.switch
-import scala.tools.asm.{Type, Opcodes}
-import scala.tools.asm.tree.{MethodInsnNode, LdcInsnNode, AbstractInsnNode}
+import scala.tools.asm.{Opcodes, Type}
+import scala.tools.asm.tree.{AbstractInsnNode, LdcInsnNode, MethodInsnNode, MethodNode}
import scala.tools.asm.tree.analysis._
import scala.tools.nsc.backend.jvm.opt.BytecodeUtils
import BytecodeUtils._
@@ -63,7 +63,7 @@ object NullnessValue {
def unknown(insn: AbstractInsnNode) = if (BytecodeUtils.instructionResultSize(insn) == 2) UnknownValue2 else UnknownValue1
}
-final class NullnessInterpreter(bTypes: BTypes) extends Interpreter[NullnessValue](Opcodes.ASM5) {
+final class NullnessInterpreter(bTypes: BTypes, method: MethodNode) extends Interpreter[NullnessValue](Opcodes.ASM5) {
def newValue(tp: Type): NullnessValue = {
// ASM loves giving semantics to null. The behavior here is the same as in SourceInterpreter,
// which is provided by the framework.
@@ -80,7 +80,13 @@ final class NullnessInterpreter(bTypes: BTypes) extends Interpreter[NullnessValu
override def newParameterValue(isInstanceMethod: Boolean, local: Int, tp: Type): NullnessValue = {
// For instance methods, the `this` parameter is known to be not null.
- if (isInstanceMethod && local == 0) NotNullValue
+ val isThis = local == 0 && (isInstanceMethod || {
+ method.parameters != null && !method.parameters.isEmpty && {
+ val p = method.parameters.get(0)
+ (p.access & Opcodes.ACC_SYNTHETIC) != 0 && p.name == "$this"
+ }
+ })
+ if (isThis) NotNullValue
else super.newParameterValue(isInstanceMethod, local, tp)
}
@@ -197,7 +203,7 @@ class NullnessFrame(nLocals: Int, nStack: Int) extends AliasingFrame[NullnessVal
* This class is required to override the `newFrame` methods, which makes makes sure the analyzer
* uses NullnessFrames.
*/
-class NullnessAnalyzer(bTypes: BTypes) extends Analyzer[NullnessValue](new NullnessInterpreter(bTypes)) {
+class NullnessAnalyzer(bTypes: BTypes, method: MethodNode) extends Analyzer[NullnessValue](new NullnessInterpreter(bTypes, method)) {
override def newFrame(nLocals: Int, nStack: Int): NullnessFrame = new NullnessFrame(nLocals, nStack)
override def newFrame(src: Frame[_ <: NullnessValue]): NullnessFrame = new NullnessFrame(src)
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala
index 16590ec75c..78acd72dba 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala
@@ -15,7 +15,6 @@ import scala.tools.asm.Attribute
import scala.tools.nsc.backend.jvm.BackendReporting._
import scala.tools.nsc.util.ClassPath
import BytecodeUtils._
-import ByteCodeRepository._
import BTypes.InternalName
import java.util.concurrent.atomic.AtomicLong
@@ -29,9 +28,10 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT)
import btypes._
/**
- * ClassNodes for classes being compiled in the current compilation run.
+ * Contains ClassNodes and the canonical path of the source file path of classes being compiled in
+ * the current compilation run.
*/
- val compilingClasses: concurrent.Map[InternalName, ClassNode] = recordPerRunCache(concurrent.TrieMap.empty)
+ val compilingClasses: concurrent.Map[InternalName, (ClassNode, String)] = recordPerRunCache(concurrent.TrieMap.empty)
/**
* Cache for parsed ClassNodes.
@@ -67,20 +67,35 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT)
}
}
- def add(classNode: ClassNode, source: Source) = {
- if (source == CompilationUnit) compilingClasses(classNode.name) = classNode
- else parsedClasses(classNode.name) = Right((classNode, lruCounter.incrementAndGet()))
+ def add(classNode: ClassNode, sourceFilePath: Option[String]) = sourceFilePath match {
+ case Some(path) if path != "<no file>" => compilingClasses(classNode.name) = (classNode, path)
+ case _ => parsedClasses(classNode.name) = Right((classNode, lruCounter.incrementAndGet()))
+ }
+
+ private def parsedClassNode(internalName: InternalName): Either[ClassNotFound, ClassNode] = {
+ val r = parsedClasses.get(internalName) match {
+ case Some(l @ Left(_)) => l
+ case Some(r @ Right((classNode, _))) =>
+ parsedClasses(internalName) = Right((classNode, lruCounter.incrementAndGet()))
+ r
+ case None =>
+ limitCacheSize()
+ val res = parseClass(internalName).map((_, lruCounter.incrementAndGet()))
+ parsedClasses(internalName) = res
+ res
+ }
+ r.map(_._1)
}
/**
- * The class node and source for an internal name. If the class node is not yet available, it is
- * parsed from the classfile on the compile classpath.
+ * The class node and source file path (if the class is being compiled) for an internal name. If
+ * the class node is not yet available, it is parsed from the classfile on the compile classpath.
*/
- def classNodeAndSource(internalName: InternalName): Either[ClassNotFound, (ClassNode, Source)] = {
- classNode(internalName) map (n => {
- val source = if (compilingClasses contains internalName) CompilationUnit else Classfile
- (n, source)
- })
+ def classNodeAndSourceFilePath(internalName: InternalName): Either[ClassNotFound, (ClassNode, Option[String])] = {
+ compilingClasses.get(internalName) match {
+ case Some((c, p)) => Right((c, Some(p)))
+ case _ => parsedClassNode(internalName).map((_, None))
+ }
}
/**
@@ -88,19 +103,9 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT)
* the classfile on the compile classpath.
*/
def classNode(internalName: InternalName): Either[ClassNotFound, ClassNode] = {
- compilingClasses.get(internalName).map(Right(_)) getOrElse {
- val r = parsedClasses.get(internalName) match {
- case Some(l @ Left(_)) => l
- case Some(r @ Right((classNode, _))) =>
- parsedClasses(internalName) = Right((classNode, lruCounter.incrementAndGet()))
- r
- case None =>
- limitCacheSize()
- val res = parseClass(internalName).map((_, lruCounter.incrementAndGet()))
- parsedClasses(internalName) = res
- res
- }
- r.map(_._1)
+ compilingClasses.get(internalName) match {
+ case Some((c, _)) => Right(c)
+ case None => parsedClassNode(internalName)
}
}
@@ -289,13 +294,3 @@ class ByteCodeRepository[BT <: BTypes](val classPath: ClassPath, val btypes: BT)
}
}
}
-
-object ByteCodeRepository {
- /**
- * The source of a ClassNode in the ByteCodeRepository. Can be either [[CompilationUnit]] if the
- * class is being compiled or [[Classfile]] if the class was parsed from the compilation classpath.
- */
- sealed trait Source
- object CompilationUnit extends Source
- object Classfile extends Source
-}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
index 63906d80e5..e21c46dbe9 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
@@ -93,6 +93,15 @@ object BytecodeUtils {
op == INVOKESPECIAL || op == INVOKESTATIC
}
+ def isVirtualCall(instruction: AbstractInsnNode): Boolean = {
+ val op = instruction.getOpcode
+ op == INVOKEVIRTUAL || op == INVOKEINTERFACE
+ }
+
+ def isCall(instruction: AbstractInsnNode): Boolean = {
+ isNonVirtualCall(instruction) || isVirtualCall(instruction)
+ }
+
def isExecutable(instruction: AbstractInsnNode): Boolean = instruction.getOpcode >= 0
def isConstructor(methodNode: MethodNode): Boolean = {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
index d241acf7b1..5248183337 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -9,14 +9,13 @@ package opt
import scala.collection.immutable.IntMap
import scala.reflect.internal.util.{NoPosition, Position}
-import scala.tools.asm.{Opcodes, Type, Handle}
+import scala.tools.asm.{Handle, Opcodes, Type}
import scala.tools.asm.tree._
import scala.collection.{concurrent, mutable}
import scala.collection.JavaConverters._
-import scala.tools.nsc.backend.jvm.BTypes.InternalName
+import scala.tools.nsc.backend.jvm.BTypes.{InternalName, MethodInlineInfo}
import scala.tools.nsc.backend.jvm.BackendReporting._
import scala.tools.nsc.backend.jvm.analysis._
-import ByteCodeRepository.{Source, CompilationUnit}
import BytecodeUtils._
class CallGraph[BT <: BTypes](val btypes: BT) {
@@ -68,6 +67,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
}
def containsCallsite(callsite: Callsite): Boolean = callsites(callsite.callsiteMethod) contains callsite.callsiteInstruction
+ def findCallSite(method: MethodNode, call: MethodInsnNode): Option[Callsite] = callsites.getOrElse(method, Map.empty).get(call)
def removeClosureInstantiation(indy: InvokeDynamicInsnNode, methodNode: MethodNode): Option[ClosureInstantiation] = {
val methodClosureInits = closureInstantiations(methodNode)
@@ -102,8 +102,8 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// It is also used to get the stack height at the call site.
val analyzer = {
- if (compilerSettings.YoptNullnessTracking && AsmAnalyzer.sizeOKForNullness(methodNode)) {
- Some(new AsmAnalyzer(methodNode, definingClass.internalName, new NullnessAnalyzer(btypes)))
+ if (compilerSettings.optNullnessTracking && AsmAnalyzer.sizeOKForNullness(methodNode)) {
+ Some(new AsmAnalyzer(methodNode, definingClass.internalName, new NullnessAnalyzer(btypes, methodNode)))
} else if (AsmAnalyzer.sizeOKForBasicValue(methodNode)) {
Some(new AsmAnalyzer(methodNode, definingClass.internalName))
} else None
@@ -128,17 +128,17 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
methodNode.instructions.iterator.asScala foreach {
case call: MethodInsnNode if a.frameAt(call) != null => // skips over unreachable code
val callee: Either[OptimizerWarning, Callee] = for {
- (method, declarationClass) <- byteCodeRepository.methodNode(call.owner, call.name, call.desc): Either[OptimizerWarning, (MethodNode, InternalName)]
- (declarationClassNode, source) <- byteCodeRepository.classNodeAndSource(declarationClass): Either[OptimizerWarning, (ClassNode, Source)]
+ (method, declarationClass) <- byteCodeRepository.methodNode(call.owner, call.name, call.desc): Either[OptimizerWarning, (MethodNode, InternalName)]
+ (declarationClassNode, calleeSourceFilePath) <- byteCodeRepository.classNodeAndSourceFilePath(declarationClass): Either[OptimizerWarning, (ClassNode, Option[String])]
} yield {
val declarationClassBType = classBTypeFromClassNode(declarationClassNode)
- val info = analyzeCallsite(method, declarationClassBType, call, source)
+ val info = analyzeCallsite(method, declarationClassBType, call, calleeSourceFilePath)
import info._
Callee(
callee = method,
calleeDeclarationClass = declarationClassBType,
safeToInline = safeToInline,
- canInlineFromSource = canInlineFromSource,
+ sourceFilePath = sourceFilePath,
annotatedInline = annotatedInline,
annotatedNoInline = annotatedNoInline,
samParamTypes = info.samParamTypes,
@@ -256,7 +256,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
/**
* Just a named tuple used as return type of `analyzeCallsite`.
*/
- private case class CallsiteInfo(safeToInline: Boolean, canInlineFromSource: Boolean,
+ private case class CallsiteInfo(safeToInline: Boolean, sourceFilePath: Option[String],
annotatedInline: Boolean, annotatedNoInline: Boolean,
samParamTypes: IntMap[ClassBType],
warning: Option[CalleeInfoWarning])
@@ -264,7 +264,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
/**
* Analyze a callsite and gather meta-data that can be used for inlining decisions.
*/
- private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSource: Source): CallsiteInfo = {
+ private def analyzeCallsite(calleeMethodNode: MethodNode, calleeDeclarationClassBType: ClassBType, call: MethodInsnNode, calleeSourceFilePath: Option[String]): CallsiteInfo = {
val methodSignature = calleeMethodNode.name + calleeMethodNode.desc
try {
@@ -273,8 +273,6 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// callee, we only check there for the methodInlineInfo, we should find it there.
calleeDeclarationClassBType.info.orThrow.inlineInfo.methodInfos.get(methodSignature) match {
case Some(methodInlineInfo) =>
- val canInlineFromSource = compilerSettings.YoptInlineGlobal || calleeSource == CompilationUnit
-
val isAbstract = BytecodeUtils.isAbstractMethod(calleeMethodNode)
val receiverType = classBTypeFromParsedClassfile(call.owner)
@@ -308,13 +306,13 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// static impl method first (safeToRewrite).
CallsiteInfo(
safeToInline =
- canInlineFromSource &&
+ inlinerHeuristics.canInlineFromSource(calleeSourceFilePath) &&
isStaticallyResolved && // (1)
!isAbstract &&
!BytecodeUtils.isConstructor(calleeMethodNode) &&
!BytecodeUtils.isNativeMethod(calleeMethodNode) &&
!BytecodeUtils.hasCallerSensitiveAnnotation(calleeMethodNode),
- canInlineFromSource = canInlineFromSource,
+ sourceFilePath = calleeSourceFilePath,
annotatedInline = methodInlineInfo.annotatedInline,
annotatedNoInline = methodInlineInfo.annotatedNoInline,
samParamTypes = samParamTypes(calleeMethodNode, receiverType),
@@ -322,12 +320,12 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
case None =>
val warning = MethodInlineInfoMissing(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, calleeDeclarationClassBType.info.orThrow.inlineInfo.warning)
- CallsiteInfo(false, false, false, false, IntMap.empty, Some(warning))
+ CallsiteInfo(false, None, false, false, IntMap.empty, Some(warning))
}
} catch {
case Invalid(noInfo: NoClassBTypeInfo) =>
val warning = MethodInlineInfoError(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, noInfo)
- CallsiteInfo(false, false, false, false, IntMap.empty, Some(warning))
+ CallsiteInfo(false, None, false, false, IntMap.empty, Some(warning))
}
}
@@ -359,7 +357,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
"Invocation of" +
s" ${callee.map(_.calleeDeclarationClass.internalName).getOrElse("?")}.${callsiteInstruction.name + callsiteInstruction.desc}" +
s"@${callsiteMethod.instructions.indexOf(callsiteInstruction)}" +
- s" in ${callsiteClass.internalName}.${callsiteMethod.name}"
+ s" in ${callsiteClass.internalName}.${callsiteMethod.name}${callsiteMethod.desc}"
}
final case class ClonedCallsite(callsite: Callsite, clonedWhenInlining: Callsite)
@@ -389,7 +387,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
* gathering the information about this callee.
*/
final case class Callee(callee: MethodNode, calleeDeclarationClass: btypes.ClassBType,
- safeToInline: Boolean, canInlineFromSource: Boolean,
+ safeToInline: Boolean, sourceFilePath: Option[String],
annotatedInline: Boolean, annotatedNoInline: Boolean,
samParamTypes: IntMap[btypes.ClassBType],
calleeInfoWarning: Option[CalleeInfoWarning]) {
@@ -432,7 +430,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
def unapply(insn: AbstractInsnNode): Option[(InvokeDynamicInsnNode, Type, Handle, Type)] = insn match {
case indy: InvokeDynamicInsnNode if indy.bsm == metafactoryHandle || indy.bsm == altMetafactoryHandle =>
indy.bsmArgs match {
- case Array(samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type, xs@_*) => // xs binding because IntelliJ gets confused about _@_*
+ case Array(samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type, _@_*) =>
// LambdaMetaFactory performs a number of automatic adaptations when invoking the lambda
// implementation method (casting, boxing, unboxing, and primitive widening, see Javadoc).
//
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
index 93dc40f318..081830d61d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
@@ -17,7 +17,6 @@ import scala.tools.nsc.backend.jvm.BTypes.InternalName
import BytecodeUtils._
import BackendReporting._
import Opcodes._
-import scala.tools.nsc.backend.jvm.opt.ByteCodeRepository.CompilationUnit
import scala.collection.JavaConverters._
class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
@@ -354,16 +353,15 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
// the method node is needed for building the call graph entry
val bodyMethod = byteCodeRepository.methodNode(lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc)
- def bodyMethodIsBeingCompiled = byteCodeRepository.classNodeAndSource(lambdaBodyHandle.getOwner).map(_._2 == CompilationUnit).getOrElse(false)
+ val sourceFilePath = byteCodeRepository.compilingClasses.get(lambdaBodyHandle.getOwner).map(_._2)
val callee = bodyMethod.map({
case (bodyMethodNode, bodyMethodDeclClass) =>
val bodyDeclClassType = classBTypeFromParsedClassfile(bodyMethodDeclClass)
- val canInlineFromSource = compilerSettings.YoptInlineGlobal || bodyMethodIsBeingCompiled
Callee(
callee = bodyMethodNode,
calleeDeclarationClass = bodyDeclClassType,
- safeToInline = canInlineFromSource,
- canInlineFromSource = canInlineFromSource,
+ safeToInline = inlinerHeuristics.canInlineFromSource(sourceFilePath),
+ sourceFilePath = sourceFilePath,
annotatedInline = false,
annotatedNoInline = false,
samParamTypes = callGraph.samParamTypes(bodyMethodNode, bodyDeclClassType),
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala
index 4163d62df7..b05669ce89 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CopyProp.scala
@@ -295,18 +295,12 @@ class CopyProp[BT <: BTypes](val btypes: BT) {
}
/**
- * Eliminate the closure value produced by `indy`. If the SAM type is known to construct
- * without side-effects (e.g. scala/FunctionN), the `indy` and its inputs
- * are eliminated, otherwise a POP is inserted.
+ * Eliminate LMF `indy` and its inputs.
*/
def handleClosureInst(indy: InvokeDynamicInsnNode): Unit = {
- if (isBuiltinFunctionType(Type.getReturnType(indy.desc).getInternalName)) {
- toRemove += indy
- callGraph.removeClosureInstantiation(indy, method)
- handleInputs(indy, Type.getArgumentTypes(indy.desc).length)
- } else {
- toInsertAfter(indy) = getPop(1)
- }
+ toRemove += indy
+ callGraph.removeClosureInstantiation(indy, method)
+ handleInputs(indy, Type.getArgumentTypes(indy.desc).length)
}
def runQueue(): Unit = while (queue.nonEmpty) {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
index 79d26b0b4e..5ce7072c60 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlineInfoAttribute.scala
@@ -51,7 +51,6 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
if (inlineInfo.isEffectivelyFinal) flags |= 1
// flags |= 2 // no longer written
if (inlineInfo.sam.isDefined) flags |= 4
- if (inlineInfo.lateInterfaces.nonEmpty) flags |= 8
result.putByte(flags)
for (samNameDesc <- inlineInfo.sam) {
@@ -79,9 +78,6 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
result.putByte(inlineInfo)
}
- result.putShort(inlineInfo.lateInterfaces.length)
- for (i <- inlineInfo.lateInterfaces) result.putShort(cw.newUTF8(i))
-
result
}
@@ -105,7 +101,6 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
val isFinal = (flags & 1) != 0
val hasSelf = (flags & 2) != 0
val hasSam = (flags & 4) != 0
- val hasLateInterfaces = (flags & 8) != 0
if (hasSelf) nextUTF8() // no longer used
@@ -128,13 +123,7 @@ case class InlineInfoAttribute(inlineInfo: InlineInfo) extends Attribute(InlineI
(name + desc, MethodInlineInfo(isFinal, isInline, isNoInline))
}).toMap
- val lateInterfaces = if (!hasLateInterfaces) Nil else {
- val numLateInterfaces = nextShort()
- (0 until numLateInterfaces).map(_ => nextUTF8())
- }
-
val info = InlineInfo(isFinal, sam, infos, None)
- info.lateInterfaces ++= lateInterfaces
InlineInfoAttribute(info)
} else {
val msg = UnknownScalaInlineInfoVersion(cr.getClassName, version)
@@ -161,8 +150,6 @@ object InlineInfoAttribute {
* [u2] name (reference)
* [u2] descriptor (reference)
* [u1] isFinal (<< 0), traitMethodWithStaticImplementation (<< 1), hasInlineAnnotation (<< 2), hasNoInlineAnnotation (<< 3)
- * [u2]? numLateInterfaces
- * [u2] lateInterface (reference)
*/
final val VERSION: Byte = 1
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
index f35eaa45e9..9c5a1a9f98 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -25,6 +25,9 @@ class Inliner[BT <: BTypes](val btypes: BT) {
import inlinerHeuristics._
import backendUtils._
+ case class InlineLog(request: InlineRequest, sizeBefore: Int, sizeAfter: Int, sizeInlined: Int, warning: Option[CannotInlineWarning])
+ var inlineLog: List[InlineLog] = Nil
+
def runInliner(): Unit = {
for (request <- collectAndOrderInlineRequests) {
val Right(callee) = request.callsite.callee // collectAndOrderInlineRequests returns callsites with a known callee
@@ -35,13 +38,36 @@ class Inliner[BT <: BTypes](val btypes: BT) {
val warnings = inline(request)
for (warning <- warnings) {
- if ((callee.annotatedInline && btypes.compilerSettings.YoptWarningEmitAtInlineFailed) || warning.emitWarning(compilerSettings)) {
+ if ((callee.annotatedInline && btypes.compilerSettings.optWarningEmitAtInlineFailed) || warning.emitWarning(compilerSettings)) {
val annotWarn = if (callee.annotatedInline) " is annotated @inline but" else ""
val msg = s"${BackendReporting.methodSignature(callee.calleeDeclarationClass.internalName, callee.callee)}$annotWarn could not be inlined:\n$warning"
backendReporting.inlinerWarning(request.callsite.callsitePosition, msg)
}
}
}
+
+ if (compilerSettings.YoptLogInline.isSetByUser) {
+ val methodPrefix = { val p = compilerSettings.YoptLogInline.value; if (p == "_") "" else p }
+ val byCallsiteMethod = inlineLog.groupBy(_.request.callsite.callsiteMethod).toList.sortBy(_._2.head.request.callsite.callsiteClass.internalName)
+ for ((m, mLogs) <- byCallsiteMethod) {
+ val initialSize = mLogs.minBy(_.sizeBefore).sizeBefore
+ val firstLog = mLogs.head
+ val methodName = s"${firstLog.request.callsite.callsiteClass.internalName}.${m.name}"
+ if (methodName.startsWith(methodPrefix)) {
+ println(s"Inlining into $methodName (initially $initialSize instructions, ultimately ${m.instructions.size}):")
+ val byCallee = mLogs.groupBy(_.request.callsite.callee.get).toList.sortBy(_._2.length).reverse
+ for ((c, cLogs) <- byCallee) {
+ val first = cLogs.head
+ if (first.warning.isEmpty) {
+ val num = if (cLogs.tail.isEmpty) "" else s" ${cLogs.length} times"
+ println(s" - Inlined ${c.calleeDeclarationClass.internalName}.${c.callee.name} (${first.sizeInlined} instructions)$num: ${first.request.reason}")
+ } else
+ println(s" - Failed to inline ${c.calleeDeclarationClass.internalName}.${c.callee.name} (${first.request.reason}): ${first.warning.get}")
+ }
+ println()
+ }
+ }
+ }
}
/**
@@ -80,6 +106,8 @@ class Inliner[BT <: BTypes](val btypes: BT) {
val elided = mutable.Set.empty[InlineRequest]
def nonElidedRequests(methodNode: MethodNode): Set[InlineRequest] = requestsByMethod(methodNode) diff elided
+ def allCallees(r: InlineRequest): Set[MethodNode] = r.post.flatMap(allCallees).toSet + r.callsite.callee.get.callee
+
/**
* Break cycles in the inline request graph by removing callsites.
*
@@ -88,20 +116,20 @@ class Inliner[BT <: BTypes](val btypes: BT) {
*/
def breakInlineCycles: List[InlineRequest] = {
// is there a path of inline requests from start to goal?
- def isReachable(start: MethodNode, goal: MethodNode): Boolean = {
- @tailrec def reachableImpl(check: List[MethodNode], visited: Set[MethodNode]): Boolean = check match {
- case x :: xs =>
+ def isReachable(start: Set[MethodNode], goal: MethodNode): Boolean = {
+ @tailrec def reachableImpl(check: Set[MethodNode], visited: Set[MethodNode]): Boolean = {
+ if (check.isEmpty) false
+ else {
+ val x = check.head
if (x == goal) true
- else if (visited(x)) reachableImpl(xs, visited)
+ else if (visited(x)) reachableImpl(check - x, visited)
else {
- val callees = nonElidedRequests(x).map(_.callsite.callee.get.callee)
- reachableImpl(xs ::: callees.toList, visited + x)
+ val callees = nonElidedRequests(x).flatMap(allCallees)
+ reachableImpl(check - x ++ callees, visited + x)
}
-
- case Nil =>
- false
+ }
}
- reachableImpl(List(start), Set.empty)
+ reachableImpl(start, Set.empty)
}
val result = new mutable.ListBuffer[InlineRequest]()
@@ -110,7 +138,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
java.util.Arrays.sort(requests, callsiteOrdering)
for (r <- requests) {
// is there a chain of inlining requests that would inline the callsite method into the callee?
- if (isReachable(r.callsite.callee.get.callee, r.callsite.callsiteMethod))
+ if (isReachable(allCallees(r), r.callsite.callsiteMethod))
elided += r
else
result += r
@@ -124,8 +152,8 @@ class Inliner[BT <: BTypes](val btypes: BT) {
if (requests.isEmpty) Nil
else {
val (leaves, others) = requests.partition(r => {
- val inlineRequestsForCallee = nonElidedRequests(r.callsite.callee.get.callee)
- inlineRequestsForCallee.forall(visited)
+ val inlineRequestsForCallees = allCallees(r).flatMap(nonElidedRequests)
+ inlineRequestsForCallees.forall(visited)
})
assert(leaves.nonEmpty, requests)
leaves ::: leavesFirst(others, visited ++ leaves)
@@ -184,7 +212,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
def impl(post: InlineRequest, at: Callsite): List[InlineRequest] = {
post.callsite.inlinedClones.find(_.clonedWhenInlining == at) match {
case Some(clonedCallsite) =>
- List(InlineRequest(clonedCallsite.callsite, post.post))
+ List(InlineRequest(clonedCallsite.callsite, post.post, post.reason))
case None =>
post.post.flatMap(impl(_, post.callsite)).flatMap(impl(_, at))
}
@@ -199,9 +227,17 @@ class Inliner[BT <: BTypes](val btypes: BT) {
* @return An inliner warning for each callsite that could not be inlined.
*/
def inline(request: InlineRequest): List[CannotInlineWarning] = canInlineBody(request.callsite) match {
- case Some(w) => List(w)
+ case Some(w) =>
+ if (compilerSettings.YoptLogInline.isSetByUser) {
+ val size = request.callsite.callsiteMethod.instructions.size
+ inlineLog ::= InlineLog(request, size, size, 0, Some(w))
+ }
+ List(w)
case None =>
+ val sizeBefore = request.callsite.callsiteMethod.instructions.size
inlineCallsite(request.callsite)
+ if (compilerSettings.YoptLogInline.isSetByUser)
+ inlineLog ::= InlineLog(request, sizeBefore, request.callsite.callsiteMethod.instructions.size, request.callsite.callee.get.callee.instructions.size, None)
val postRequests = request.post.flatMap(adaptPostRequestForMainCallsite(_, request.callsite))
postRequests flatMap inline
}
@@ -219,7 +255,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
def inlineCallsite(callsite: Callsite): Unit = {
import callsite.{callsiteClass, callsiteMethod, callsiteInstruction, receiverKnownNotNull, callsiteStackHeight}
val Right(callsiteCallee) = callsite.callee
- import callsiteCallee.{callee, calleeDeclarationClass}
+ import callsiteCallee.{callee, calleeDeclarationClass, sourceFilePath}
// Inlining requires the callee not to have unreachable code, the analyzer used below should not
// return any `null` frames. Note that inlining a method can create unreachable code. Example:
@@ -234,11 +270,14 @@ class Inliner[BT <: BTypes](val btypes: BT) {
// New labels for the cloned instructions
val labelsMap = cloneLabels(callee)
- val (clonedInstructions, instructionMap, hasSerializableClosureInstantiation) = cloneInstructions(callee, labelsMap)
- val keepLineNumbers = callsiteClass == calleeDeclarationClass
- if (!keepLineNumbers) {
- removeLineNumberNodes(clonedInstructions)
+ val sameSourceFile = sourceFilePath match {
+ case Some(calleeSource) => byteCodeRepository.compilingClasses.get(callsiteClass.internalName) match {
+ case Some((_, `calleeSource`)) => true
+ case _ => false
+ }
+ case _ => false
}
+ val (clonedInstructions, instructionMap, hasSerializableClosureInstantiation) = cloneInstructions(callee, labelsMap, keepLineNumbers = sameSourceFile)
// local vars in the callee are shifted by the number of locals at the callsite
val localVarShift = callsiteMethod.maxLocals
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
index 6aaf9734d3..79e74f3eb7 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
@@ -7,21 +7,23 @@ package scala.tools.nsc
package backend.jvm
package opt
-import scala.tools.asm.tree.MethodNode
-import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.collection.JavaConverters._
+import scala.tools.asm.Opcodes
+import scala.tools.asm.tree.{MethodInsnNode, MethodNode}
+import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.tools.nsc.backend.jvm.BackendReporting.OptimizerWarning
class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
import bTypes._
- import inliner._
import callGraph._
- case class InlineRequest(callsite: Callsite, post: List[InlineRequest]) {
+ case class InlineRequest(callsite: Callsite, post: List[InlineRequest], reason: String) {
// invariant: all post inline requests denote callsites in the callee of the main callsite
for (pr <- post) assert(pr.callsite.callsiteMethod == callsite.callee.get.callee, s"Callsite method mismatch: main $callsite - post ${pr.callsite}")
}
+ def canInlineFromSource(sourceFilePath: Option[String]) = compilerSettings.optInlineGlobal || sourceFilePath.isDefined
+
/**
* Select callsites from the call graph that should be inlined, grouped by the containing method.
* Cyclic inlining requests are allowed, the inliner will eliminate requests to break cycles.
@@ -32,27 +34,27 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
// classpath. In order to get only the callsites being compiled, we start at the map of
// compilingClasses in the byteCodeRepository.
val compilingMethods = for {
- classNode <- byteCodeRepository.compilingClasses.valuesIterator
- methodNode <- classNode.methods.iterator.asScala
+ (classNode, _) <- byteCodeRepository.compilingClasses.valuesIterator
+ methodNode <- classNode.methods.iterator.asScala
} yield methodNode
compilingMethods.map(methodNode => {
var requests = Set.empty[InlineRequest]
callGraph.callsites(methodNode).valuesIterator foreach {
- case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, canInlineFromSource, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
- inlineRequest(callsite) match {
+ case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, sourceFilePath, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
+ inlineRequest(callsite, requests) match {
case Some(Right(req)) => requests += req
case Some(Left(w)) =>
- if ((calleeAnnotatedInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) || w.emitWarning(compilerSettings)) {
+ if ((calleeAnnotatedInline && bTypes.compilerSettings.optWarningEmitAtInlineFailed) || w.emitWarning(compilerSettings)) {
val annotWarn = if (calleeAnnotatedInline) " is annotated @inline but" else ""
val msg = s"${BackendReporting.methodSignature(calleeDeclClass.internalName, callee)}$annotWarn could not be inlined:\n$w"
backendReporting.inlinerWarning(callsite.callsitePosition, msg)
}
case None =>
- if (canInlineFromSource && calleeAnnotatedInline && !callsite.annotatedNoInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) {
+ if (canInlineFromSource(sourceFilePath) && calleeAnnotatedInline && !callsite.annotatedNoInline && bTypes.compilerSettings.optWarningEmitAtInlineFailed) {
// if the callsite is annotated @inline, we report an inline warning even if the underlying
- // reason is, for example, mixed compilation (which has a separate -Yopt-warning flag).
+ // reason is, for example, mixed compilation (which has a separate -opt-warning flag).
def initMsg = s"${BackendReporting.methodSignature(calleeDeclClass.internalName, callee)} is annotated @inline but cannot be inlined"
def warnMsg = callsiteWarning.map(" Possible reason:\n" + _).getOrElse("")
if (!safeToInline)
@@ -87,20 +89,49 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
* InlineRequest for the original callsite? new subclass of OptimizerWarning.
* `Some(Right)` if the callsite should be and can be inlined
*/
- def inlineRequest(callsite: Callsite): Option[Either[OptimizerWarning, InlineRequest]] = {
+ def inlineRequest(callsite: Callsite, selectedRequestsForCallee: Set[InlineRequest]): Option[Either[OptimizerWarning, InlineRequest]] = {
val callee = callsite.callee.get
- def requestIfCanInline(callsite: Callsite): Either[OptimizerWarning, InlineRequest] = inliner.earlyCanInlineCheck(callsite) match {
+ def requestIfCanInline(callsite: Callsite, reason: String): Either[OptimizerWarning, InlineRequest] = inliner.earlyCanInlineCheck(callsite) match {
case Some(w) => Left(w)
- case None => Right(InlineRequest(callsite, Nil))
+ case None =>
+ val callee = callsite.callee.get
+ val postInlineRequest: List[InlineRequest] = callee.calleeDeclarationClass.isInterface match {
+ case Right(true) =>
+ // Treat the pair of trait interface method and static method as one for the purposes of inlining:
+ // if we inline invokeinterface, invoke the invokestatic, too.
+ val calls = callee.callee.instructions.iterator().asScala.filter(BytecodeUtils.isCall).take(2).toList
+ calls match {
+ case List(x: MethodInsnNode) if x.getOpcode == Opcodes.INVOKESTATIC && x.name == (callee.callee.name + "$") =>
+ callGraph.addIfMissing(callee.callee, callee.calleeDeclarationClass)
+ val maybeNodeToCallsite1 = callGraph.findCallSite(callee.callee, x)
+ maybeNodeToCallsite1.toList.flatMap(x => requestIfCanInline(x, reason).right.toOption)
+ case _ =>
+ Nil
+
+ }
+ case _ => Nil
+ }
+
+ Right(InlineRequest(callsite, postInlineRequest, reason))
+
}
compilerSettings.YoptInlineHeuristics.value match {
case "everything" =>
- if (callee.safeToInline) Some(requestIfCanInline(callsite))
+ if (callee.safeToInline) {
+ val reason = if (compilerSettings.YoptLogInline.isSetByUser) "the inline strategy is \"everything\"" else null
+ Some(requestIfCanInline(callsite, reason))
+ }
else None
case "at-inline-annotated" =>
- if (callee.safeToInline && callee.annotatedInline) Some(requestIfCanInline(callsite))
+ if (callee.safeToInline && callee.annotatedInline) {
+ val reason = if (compilerSettings.YoptLogInline.isSetByUser) {
+ val what = if (callee.safeToInline) "callee" else "callsite"
+ s"the $what is annotated `@inline`"
+ } else null
+ Some(requestIfCanInline(callsite, reason))
+ }
else None
case "default" =>
@@ -108,7 +139,30 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
def shouldInlineHO = callee.samParamTypes.nonEmpty && (callee.samParamTypes exists {
case (index, _) => callsite.argInfos.contains(index)
})
- if (callee.annotatedInline || callsite.annotatedInline || shouldInlineHO) Some(requestIfCanInline(callsite))
+ if (callee.annotatedInline || callsite.annotatedInline || shouldInlineHO) {
+ val reason = if (compilerSettings.YoptLogInline.isSetByUser) {
+ if (callee.annotatedInline || callsite.annotatedInline) {
+ val what = if (callee.safeToInline) "callee" else "callsite"
+ s"the $what is annotated `@inline`"
+ } else {
+ val paramNames = Option(callee.callee.parameters).map(_.asScala.map(_.name).toVector)
+ def param(i: Int) = {
+ def syn = s"<param $i>"
+ paramNames.fold(syn)(v => v.applyOrElse(i, (_: Int) => syn))
+ }
+ def samInfo(i: Int, sam: String, arg: String) = s"the argument for parameter (${param(i)}: $sam) is a $arg"
+ val argInfos = for ((i, sam) <- callee.samParamTypes; info <- callsite.argInfos.get(i)) yield {
+ val argKind = info match {
+ case FunctionLiteral => "function literal"
+ case ForwardedParam(_) => "parameter of the callsite method"
+ }
+ samInfo(i, sam.internalName.split('/').last, argKind)
+ }
+ s"the callee is a higher-order method, ${argInfos.mkString(", ")}"
+ }
+ } else null
+ Some(requestIfCanInline(callsite, reason))
+ }
else None
} else None
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
index 4e1349257e..447ee209b5 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
@@ -191,7 +191,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
* @return `true` if unreachable code was eliminated in some method, `false` otherwise.
*/
def methodOptimizations(clazz: ClassNode): Boolean = {
- !compilerSettings.YoptNone && clazz.methods.asScala.foldLeft(false) {
+ !compilerSettings.optNone && clazz.methods.asScala.foldLeft(false) {
case (changed, method) => methodOptimizations(method, clazz.name) || changed
}
}
@@ -231,7 +231,8 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
// for local variables in dead blocks. Maybe that's a bug in the ASM framework.
var currentTrace: String = null
- val doTrace = compilerSettings.YoptTrace.isSetByUser && compilerSettings.YoptTrace.value == ownerClassName + "." + method.name
+ val methodPrefix = {val p = compilerSettings.YoptTrace.value; if (p == "_") "" else p }
+ val doTrace = compilerSettings.YoptTrace.isSetByUser && s"$ownerClassName.${method.name}".startsWith(methodPrefix)
def traceIfChanged(optName: String): Unit = if (doTrace) {
val after = AsmUtils.textify(method)
if (currentTrace != after) {
@@ -261,46 +262,46 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
traceIfChanged("beforeMethodOpt")
// NULLNESS OPTIMIZATIONS
- val runNullness = compilerSettings.YoptNullnessTracking && requestNullness
+ val runNullness = compilerSettings.optNullnessTracking && requestNullness
val nullnessOptChanged = runNullness && nullnessOptimizations(method, ownerClassName)
traceIfChanged("nullness")
// UNREACHABLE CODE
// Both AliasingAnalyzer (used in copyProp) and ProdConsAnalyzer (used in eliminateStaleStores,
// boxUnboxElimination) require not having unreachable instructions (null frames).
- val runDCE = (compilerSettings.YoptUnreachableCode && (requestDCE || nullnessOptChanged)) ||
- compilerSettings.YoptBoxUnbox ||
- compilerSettings.YoptCopyPropagation
+ val runDCE = (compilerSettings.optUnreachableCode && (requestDCE || nullnessOptChanged)) ||
+ compilerSettings.optBoxUnbox ||
+ compilerSettings.optCopyPropagation
val (codeRemoved, liveLabels) = if (runDCE) removeUnreachableCodeImpl(method, ownerClassName) else (false, Set.empty[LabelNode])
traceIfChanged("dce")
// BOX-UNBOX
- val runBoxUnbox = compilerSettings.YoptBoxUnbox && (requestBoxUnbox || nullnessOptChanged)
+ val runBoxUnbox = compilerSettings.optBoxUnbox && (requestBoxUnbox || nullnessOptChanged)
val boxUnboxChanged = runBoxUnbox && boxUnboxElimination(method, ownerClassName)
traceIfChanged("boxUnbox")
// COPY PROPAGATION
- val runCopyProp = compilerSettings.YoptCopyPropagation && (firstIteration || boxUnboxChanged)
+ val runCopyProp = compilerSettings.optCopyPropagation && (firstIteration || boxUnboxChanged)
val copyPropChanged = runCopyProp && copyPropagation(method, ownerClassName)
traceIfChanged("copyProp")
// STALE STORES
- val runStaleStores = compilerSettings.YoptCopyPropagation && (requestStaleStores || nullnessOptChanged || codeRemoved || boxUnboxChanged || copyPropChanged)
+ val runStaleStores = compilerSettings.optCopyPropagation && (requestStaleStores || nullnessOptChanged || codeRemoved || boxUnboxChanged || copyPropChanged)
val storesRemoved = runStaleStores && eliminateStaleStores(method, ownerClassName)
traceIfChanged("staleStores")
// REDUNDANT CASTS
- val runRedundantCasts = compilerSettings.YoptRedundantCasts && (firstIteration || boxUnboxChanged)
+ val runRedundantCasts = compilerSettings.optRedundantCasts && (firstIteration || boxUnboxChanged)
val castRemoved = runRedundantCasts && eliminateRedundantCasts(method, ownerClassName)
traceIfChanged("redundantCasts")
// PUSH-POP
- val runPushPop = compilerSettings.YoptCopyPropagation && (requestPushPop || firstIteration || storesRemoved || castRemoved)
+ val runPushPop = compilerSettings.optCopyPropagation && (requestPushPop || firstIteration || storesRemoved || castRemoved)
val pushPopRemoved = runPushPop && eliminatePushPop(method, ownerClassName)
traceIfChanged("pushPop")
// STORE-LOAD PAIRS
- val runStoreLoad = compilerSettings.YoptCopyPropagation && (requestStoreLoad || boxUnboxChanged || copyPropChanged || pushPopRemoved)
+ val runStoreLoad = compilerSettings.optCopyPropagation && (requestStoreLoad || boxUnboxChanged || copyPropChanged || pushPopRemoved)
val storeLoadRemoved = runStoreLoad && eliminateStoreLoad(method)
traceIfChanged("storeLoadPairs")
@@ -312,7 +313,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
// SIMPLIFY JUMPS
// almost all of the above optimizations enable simplifying more jumps, so we just run it in every iteration
- val runSimplifyJumps = compilerSettings.YoptSimplifyJumps
+ val runSimplifyJumps = compilerSettings.optSimplifyJumps
val jumpsChanged = runSimplifyJumps && simplifyJumps(method)
traceIfChanged("simplifyJumps")
@@ -358,21 +359,21 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
requestPushPop = true,
requestStoreLoad = true,
firstIteration = true)
- if (compilerSettings.YoptUnreachableCode) unreachableCodeEliminated += method
+ if (compilerSettings.optUnreachableCode) unreachableCodeEliminated += method
r
} else (false, false)
// (*) Removing stale local variable descriptors is required for correctness, see comment in `methodOptimizations`
val localsRemoved =
- if (compilerSettings.YoptCompactLocals) compactLocalVariables(method) // also removes unused
+ if (compilerSettings.optCompactLocals) compactLocalVariables(method) // also removes unused
else if (requireEliminateUnusedLocals) removeUnusedLocalVariableNodes(method)() // (*)
else false
traceIfChanged("localVariables")
- val lineNumbersRemoved = if (compilerSettings.YoptUnreachableCode) removeEmptyLineNumbers(method) else false
+ val lineNumbersRemoved = if (compilerSettings.optUnreachableCode) removeEmptyLineNumbers(method) else false
traceIfChanged("lineNumbers")
- val labelsRemoved = if (compilerSettings.YoptUnreachableCode) removeEmptyLabelNodes(method) else false
+ val labelsRemoved = if (compilerSettings.optUnreachableCode) removeEmptyLabelNodes(method) else false
traceIfChanged("labels")
// assert that local variable annotations are empty (we don't emit them) - otherwise we'd have
@@ -396,7 +397,7 @@ class LocalOpt[BT <: BTypes](val btypes: BT) {
*/
def nullnessOptimizations(method: MethodNode, ownerClassName: InternalName): Boolean = {
AsmAnalyzer.sizeOKForNullness(method) && {
- lazy val nullnessAnalyzer = new AsmAnalyzer(method, ownerClassName, new NullnessAnalyzer(btypes))
+ lazy val nullnessAnalyzer = new AsmAnalyzer(method, ownerClassName, new NullnessAnalyzer(btypes, method))
// When running nullness optimizations the method may still have unreachable code. Analyzer
// frames of unreachable instructions are `null`.
diff --git a/src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala b/src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala
index 6b435542a3..a1af3413ea 100644
--- a/src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala
+++ b/src/compiler/scala/tools/nsc/classpath/AggregateClassPath.scala
@@ -6,6 +6,7 @@ package scala.tools.nsc.classpath
import java.net.URL
import scala.annotation.tailrec
import scala.collection.mutable.ArrayBuffer
+import scala.reflect.internal.FatalError
import scala.reflect.io.AbstractFile
import scala.tools.nsc.util.ClassPath
import scala.tools.nsc.util.ClassRepresentation
@@ -72,7 +73,16 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath {
getDistinctEntries(_.sources(inPackage))
override private[nsc] def list(inPackage: String): ClassPathEntries = {
- val (packages, classesAndSources) = aggregates.map(_.list(inPackage)).unzip
+ val (packages, classesAndSources) = aggregates.map { cp =>
+ try {
+ cp.list(inPackage)
+ } catch {
+ case ex: java.io.IOException =>
+ val e = new FatalError(ex.getMessage)
+ e.initCause(ex)
+ throw e
+ }
+ }.unzip
val distinctPackages = packages.flatten.distinct
val distinctClassesAndSources = mergeClassesAndSources(classesAndSources: _*)
ClassPathEntries(distinctPackages, distinctClassesAndSources)
diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
index cc851b6330..fd9c99a3b9 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
@@ -27,7 +27,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
def freshName(prefix: String): Name = freshTermName(prefix)
def freshTermName(prefix: String): TermName = unit.freshTermName(prefix)
def freshTypeName(prefix: String): TypeName = unit.freshTypeName(prefix)
- def deprecationWarning(off: Int, msg: String) = currentRun.reporting.deprecationWarning(off, msg)
+ def deprecationWarning(off: Int, msg: String, since: String) = currentRun.reporting.deprecationWarning(off, msg, since)
implicit def i2p(offset : Int) : Position = Position.offset(unit.source, offset)
def warning(pos : Int, msg : String) : Unit = reporter.warning(pos, msg)
def syntaxError(pos: Int, msg: String) : Unit = reporter.error(pos, msg)
diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
index ac86dfd665..c74a6938c6 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
@@ -860,9 +860,9 @@ trait JavaScanners extends ast.parser.ScannersCommon {
class JavaUnitScanner(unit: CompilationUnit) extends JavaScanner {
in = new JavaCharArrayReader(unit.source.content, !settings.nouescape.value, syntaxError)
init()
- def error (pos: Int, msg: String) = reporter.error(pos, msg)
+ def error(pos: Int, msg: String) = reporter.error(pos, msg)
def incompleteInputError(pos: Int, msg: String) = currentRun.parsing.incompleteInputError(pos, msg)
- def deprecationWarning(pos: Int, msg: String) = currentRun.reporting.deprecationWarning(pos, msg)
+ def deprecationWarning(pos: Int, msg: String, since: String) = currentRun.reporting.deprecationWarning(pos, msg, since)
implicit def g2p(pos: Int): Position = Position.offset(unit.source, pos)
}
}
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 9a0d86a94d..4d236b226d 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -30,7 +30,7 @@ trait ScalaSettings extends AbsScalaSettings
protected def defaultClasspath = sys.env.getOrElse("CLASSPATH", ".")
/** Enabled under -Xexperimental. */
- protected def experimentalSettings = List[BooleanSetting](YmethodInfer, overrideObjects, overrideVars)
+ protected def experimentalSettings = List[BooleanSetting](YpartialUnification)
/** Enabled under -Xfuture. */
protected def futureSettings = List[BooleanSetting]()
@@ -201,11 +201,12 @@ trait ScalaSettings extends AbsScalaSettings
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 YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
+ val YpartialUnification = BooleanSetting ("-Ypartial-unification", "Enable partial unification in type constructor inference")
val exposeEmptyPackage = BooleanSetting ("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "method")
- object YoptChoices extends MultiChoiceEnumeration {
+ object optChoices extends MultiChoiceEnumeration {
val unreachableCode = Choice("unreachable-code", "Eliminate unreachable code, exception handlers guarding no instructions, redundant metadata (debug information, line numbers).")
val simplifyJumps = Choice("simplify-jumps", "Simplify branching instructions, eliminate unnecessary ones.")
val compactLocals = Choice("compact-locals", "Eliminate empty slots in the sequence of local variables.")
@@ -217,8 +218,8 @@ trait ScalaSettings extends AbsScalaSettings
val inlineProject = Choice("inline-project", "Inline only methods defined in the files being compiled. Enables unreachable-code.")
val inlineGlobal = Choice("inline-global", "Inline methods from any source, including classfiles on the compile classpath. Enables unreachable-code.")
- // note: unlike the other optimizer levels, "l:none" appears up in the `Yopt.value` set because it's not an expanding option (expandsTo is empty)
- val lNone = Choice("l:none", "Disable optimizations. Takes precedence: `-Yopt:l:none,+box-unbox` / `-Yopt:l:none -Yopt:box-unbox` don't enable box-unbox.")
+ // note: unlike the other optimizer levels, "l:none" appears up in the `opt.value` set because it's not an expanding option (expandsTo is empty)
+ val lNone = Choice("l:none", "Disable optimizations. Takes precedence: `-opt:l:none,+box-unbox` / `-opt:l:none -opt:box-unbox` don't enable box-unbox.")
private val defaultChoices = List(unreachableCode)
val lDefault = Choice("l:default", "Enable default optimizations: "+ defaultChoices.mkString("", ",", "."), expandsTo = defaultChoices)
@@ -234,37 +235,37 @@ trait ScalaSettings extends AbsScalaSettings
}
// We don't use the `default` parameter of `MultiChoiceSetting`: it specifies the default values
- // when `-Yopt` is passed without explicit choices. When `-Yopt` is not explicitly specified, the
- // set `Yopt.value` is empty.
- val Yopt = MultiChoiceSetting(
- name = "-Yopt",
+ // when `-opt` is passed without explicit choices. When `-opt` is not explicitly specified, the
+ // set `opt.value` is empty.
+ val opt = MultiChoiceSetting(
+ name = "-opt",
helpArg = "optimization",
descr = "Enable optimizations",
- domain = YoptChoices)
+ domain = optChoices)
- private def optEnabled(choice: YoptChoices.Choice) = {
- !Yopt.contains(YoptChoices.lNone) && {
- Yopt.contains(choice) ||
- !Yopt.isSetByUser && YoptChoices.lDefault.expandsTo.contains(choice)
+ private def optEnabled(choice: optChoices.Choice) = {
+ !opt.contains(optChoices.lNone) && {
+ opt.contains(choice) ||
+ !opt.isSetByUser && optChoices.lDefault.expandsTo.contains(choice)
}
}
- def YoptNone = Yopt.contains(YoptChoices.lNone)
- def YoptUnreachableCode = optEnabled(YoptChoices.unreachableCode)
- def YoptSimplifyJumps = optEnabled(YoptChoices.simplifyJumps)
- def YoptCompactLocals = optEnabled(YoptChoices.compactLocals)
- def YoptCopyPropagation = optEnabled(YoptChoices.copyPropagation)
- def YoptRedundantCasts = optEnabled(YoptChoices.redundantCasts)
- def YoptBoxUnbox = optEnabled(YoptChoices.boxUnbox)
- def YoptNullnessTracking = optEnabled(YoptChoices.nullnessTracking)
- def YoptClosureInvocations = optEnabled(YoptChoices.closureInvocations)
+ def optNone = opt.contains(optChoices.lNone)
+ def optUnreachableCode = optEnabled(optChoices.unreachableCode)
+ def optSimplifyJumps = optEnabled(optChoices.simplifyJumps)
+ def optCompactLocals = optEnabled(optChoices.compactLocals)
+ def optCopyPropagation = optEnabled(optChoices.copyPropagation)
+ def optRedundantCasts = optEnabled(optChoices.redundantCasts)
+ def optBoxUnbox = optEnabled(optChoices.boxUnbox)
+ def optNullnessTracking = optEnabled(optChoices.nullnessTracking)
+ def optClosureInvocations = optEnabled(optChoices.closureInvocations)
- def YoptInlineProject = optEnabled(YoptChoices.inlineProject)
- def YoptInlineGlobal = optEnabled(YoptChoices.inlineGlobal)
- def YoptInlinerEnabled = YoptInlineProject || YoptInlineGlobal
+ def optInlineProject = optEnabled(optChoices.inlineProject)
+ def optInlineGlobal = optEnabled(optChoices.inlineGlobal)
+ def optInlinerEnabled = optInlineProject || optInlineGlobal
- def YoptBuildCallGraph = YoptInlinerEnabled || YoptClosureInvocations
- def YoptAddToBytecodeRepository = YoptBuildCallGraph || YoptInlinerEnabled || YoptClosureInvocations
+ def optBuildCallGraph = optInlinerEnabled || optClosureInvocations
+ def optAddToBytecodeRepository = optBuildCallGraph || optInlinerEnabled || optClosureInvocations
val YoptInlineHeuristics = ChoiceSetting(
name = "-Yopt-inline-heuristics",
@@ -273,7 +274,7 @@ trait ScalaSettings extends AbsScalaSettings
choices = List("at-inline-annotated", "everything", "default"),
default = "default")
- object YoptWarningsChoices extends MultiChoiceEnumeration {
+ object optWarningsChoices extends MultiChoiceEnumeration {
val none = Choice("none" , "No optimizer warnings.")
val atInlineFailedSummary = Choice("at-inline-failed-summary" , "One-line summary if there were @inline method calls that could not be inlined.")
val atInlineFailed = Choice("at-inline-failed" , "A detailed warning for each @inline method call that could not be inlined.")
@@ -283,26 +284,28 @@ trait ScalaSettings extends AbsScalaSettings
val noInlineMissingScalaInlineInfoAttr = Choice("no-inline-missing-attribute", "Warn if an inlining decision cannot be made because a Scala classfile does not have a ScalaInlineInfo attribute.")
}
- val YoptWarnings = MultiChoiceSetting(
- name = "-Yopt-warnings",
+ val optWarnings = MultiChoiceSetting(
+ name = "-opt-warnings",
helpArg = "warning",
descr = "Enable optimizer warnings",
- domain = YoptWarningsChoices,
- default = Some(List(YoptWarningsChoices.atInlineFailed.name)))
+ domain = optWarningsChoices,
+ default = Some(List(optWarningsChoices.atInlineFailed.name)))
- def YoptWarningsSummaryOnly = YoptWarnings.value subsetOf Set(YoptWarningsChoices.none, YoptWarningsChoices.atInlineFailedSummary)
+ def optWarningsSummaryOnly = optWarnings.value subsetOf Set(optWarningsChoices.none, optWarningsChoices.atInlineFailedSummary)
- def YoptWarningEmitAtInlineFailed =
- !YoptWarnings.isSetByUser ||
- YoptWarnings.contains(YoptWarningsChoices.atInlineFailedSummary) ||
- YoptWarnings.contains(YoptWarningsChoices.atInlineFailed) ||
- YoptWarnings.contains(YoptWarningsChoices.anyInlineFailed)
+ def optWarningEmitAtInlineFailed =
+ !optWarnings.isSetByUser ||
+ optWarnings.contains(optWarningsChoices.atInlineFailedSummary) ||
+ optWarnings.contains(optWarningsChoices.atInlineFailed) ||
+ optWarnings.contains(optWarningsChoices.anyInlineFailed)
- def YoptWarningNoInlineMixed = YoptWarnings.contains(YoptWarningsChoices.noInlineMixed)
- def YoptWarningNoInlineMissingBytecode = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingBytecode)
- def YoptWarningNoInlineMissingScalaInlineInfoAttr = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingScalaInlineInfoAttr)
+ def optWarningNoInlineMixed = optWarnings.contains(optWarningsChoices.noInlineMixed)
+ def optWarningNoInlineMissingBytecode = optWarnings.contains(optWarningsChoices.noInlineMissingBytecode)
+ def optWarningNoInlineMissingScalaInlineInfoAttr = optWarnings.contains(optWarningsChoices.noInlineMissingScalaInlineInfoAttr)
- val YoptTrace = StringSetting("-Yopt-trace", "package/Class.method", "Trace the optimizer progress for a specific method.", "")
+ val YoptTrace = StringSetting("-Yopt-trace", "package/Class.method", "Trace the optimizer progress for methods; `_` to print all, prefix match to select.", "")
+
+ val YoptLogInline = StringSetting("-Yopt-log-inline", "package/Class.method", "Print a summary of inliner activity; `_` to print all, prefix match to select.", "")
private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug."
@@ -340,8 +343,8 @@ trait ScalaSettings extends AbsScalaSettings
val future = BooleanSetting("-Xfuture", "Turn on future language features.") enablingIfNotSetByUser futureSettings
val optimise = BooleanSetting("-optimise", "Compiler flag for the optimizer in Scala 2.11")
.withAbbreviation("-optimize")
- .withDeprecationMessage("In 2.12, -optimise enables -Yopt:l:classpath. Check -Yopt:help for using the Scala 2.12 optimizer.")
- .withPostSetHook(_ => Yopt.tryToSet(List(YoptChoices.lClasspath.name)))
+ .withDeprecationMessage("In 2.12, -optimise enables -opt:l:classpath. Check -opt:help for using the Scala 2.12 optimizer.")
+ .withPostSetHook(_ => opt.tryToSet(List(optChoices.lClasspath.name)))
val Xexperimental = BooleanSetting("-Xexperimental", "Enable experimental extensions.") enablingIfNotSetByUser experimentalSettings
// Feature extensions
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index 636fb08b89..971a55f763 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -715,6 +715,9 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
primaryConstrBody.expr)
})
+ if (omittableAccessor.exists(_.isOuterField) && !constructorStats.exists(_.exists { case i: Ident if i.symbol.isOuterParam => true; case _ => false}))
+ primaryConstructor.symbol.updateAttachment(OuterArgCanBeElided)
+
val constructors = primaryConstructor :: auxConstructors
// Unlink all fields that can be dropped from class scope
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index d350ca8e17..804bcddb7b 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -61,6 +61,9 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
private def mkLambdaMetaFactoryCall(fun: Function, target: Symbol, functionalInterface: Symbol, samUserDefined: Symbol, isSpecialized: Boolean): Tree = {
val pos = fun.pos
+ def isSelfParam(p: Symbol) = p.isSynthetic && p.name == nme.SELF
+ val hasSelfParam = isSelfParam(target.firstParam)
+
val allCapturedArgRefs = {
// find which variables are free in the lambda because those are captures that need to be
// passed into the constructor of the anonymous function class
@@ -68,7 +71,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
gen.mkAttributedRef(capture) setPos pos
).toList
- if (target hasFlag STATIC) captureArgs // no `this` reference needed
+ if (!hasSelfParam) captureArgs.filterNot(arg => isSelfParam(arg.symbol))
+ else if (currentMethod.hasFlag(Flags.STATIC)) captureArgs
else (gen.mkAttributedThis(fun.symbol.enclClass) setPos pos) :: captureArgs
}
@@ -179,7 +183,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val numCaptures = targetParams.length - functionParamTypes.length
val (targetCapturedParams, targetFunctionParams) = targetParams.splitAt(numCaptures)
- val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT)
+ val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT | STATIC)
val bridgeCapturedParams = targetCapturedParams.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName))
val bridgeFunctionParams =
map2(targetFunctionParams, bridgeParamTypes)((param, tp) => methSym.newSyntheticValueParam(tp, param.name.toTermName))
@@ -221,12 +225,11 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
}
}
+
private def transformFunction(originalFunction: Function): Tree = {
val target = targetMethod(originalFunction)
- target.makeNotPrivate(target.owner)
-
- // must be done before calling createBoxingBridgeMethod and mkLambdaMetaFactoryCall
- if (!(target hasFlag STATIC) && !methodReferencesThis(target)) target setFlag STATIC
+ assert(target.hasFlag(Flags.STATIC))
+ target.setFlag(notPRIVATE)
val funSym = originalFunction.tpe.typeSymbolDirect
// The functional interface that can be used to adapt the lambda target method `target` to the given function type.
@@ -252,17 +255,41 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// here's the main entry point of the transform
override def transform(tree: Tree): Tree = tree match {
// the main thing we care about is lambdas
- case fun: Function => super.transform(transformFunction(fun))
+ case fun: Function =>
+ super.transform(transformFunction(fun))
case Template(_, _, _) =>
+ def pretransform(tree: Tree): Tree = tree match {
+ case dd: DefDef if dd.symbol.isDelambdafyTarget =>
+ if (!dd.symbol.hasFlag(STATIC) && methodReferencesThis(dd.symbol)) {
+ gen.mkStatic(dd, dd.symbol.name, sym => sym)
+ } else {
+ dd.symbol.setFlag(STATIC)
+ dd
+ }
+ case t => t
+ }
try {
// during this call boxingBridgeMethods will be populated from the Function case
- val Template(parents, self, body) = super.transform(tree)
+ val Template(parents, self, body) = super.transform(deriveTemplate(tree)(_.mapConserve(pretransform)))
Template(parents, self, body ++ boxingBridgeMethods)
} finally boxingBridgeMethods.clear()
+ case dd: DefDef if dd.symbol.isLiftedMethod && !dd.symbol.isDelambdafyTarget =>
+ // SI-9390 emit lifted methods that don't require a `this` reference as STATIC
+ // delambdafy targets are excluded as they are made static by `transformFunction`.
+ if (!dd.symbol.hasFlag(STATIC) && !methodReferencesThis(dd.symbol)) {
+ dd.symbol.setFlag(STATIC)
+ dd.symbol.removeAttachment[mixer.NeedStaticImpl.type]
+ }
+ super.transform(tree)
+ case Apply(fun, outer :: rest) if shouldElideOuterArg(fun.symbol, outer) =>
+ val nullOuter = gen.mkZero(outer.tpe)
+ treeCopy.Apply(tree, transform(fun), nullOuter :: transformTrees(rest))
case _ => super.transform(tree)
}
} // DelambdafyTransformer
+ private def shouldElideOuterArg(fun: Symbol, outerArg: Tree): Boolean =
+ fun.isConstructor && treeInfo.isQualifierSafeToElide(outerArg) && fun.hasAttachment[OuterArgCanBeElided.type]
// A traverser that finds symbols used but not defined in the given Tree
// TODO freeVarTraverser in LambdaLift does a very similar task. With some
@@ -313,19 +340,28 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// recursively find methods that refer to 'this' directly or indirectly via references to other methods
// for each method found add it to the referrers set
- private def refersToThis(symbol: Symbol): Boolean =
- (thisReferringMethods contains symbol) ||
- (liftedMethodReferences(symbol) exists refersToThis) && {
- // add it early to memoize
- debuglog(s"$symbol indirectly refers to 'this'")
- thisReferringMethods += symbol
- true
+ private def refersToThis(symbol: Symbol): Boolean = {
+ var seen = mutable.Set[Symbol]()
+ def loop(symbol: Symbol): Boolean = {
+ if (seen(symbol)) false
+ else {
+ seen += symbol
+ (thisReferringMethods contains symbol) ||
+ (liftedMethodReferences(symbol) exists loop) && {
+ // add it early to memoize
+ debuglog(s"$symbol indirectly refers to 'this'")
+ thisReferringMethods += symbol
+ true
+ }
}
+ }
+ loop(symbol)
+ }
private var currentMethod: Symbol = NoSymbol
override def traverse(tree: Tree) = tree match {
- case DefDef(_, _, _, _, _, _) if tree.symbol.isDelambdafyTarget =>
+ case DefDef(_, _, _, _, _, _) if tree.symbol.isDelambdafyTarget || tree.symbol.isLiftedMethod =>
// we don't expect defs within defs. At this phase trees should be very flat
if (currentMethod.exists) devWarning("Found a def within a def at a phase where defs are expected to be flattened out.")
currentMethod = tree.symbol
@@ -336,6 +372,12 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// They'll be of the form {(args...) => this.anonfun(args...)}
// but we do need to make note of the lifted body method in case it refers to 'this'
if (currentMethod.exists) liftedMethodReferences(currentMethod) += targetMethod(fun)
+ case Apply(sel @ Select(This(_), _), args) if sel.symbol.isLiftedMethod =>
+ if (currentMethod.exists) liftedMethodReferences(currentMethod) += sel.symbol
+ super.traverseTrees(args)
+ case Apply(fun, outer :: rest) if shouldElideOuterArg(fun.symbol, outer) =>
+ super.traverse(fun)
+ super.traverseTrees(rest)
case This(_) =>
if (currentMethod.exists && tree.symbol == currentMethod.enclClass) {
debuglog(s"$currentMethod directly refers to 'this'")
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 5e903946c1..db8e203c1c 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -189,18 +189,23 @@ abstract class Erasure extends AddInterfaces
/* Drop redundant types (ones which are implemented by some other parent) from the immediate parents.
* This is important on Android because there is otherwise an interface explosion.
+ * This is now restricted to Scala defined ancestors: a Java defined ancestor may need to be listed
+ * as an immediate parent to support an `invokespecial`.
*/
def minimizeParents(parents: List[Type]): List[Type] = if (parents.isEmpty) parents else {
- def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait
+ def isRedundantParent(sym: Symbol) = sym.isInterface || sym.isTrait
var rest = parents.tail
var leaves = collection.mutable.ListBuffer.empty[Type] += parents.head
while(rest.nonEmpty) {
val candidate = rest.head
- val nonLeaf = leaves exists { t => t.typeSymbol isSubClass candidate.typeSymbol }
- if(!nonLeaf) {
- leaves = leaves filterNot { t => isInterfaceOrTrait(t.typeSymbol) && (candidate.typeSymbol isSubClass t.typeSymbol) }
- leaves += candidate
+ if (candidate.typeSymbol.isJavaDefined && candidate.typeSymbol.isInterface) leaves += candidate
+ else {
+ val nonLeaf = leaves exists { t => t.typeSymbol isSubClass candidate.typeSymbol }
+ if (!nonLeaf) {
+ leaves = leaves filterNot { t => isRedundantParent(t.typeSymbol) && (candidate.typeSymbol isSubClass t.typeSymbol) }
+ leaves += candidate
+ }
}
rest = rest.tail
}
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index 3d6fad4238..411ff6b9be 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -67,8 +67,6 @@ abstract class ExplicitOuter extends InfoTransform
result
}
- private val innerClassConstructorParamName: TermName = newTermName("arg" + nme.OUTER)
-
class RemoveBindingsTransformer(toRemove: Set[Symbol]) extends Transformer {
override def transform(tree: Tree) = tree match {
case Bind(_, body) if toRemove(tree.symbol) => super.transform(body)
@@ -169,7 +167,7 @@ abstract class ExplicitOuter extends InfoTransform
val paramsWithOuter =
if (sym.isClassConstructor && isInner(sym.owner)) // 1
- sym.newValueParameter(innerClassConstructorParamName, sym.pos).setInfo(sym.owner.outerClass.thisType) :: params
+ sym.newValueParameter(nme.OUTER_ARG, sym.pos).setInfo(sym.owner.outerClass.thisType) :: params
else params
if ((resTpTransformed ne resTp) || (paramsWithOuter ne params)) MethodType(paramsWithOuter, resTpTransformed)
diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
index 9d02228ab5..f2237a0716 100644
--- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala
@@ -192,8 +192,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
extensionDefs(currentOwner.companionModule) = new mutable.ListBuffer[Tree]
currentOwner.primaryConstructor.makeNotPrivate(NoSymbol)
// SI-7859 make param accessors accessible so the erasure can generate unbox operations.
- val paramAccessors = currentOwner.info.decls.filter(sym => sym.isParamAccessor && sym.isMethod)
- paramAccessors.foreach(_.makeNotPrivate(currentOwner))
+ currentOwner.info.decls.foreach(sym => if (sym.isParamAccessor && sym.isMethod) sym.makeNotPrivate(currentOwner))
super.transform(tree)
} else if (currentOwner.isStaticOwner) {
super.transform(tree)
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index 19ba9345fa..d62b77dac2 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -130,6 +130,10 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
*/
def addMember(clazz: Symbol, member: Symbol): Symbol = {
debuglog(s"mixing into $clazz: ${member.defString}")
+ // This attachment is used to instruct the backend about which methids in traits require
+ // a static trait impl method. We remove this from the new symbol created for the method
+ // mixed into the subclass.
+ member.removeAttachment[NeedStaticImpl.type]
clazz.info.decls enter member setFlag MIXEDIN resetFlag JAVA_DEFAULTMETHOD
}
def cloneAndAddMember(mixinClass: Symbol, mixinMember: Symbol, clazz: Symbol): Symbol =
@@ -344,6 +348,10 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
reporter.error(clazz.pos, "Member %s of mixin %s is missing a concrete super implementation.".format(
mixinMember.alias, mixinClass))
case alias1 =>
+ if (alias1.owner.isJavaDefined && alias1.owner.isInterface && !clazz.parentSymbols.contains(alias1.owner)) {
+ val suggestedParent = exitingTyper(clazz.info.baseType(alias1.owner))
+ reporter.error(clazz.pos, s"Unable to implement a super accessor required by trait ${mixinClass.name} unless $suggestedParent is directly extended by $clazz.")
+ }
superAccessor.asInstanceOf[TermSymbol] setAlias alias1
}
}
@@ -1001,8 +1009,20 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
val parents1 = currentOwner.info.parents map (t => TypeTree(t) setPos tree.pos)
// mark fields which can be nulled afterward
lazyValNullables = nullableFields(templ) withDefaultValue Set()
+ // Remove bodies of accessors in traits - TODO: after PR #5141 (fields refactoring), this might be a no-op
+ val bodyEmptyAccessors = if (!sym.enclClass.isTrait) body else body mapConserve {
+ case dd: DefDef if dd.symbol.isAccessor && !dd.symbol.isLazy =>
+ deriveDefDef(dd)(_ => EmptyTree)
+ case tree => tree
+ }
// add all new definitions to current class or interface
- treeCopy.Template(tree, parents1, self, addNewDefs(currentOwner, body))
+ val body1 = addNewDefs(currentOwner, bodyEmptyAccessors)
+ body1 foreach {
+ case dd: DefDef if isTraitMethodRequiringStaticImpl(dd) =>
+ dd.symbol.updateAttachment(NeedStaticImpl)
+ case _ =>
+ }
+ treeCopy.Template(tree, parents1, self, body1)
case Select(qual, name) if sym.owner.isTrait && !sym.isMethod =>
// refer to fields in some trait an abstract getter in the interface.
@@ -1018,7 +1038,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
typedPos(tree.pos)((qual DOT setter)(rhs))
-
case _ =>
tree
}
@@ -1037,4 +1056,14 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
finally localTyper = saved
}
}
+
+ private def isTraitMethodRequiringStaticImpl(dd: DefDef): Boolean = {
+ val sym = dd.symbol
+ dd.rhs.nonEmpty &&
+ sym.owner.isTrait &&
+ !sym.isPrivate && // no need to put implementations of private methods into a static method
+ !sym.hasFlag(Flags.STATIC)
+ }
+
+ case object NeedStaticImpl extends PlainAttachment
}
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index e894c58b1a..40ab8c0cf8 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -1329,6 +1329,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
class SpecializationDuplicator(casts: Map[Symbol, Type]) extends Duplicator(casts) {
override def retyped(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol, env: scala.collection.Map[Symbol, Type]): Tree =
enteringSpecialize(super.retyped(context, tree, oldThis, newThis, env))
+
}
/** A tree symbol substituter that substitutes on type skolems.
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index e0b1543f24..374e8430d8 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -213,6 +213,7 @@ abstract class UnCurry extends InfoTransform
// Expand the function body into an anonymous class
gen.expandFunction(localTyper)(fun, inConstructorFlag)
} else {
+ val mustExpand = mustExpandFunction(fun)
// method definition with the same arguments, return type, and body as the original lambda
val liftedMethod = gen.mkLiftedFunctionBodyMethod(localTyper)(fun.symbol.owner, fun)
@@ -221,11 +222,18 @@ abstract class UnCurry extends InfoTransform
gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), (fun.vparams map (_.symbol)) :: Nil)
))
+ if (!mustExpand) {
+ liftedMethod.symbol.updateAttachment(DelambdafyTarget)
+ liftedMethod.updateAttachment(DelambdafyTarget)
+ }
+
val typedNewFun = localTyper.typedPos(fun.pos)(Block(liftedMethod, super.transform(newFun)))
- if (mustExpandFunction(fun)) {
+ if (mustExpand) {
val Block(stats, expr : Function) = typedNewFun
treeCopy.Block(typedNewFun, stats, gen.expandFunction(localTyper)(expr, inConstructorFlag))
- } else typedNewFun
+ } else {
+ typedNewFun
+ }
}
def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = {
@@ -341,13 +349,18 @@ abstract class UnCurry extends InfoTransform
private def isSelfSynchronized(ddef: DefDef) = ddef.rhs match {
case Apply(fn @ TypeApply(Select(sel, _), _), _) =>
- fn.symbol == Object_synchronized && sel.symbol == ddef.symbol.enclClass && !ddef.symbol.enclClass.isTrait
+ fn.symbol == Object_synchronized && sel.symbol == ddef.symbol.enclClass && !ddef.symbol.enclClass.isTrait &&
+ !ddef.symbol.isDelambdafyTarget /* these become static later, unsuitable for ACC_SYNCHRONIZED */
case _ => false
}
/** If an eligible method is entirely wrapped in a call to synchronized
* locked on the same instance, remove the synchronized scaffolding and
* mark the method symbol SYNCHRONIZED for bytecode generation.
+ *
+ * Delambdafy targets are deemed ineligible as the Delambdafy phase will
+ * replace `this.synchronized` with `$this.synchronzed` now that it emits
+ * all lambda impl methods as static.
*/
private def translateSynchronized(tree: Tree) = tree match {
case dd @ DefDef(_, _, _, _, _, Apply(fn, body :: Nil)) if isSelfSynchronized(dd) =>
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
index d4f44303bb..2c1fb064cc 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala
@@ -148,7 +148,7 @@ trait ScalacPatternExpanders {
val tupled = extractor.asSinglePattern
if (effectivePatternArity(args) == 1 && isTupleType(extractor.typeOfSinglePattern)) {
val sym = sel.symbol.owner
- currentRun.reporting.deprecationWarning(sel.pos, sym, s"${sym} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)")
+ currentRun.reporting.deprecationWarning(sel.pos, sym, s"${sym} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)", "2.11.0")
}
tupled
} else extractor
diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala
index 2f4d228347..57de44a038 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala
@@ -74,10 +74,10 @@ trait Adaptations {
if (settings.future)
context.error(t.pos, adaptWarningMessage("Adaptation of argument list by inserting () has been removed.", showAdaptation = false))
else {
- val msg = "Adaptation of argument list by inserting () has been deprecated: " + (
+ val msg = "Adaptation of argument list by inserting () is deprecated: " + (
if (isLeakyTarget) "leaky (Object-receiving) target makes this especially dangerous."
else "this is unlikely to be what you want.")
- context.deprecationWarning(t.pos, t.symbol, adaptWarningMessage(msg))
+ context.deprecationWarning(t.pos, t.symbol, adaptWarningMessage(msg), "2.11.0")
}
} 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."))
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index e190b57017..81a465ef2f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -198,7 +198,7 @@ trait ContextErrors {
val foundType: Type = req.dealiasWiden match {
case RefinedType(parents, decls) if !decls.isEmpty && found.typeSymbol.isAnonOrRefinementClass =>
val retyped = typed (tree.duplicate.clearType())
- val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic)
+ val foundDecls = retyped.tpe.decls filter (sym => !sym.isConstructor && !sym.isSynthetic && !sym.isErroneous)
if (foundDecls.isEmpty || (found.typeSymbol eq NoSymbol)) found
else {
// The members arrive marked private, presumably because there was no
@@ -212,7 +212,8 @@ trait ContextErrors {
case _ =>
found
}
- assert(!foundType.isErroneous && !req.isErroneous, (foundType, req))
+ assert(!foundType.isErroneous, s"AdaptTypeError - foundType is Erroneous: $foundType")
+ assert(!req.isErroneous, s"AdaptTypeError - req is Erroneous: $req")
issueNormalTypeError(callee, withAddendum(callee.pos)(typeErrorMsg(foundType, req)))
infer.explainTypes(foundType, req)
@@ -538,8 +539,43 @@ trait ContextErrors {
def NamedAndDefaultArgumentsNotSupportedForMacros(tree: Tree, fun: Tree) =
NormalTypeError(tree, "macro applications do not support named and/or default arguments")
- def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree) =
- NormalTypeError(tree, "too many arguments for "+treeSymTypeMsg(fun))
+ def TooManyArgsNamesDefaultsError(tree: Tree, fun: Tree, formals: List[Type], args: List[Tree], namelessArgs: List[Tree], argPos: Array[Int]) = {
+ val expected = formals.size
+ val supplied = args.size
+ // pick a caret. For f(k=1,i=2,j=3), argPos[0,-1,1] b/c `k=1` taken as arg0
+ val excessive = {
+ val i = argPos.indexWhere(_ >= expected)
+ if (i < 0) tree else args(i min (supplied - 1))
+ }
+ val msg = {
+ val badappl = {
+ val excess = supplied - expected
+ val target = treeSymTypeMsg(fun)
+
+ if (expected == 0) s"no arguments allowed for nullary $target"
+ else if (excess < 3 && expected <= 5) s"too many arguments ($supplied) for $target"
+ else if (expected > 10) s"$supplied arguments but expected $expected for $target"
+ else {
+ val more =
+ if (excess == 1) "one more argument"
+ else if (excess > 0) s"$excess more arguments"
+ else "too many arguments"
+ s"$more than can be applied to $target"
+ }
+ }
+ val unknowns = (namelessArgs zip args) collect {
+ case (_: Assign, AssignOrNamedArg(Ident(name), _)) => name
+ }
+ val suppl =
+ unknowns.size match {
+ case 0 => ""
+ case 1 => s"\nNote that '${unknowns.head}' is not a parameter name of the invoked method."
+ case _ => unknowns.mkString("\nNote that '", "', '", "' are not parameter names of the invoked method.")
+ }
+ s"${badappl}${suppl}"
+ }
+ NormalTypeError(excessive, msg)
+ }
// can it still happen? see test case neg/overloaded-unapply.scala
def OverloadedUnapplyError(tree: Tree) =
@@ -551,7 +587,7 @@ trait ContextErrors {
def MultipleVarargError(tree: Tree) =
NormalTypeError(tree, "when using named arguments, the vararg parameter has to be specified exactly once")
- def ModuleUsingCompanionClassDefaultArgsErrror(tree: Tree) =
+ def ModuleUsingCompanionClassDefaultArgsError(tree: Tree) =
NormalTypeError(tree, "module extending its companion class cannot use default constructor arguments")
def NotEnoughArgsError(tree: Tree, fun: Tree, missing: List[Symbol]) = {
@@ -631,7 +667,7 @@ trait ContextErrors {
//adapt
def MissingArgsForMethodTpeError(tree: Tree, meth: Symbol) = {
- val f = meth.name
+ val f = meth.name.decoded
val paf = s"$f(${ meth.asMethod.paramLists map (_ map (_ => "_") mkString ",") mkString ")(" })"
val advice = s"""
|Unapplied methods are only converted to functions when a function type is expected.
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index bcc1ed3e64..16ef75c863 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -586,8 +586,8 @@ trait Contexts { self: Analyzer =>
}
- def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit =
- currentRun.reporting.deprecationWarning(fixPosition(pos), sym, msg)
+ def deprecationWarning(pos: Position, sym: Symbol, msg: String, since: String): Unit =
+ currentRun.reporting.deprecationWarning(fixPosition(pos), sym, msg, since)
def deprecationWarning(pos: Position, sym: Symbol): Unit =
currentRun.reporting.deprecationWarning(fixPosition(pos), sym) // TODO: allow this to escalate to an error, and implicit search will ignore deprecated implicits
diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
index 0c10242950..78e72cf771 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
@@ -229,7 +229,12 @@ abstract class Duplicators extends Analyzer {
case ddef @ DefDef(_, _, _, _, tpt, rhs) =>
ddef.tpt modifyType fixType
- super.typed(ddef.clearType(), mode, pt)
+ val result = super.typed(ddef.clearType(), mode, pt)
+ // TODO this is a hack, we really need a cleaner way to transport symbol attachments to duplicated methods
+ // bodies in specialized subclasses.
+ if (ddef.hasAttachment[DelambdafyTarget.type])
+ result.symbol.updateAttachment(DelambdafyTarget)
+ result
case fun: Function =>
debuglog("Clearing the type and retyping Function: " + fun)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index dc91d23011..7112edd75d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1257,7 +1257,6 @@ trait Infer extends Checkable {
def isFreeTypeParamOfTerm(sym: Symbol) = (
sym.isAbstractType
&& sym.owner.isTerm
- && !sym.info.bounds.exists(_.typeParams.nonEmpty)
)
// Intentionally *not* using `Type#typeSymbol` here, which would normalize `tp`
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 8943ec810d..9c1ba7ced1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -431,6 +431,7 @@ trait Namers extends MethodSynthesis {
&& !(module isCoDefinedWith clazz)
&& module.exists
&& clazz.exists
+ && (currentRun.compiles(clazz) == currentRun.compiles(module))
)
if (fails) {
reporter.error(tree.pos, (
@@ -463,7 +464,7 @@ trait Namers extends MethodSynthesis {
// opening up the package object on the classpath at all if one exists in source.
if (m.isPackageObject) {
val packageScope = m.enclosingPackageClass.rawInfo.decls
- packageScope.filter(_.owner != m.enclosingPackageClass).toList.foreach(packageScope unlink _)
+ packageScope.foreach(mem => if (mem.owner != m.enclosingPackageClass) packageScope unlink mem)
}
updatePosFlags(m, tree.pos, moduleFlags)
setPrivateWithin(tree, m)
diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
index 5062289ed1..ab6837ec01 100644
--- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -559,20 +559,22 @@ trait NamesDefaults { self: Analyzer =>
def removeNames(typer: Typer)(args: List[Tree], params: List[Symbol]): (List[Tree], Array[Int]) = {
implicit val context0 = typer.context
def matchesName(param: Symbol, name: Name, argIndex: Int) = {
- def warn(w: String) = context0.deprecationWarning(args(argIndex).pos, param, w)
+ def warn(msg: String, since: String) = context0.deprecationWarning(args(argIndex).pos, param, msg, since)
def checkDeprecation(anonOK: Boolean) =
when (param.deprecatedParamName) {
case Some(`name`) => true
case Some(nme.NO_NAME) => anonOK
}
+ def version = param.deprecatedParamVersion.getOrElse("")
+ def since = if (version.isEmpty) version else s" (since $version)"
def checkName = {
val res = param.name == name
- if (res && checkDeprecation(true)) warn(s"naming parameter $name has been deprecated.")
+ if (res && checkDeprecation(true)) warn(s"naming parameter $name is deprecated$since.", version)
res
}
def checkAltName = {
val res = checkDeprecation(false)
- if (res) warn(s"the parameter name $name has been deprecated. Use ${param.name} instead.")
+ if (res) warn(s"the parameter name $name is deprecated$since: use ${param.name} instead", version)
res
}
!param.isSynthetic && (checkName || checkAltName)
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index da269168ec..d1764ea482 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -172,12 +172,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// This has become noisy with implicit classes.
if (settings.warnPolyImplicitOverload && settings.developer) {
- clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym =>
+ clazz.info.decls.foreach(sym => if (sym.isImplicit && sym.typeParams.nonEmpty) {
// implicit classes leave both a module symbol and a method symbol as residue
val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule)
if (alts.size > 1)
alts foreach (x => reporter.warning(x.pos, "parameterized overloaded implicit methods are not visible as view bounds"))
- }
+ })
}
}
@@ -548,9 +548,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
def checkOverrideDeprecated() {
if (other.hasDeprecatedOverridingAnnotation && !member.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) {
- val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse ""
- val msg = s"overriding ${other.fullLocationString} is deprecated$suffix"
- currentRun.reporting.deprecationWarning(member.pos, other, msg)
+ val version = other.deprecatedOverridingVersion.getOrElse("")
+ val since = if (version.isEmpty) version else s" (since $version)"
+ val message = other.deprecatedOverridingMessage map (msg => s": $msg") getOrElse ""
+ val report = s"overriding ${other.fullLocationString} is deprecated$since$message"
+ currentRun.reporting.deprecationWarning(member.pos, other, report, version)
}
}
}
@@ -1422,7 +1424,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
currentRun.reporting.deprecationWarning(
tree.pos,
symbol,
- s"${symbol.toString} overrides concrete, non-deprecated symbol(s): ${concrOvers.map(_.name.decode).mkString(", ")}")
+ s"${symbol.toString} overrides concrete, non-deprecated symbol(s): ${concrOvers.map(_.name.decode).mkString(", ")}", "")
}
}
private def isRepeatedParamArg(tree: Tree) = currentApplication match {
@@ -1657,24 +1659,32 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// inside annotations.
applyRefchecksToAnnotations(tree)
var result: Tree = tree match {
- case DefDef(_, _, _, _, _, EmptyTree) if sym hasAnnotation NativeAttr =>
- sym resetFlag DEFERRED
- transform(deriveDefDef(tree)(_ => typed(gen.mkSysErrorCall("native method stub"))))
-
- case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) =>
+ case vod: ValOrDefDef =>
checkDeprecatedOvers(tree)
- checkInfiniteLoop(tree.asInstanceOf[ValOrDefDef])
+ checkInfiniteLoop(vod)
if (settings.warnNullaryUnit)
checkNullaryMethodReturnType(sym)
if (settings.warnInaccessible) {
if (!sym.isConstructor && !sym.isEffectivelyFinalOrNotOverridden && !sym.isSynthetic)
checkAccessibilityOfReferencedTypes(tree)
}
- tree match {
- case dd: DefDef => checkByNameRightAssociativeDef(dd)
- case _ =>
+ vod match {
+ case dd: DefDef =>
+ checkByNameRightAssociativeDef(dd)
+
+ if (sym hasAnnotation NativeAttr) {
+ if (sym.owner.isTrait) {
+ reporter.error(tree.pos, "A trait cannot define a native method.")
+ tree
+ } else if (dd.rhs == EmptyTree) {
+ // pretend it had a stub implementation
+ sym resetFlag DEFERRED
+ deriveDefDef(dd)(_ => typed(gen.mkSysErrorCall("native method stub")))
+ } else tree
+ } else tree
+
+ case _ => tree
}
- tree
case Template(parents, self, body) =>
localTyper = localTyper.atOwner(tree, currentOwner)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 329ce8c23b..9fa3564b2b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1704,9 +1704,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (!isPastTyper && psym.hasDeprecatedInheritanceAnnotation &&
!sameSourceFile && !context.owner.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) {
- val suffix = psym.deprecatedInheritanceMessage map (": " + _) getOrElse ""
- val msg = s"inheritance from ${psym.fullLocationString} is deprecated$suffix"
- context.deprecationWarning(parent.pos, psym, msg)
+ val version = psym.deprecatedInheritanceVersion.getOrElse("")
+ val since = if (version.isEmpty) version else s" (since $version)"
+ val message = psym.deprecatedInheritanceMessage.map(msg => s": $msg").getOrElse("")
+ val report = s"inheritance from ${psym.fullLocationString} is deprecated$since$message"
+ context.deprecationWarning(parent.pos, psym, report, version)
}
if (psym.isSealed && !phase.erasedTypes)
@@ -2066,35 +2068,39 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ =>
(call, Nil)
}
- val (superConstr, superArgs) = decompose(rhs)
- assert(superConstr.symbol ne null, superConstr)//debug
- def superClazz = superConstr.symbol.owner
- def superParamAccessors = superClazz.constrParamAccessors
// associate superclass paramaccessors with their aliases
- if (superConstr.symbol.isPrimaryConstructor && !superClazz.isJavaDefined && sameLength(superParamAccessors, superArgs)) {
- for ((superAcc, superArg @ Ident(name)) <- superParamAccessors zip superArgs) {
- if (mexists(vparamss)(_.symbol == superArg.symbol)) {
- val alias = (
- superAcc.initialize.alias
- orElse (superAcc getterIn superAcc.owner)
- filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias)
- )
- if (alias.exists && !alias.accessed.isVariable && !isRepeatedParamType(alias.accessed.info)) {
- val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match {
- case acc if !acc.isDeferred && acc.hasAccessorFlag => acc.accessed
- case acc => acc
- }
- ownAcc match {
- case acc: TermSymbol if !acc.isVariable && !isByNameParamType(acc.info) =>
- debuglog(s"$acc has alias ${alias.fullLocationString}")
- acc setAlias alias
- case _ =>
+ val (superConstr, superArgs) = decompose(rhs)
+ if (superConstr.symbol.isPrimaryConstructor) {
+ val superClazz = superConstr.symbol.owner
+ if (!superClazz.isJavaDefined) {
+ val superParamAccessors = superClazz.constrParamAccessors
+ if (sameLength(superParamAccessors, superArgs)) {
+ for ((superAcc, superArg@Ident(name)) <- superParamAccessors zip superArgs) {
+ if (mexists(vparamss)(_.symbol == superArg.symbol)) {
+ val alias = (
+ superAcc.initialize.alias
+ orElse (superAcc getterIn superAcc.owner)
+ filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias)
+ )
+ if (alias.exists && !alias.accessed.isVariable && !isRepeatedParamType(alias.accessed.info)) {
+ val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match {
+ case acc if !acc.isDeferred && acc.hasAccessorFlag => acc.accessed
+ case acc => acc
+ }
+ ownAcc match {
+ case acc: TermSymbol if !acc.isVariable && !isByNameParamType(acc.info) =>
+ debuglog(s"$acc has alias ${alias.fullLocationString}")
+ acc setAlias alias
+ case _ =>
+ }
+ }
}
}
}
}
}
+
pending.foreach(ErrorUtils.issueTypeError)
}
@@ -2389,19 +2395,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
// The block is an anonymous class definitions/instantiation pair
// -> members that are hidden by the type of the block are made private
- val toHide = (
- classDecls filter (member =>
- member.isTerm
- && member.isPossibleInRefinement
- && member.isPublic
- && !matchesVisibleMember(member)
- ) map (member => member
- resetFlag (PROTECTED | LOCAL)
- setFlag (PRIVATE | SYNTHETIC_PRIVATE)
- setPrivateWithin NoSymbol
- )
- )
- syntheticPrivates ++= toHide
+ classDecls foreach { toHide =>
+ if (toHide.isTerm
+ && toHide.isPossibleInRefinement
+ && toHide.isPublic
+ && !matchesVisibleMember(toHide)) {
+ (toHide
+ resetFlag (PROTECTED | LOCAL)
+ setFlag (PRIVATE | SYNTHETIC_PRIVATE)
+ setPrivateWithin NoSymbol)
+
+ syntheticPrivates += toHide
+ }
+ }
+
case _ =>
}
}
@@ -3330,7 +3337,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// #2064
duplErrorTree(WrongNumberOfArgsError(tree, fun))
} else if (lencmp > 0) {
- tryTupleApply orElse duplErrorTree(TooManyArgsNamesDefaultsError(tree, fun))
+ tryTupleApply orElse duplErrorTree {
+ val (namelessArgs, argPos) = removeNames(Typer.this)(args, params)
+ TooManyArgsNamesDefaultsError(tree, fun, formals, args, namelessArgs, argPos)
+ }
} else if (lencmp == 0) {
// we don't need defaults. names were used, so this application is transformed
// into a block (@see transformNamedApplication in NamesDefaults)
@@ -3394,7 +3404,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val lencmp2 = compareLengths(allArgs, formals)
if (!sameLength(allArgs, args) && callToCompanionConstr(context, funSym)) {
- duplErrorTree(ModuleUsingCompanionClassDefaultArgsErrror(tree))
+ duplErrorTree(ModuleUsingCompanionClassDefaultArgsError(tree))
} else if (lencmp2 > 0) {
removeNames(Typer.this)(allArgs, params) // #3818
duplErrTree
@@ -3639,10 +3649,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
reportAnnotationError(MultipleArgumentListForAnnotationError(ann))
}
else {
- val annScope = annType.decls
- .filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined)
+ val annScopeJava =
+ if (isJava) annType.decls.filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined)
+ else EmptyScope // annScopeJava is only used if isJava
+
val names = mutable.Set[Symbol]()
- names ++= (if (isJava) annScope.iterator
+ names ++= (if (isJava) annScopeJava.iterator
else typedFun.tpe.params.iterator)
def hasValue = names exists (_.name == nme.value)
@@ -3653,7 +3665,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val nvPairs = args map {
case arg @ AssignOrNamedArg(Ident(name), rhs) =>
- val sym = if (isJava) annScope.lookup(name)
+ val sym = if (isJava) annScopeJava.lookup(name)
else findSymbol(typedFun.tpe.params)(_.name == name)
if (sym == NoSymbol) {
reportAnnotationError(UnknownAnnotationNameError(arg, name))
@@ -3709,7 +3721,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2)
- context.deprecationWarning(ann.pos, DeprecatedAttr, "@deprecated now takes two arguments; see the scaladoc.")
+ context.deprecationWarning(ann.pos, DeprecatedAttr, "@deprecated now takes two arguments; see the scaladoc.", "2.11.0")
if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) ErroneousAnnotation
else annInfo(typedAnn)
@@ -4494,7 +4506,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val opeqStart = if (Statistics.canEnable) Statistics.startTimer(failedOpEqNanos) else null
def onError(reportError: => Tree): Tree = fun match {
- case Select(qual, name) if !mode.inPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) =>
+ case Select(qual, name)
+ if !mode.inPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) && !qual.exists(_.isErroneous) =>
+
val qual1 = typedQualifier(qual)
if (treeInfo.isVariableOrGetter(qual1)) {
if (Statistics.canEnable) Statistics.stopTimer(failedOpEqNanos, opeqStart)
@@ -4782,7 +4796,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// temporarily use `filter` as an alternative for `withFilter`
def tryWithFilterAndFilter(tree: Select, qual: Tree): Tree = {
- def warn(sym: Symbol) = context.deprecationWarning(tree.pos, sym, s"`withFilter' method does not yet exist on ${qual.tpe.widen}, using `filter' method instead")
+ def warn(sym: Symbol) = context.deprecationWarning(tree.pos, sym, s"`withFilter' method does not yet exist on ${qual.tpe.widen}, using `filter' method instead", "2.11.0")
silent(_ => typedSelect(tree, qual, nme.withFilter)) orElse { _ =>
silent(_ => typed1(Select(qual, nme.filter) setPos tree.pos, mode, pt)) match {
case SilentResultValue(res) => warn(res.symbol) ; res
@@ -5577,7 +5591,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
def reportWarning(inferredType: Type) = {
val explanation = s"inference of $inferredType from macro impl's c.Expr[$inferredType] is deprecated and is going to stop working in 2.12"
- context.deprecationWarning(ddef.pos, ddef.symbol, s"$commonMessage ($explanation)")
+ context.deprecationWarning(ddef.pos, ddef.symbol, s"$commonMessage ($explanation)", "2.12.0")
}
computeMacroDefTypeFromMacroImplRef(ddef, rhs1) match {
case ErrorType => ErrorType
diff --git a/src/compiler/scala/tools/nsc/util/StackTracing.scala b/src/compiler/scala/tools/nsc/util/StackTracing.scala
index fa4fe29f28..0765bb923f 100644
--- a/src/compiler/scala/tools/nsc/util/StackTracing.scala
+++ b/src/compiler/scala/tools/nsc/util/StackTracing.scala
@@ -19,7 +19,7 @@ private[util] trait StackTracing extends Any {
def stackTracePrefixString(e: Throwable)(p: StackTraceElement => Boolean): String = {
import collection.mutable.{ ArrayBuffer, ListBuffer }
import compat.Platform.EOL
- import util.Properties.isJavaAtLeast
+ import scala.util.Properties.isJavaAtLeast
val sb = ListBuffer.empty[String]
diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala
index cbdb01a10a..9825acd39f 100644
--- a/src/compiler/scala/tools/reflect/FormatInterpolator.scala
+++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala
@@ -117,7 +117,7 @@ abstract class FormatInterpolator {
c.error(errPoint, msg("unsupported"))
s0
} else {
- currentRun.reporting.deprecationWarning(errPoint, msg("deprecated"))
+ currentRun.reporting.deprecationWarning(errPoint, msg("deprecated"), "2.11.0")
try StringContext.treatEscapes(s0) catch escapeHatch
}
}