summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bincompat-backward.whitelist.conf5
-rw-r--r--bincompat-forward.whitelist.conf8
-rwxr-xr-xscripts/jobs/integrate/bootstrap2
-rw-r--r--spec/01-lexical-syntax.md2
-rw-r--r--spec/13-syntax-summary.md2
-rw-r--r--spec/_layouts/default.yml4
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala3
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala13
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala76
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala42
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala311
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala205
-rw-r--r--src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala12
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala1
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala95
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Logic.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala2
-rw-r--r--src/interactive/scala/tools/nsc/interactive/Global.scala3
-rw-r--r--src/library/scala/collection/mutable/HashMap.scala31
-rw-r--r--src/library/scala/concurrent/SyncVar.scala4
-rw-r--r--src/library/scala/inline.scala2
-rw-r--r--src/library/scala/noinline.scala2
-rw-r--r--src/library/scala/sys/process/ProcessBuilderImpl.scala14
-rw-r--r--src/library/scala/sys/process/ProcessImpl.scala34
-rw-r--r--src/reflect/scala/reflect/internal/StdAttachments.scala3
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala9
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala5
-rw-r--r--src/reflect/scala/reflect/internal/tpe/TypeMaps.scala4
-rw-r--r--src/reflect/scala/reflect/internal/transform/UnCurry.scala69
-rw-r--r--src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala28
-rw-r--r--src/reflect/scala/reflect/internal/util/SourceFile.scala23
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala2
-rw-r--r--src/repl/scala/tools/nsc/interpreter/IMain.scala2
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala13
-rw-r--r--test/benchmarks/README.md7
-rw-r--r--test/benchmarks/build.sbt4
-rw-r--r--test/benchmarks/project/plugins.sbt2
-rw-r--r--test/benchmarks/src/main/scala/scala/collection/immutable/VectorMapBenchmark.scala32
-rw-r--r--test/benchmarks/src/main/scala/scala/collection/mutable/HashMapBenchmark.scala70
-rw-r--r--test/files/jvm/varargs-separate-bytecode.check1
-rw-r--r--test/files/jvm/varargs-separate-bytecode/AbstractProps_1.scala8
-rw-r--r--test/files/jvm/varargs-separate-bytecode/Props_2.scala3
-rw-r--r--test/files/jvm/varargs-separate-bytecode/Test.scala15
-rw-r--r--test/files/neg/sabin2.check2
-rw-r--r--test/files/neg/sealed-final-neg.check6
-rw-r--r--test/files/neg/t0764.check2
-rw-r--r--test/files/neg/t1010.check2
-rw-r--r--test/files/neg/t5120.check2
-rw-r--r--test/files/neg/t6829.check14
-rw-r--r--test/files/run/reflection-mem-typecheck.scala28
-rw-r--r--test/files/run/repl-inline.check11
-rw-r--r--test/files/run/repl-inline.scala27
-rw-r--r--test/files/run/t5125b.check3
-rw-r--r--test/files/run/t5125b.scala12
-rw-r--r--test/files/run/t7747-repl.check6
-rw-r--r--test/files/run/t9814.scala28
-rw-r--r--test/junit/scala/reflect/internal/util/SourceFileTest.scala19
-rw-r--r--test/junit/scala/sys/process/PipedProcessTest.scala (renamed from test/junit/scala/sys/process/t7350.scala)1
-rw-r--r--test/junit/scala/sys/process/ProcessTest.scala25
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala13
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala6
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala22
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala2
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala144
-rw-r--r--test/junit/scala/tools/nsc/classpath/VirtualDirectoryClassPathTest.scala41
-rw-r--r--test/junit/scala/tools/nsc/interpreter/CompletionTest.scala21
71 files changed, 1171 insertions, 454 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf
index bb94f4be6c..af80bedf5b 100644
--- a/bincompat-backward.whitelist.conf
+++ b/bincompat-backward.whitelist.conf
@@ -12,7 +12,10 @@ filter {
{
matchName="scala.reflect.runtime.SymbolLoaders#TopClassCompleter.this"
problemName=IncompatibleMethTypeProblem
+ },
+ {
+ matchName="scala.sys.process.ProcessImpl#CompoundProcess.getExitValue"
+ problemName=DirectMissingMethodProblem
}
-
]
}
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf
index 705fa031ab..541268e50e 100644
--- a/bincompat-forward.whitelist.conf
+++ b/bincompat-forward.whitelist.conf
@@ -12,6 +12,14 @@ filter {
{
matchName="scala.reflect.runtime.SymbolLoaders#TopClassCompleter.this"
problemName=IncompatibleMethTypeProblem
+ },
+ {
+ matchName="scala.sys.process.ProcessImpl#CompoundProcess.futureValue"
+ problemName=DirectMissingMethodProblem
+ },
+ {
+ matchName="scala.sys.process.ProcessImpl#CompoundProcess.futureThread"
+ problemName=DirectMissingMethodProblem
}
]
}
diff --git a/scripts/jobs/integrate/bootstrap b/scripts/jobs/integrate/bootstrap
index ed1e05251a..7c045ae918 100755
--- a/scripts/jobs/integrate/bootstrap
+++ b/scripts/jobs/integrate/bootstrap
@@ -430,7 +430,7 @@ removeExistingBuilds() {
local scalaLangModules=`curl -s $storageApiUrl/org/scala-lang | jq -r '.children | .[] | "org/scala-lang" + .uri' | grep -v actors-migration`
for module in $scalaLangModules; do
- local artifacts=`curl -s $storageApiUrl/$module | jq -r ".children | .[] | select(.uri | contains(\"$SCALA_VER\")) | .uri"`
+ local artifacts=`curl -s $storageApiUrl/$module | jq -r ".children | .[] | select(.uri | endswith(\"$SCALA_VER\")) | .uri"`
for artifact in $artifacts; do
echo "Deleting $releaseTempRepoUrl$module$artifact"
curl -s --netrc-file $netrcFile -X DELETE $releaseTempRepoUrl$module$artifact
diff --git a/spec/01-lexical-syntax.md b/spec/01-lexical-syntax.md
index e4764c10dc..78f1a1a408 100644
--- a/spec/01-lexical-syntax.md
+++ b/spec/01-lexical-syntax.md
@@ -55,7 +55,7 @@ plainid ::= upper idrest
| varid
| op
id ::= plainid
- | ‘`’ stringLiteral ‘`’
+ | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’
idrest ::= {letter | digit} [‘_’ op]
```
diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md
index dd042824f4..be5cc1324e 100644
--- a/spec/13-syntax-summary.md
+++ b/spec/13-syntax-summary.md
@@ -38,7 +38,7 @@ plainid ::= upper idrest
| varid
| op
id ::= plainid
- | ‘`’ stringLiteral ‘`’
+ | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’
idrest ::= {letter | digit} [‘_’ op]
integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’]
diff --git a/spec/_layouts/default.yml b/spec/_layouts/default.yml
index 06d8c1c118..983d4e56ae 100644
--- a/spec/_layouts/default.yml
+++ b/spec/_layouts/default.yml
@@ -15,9 +15,9 @@
}
});
</script>
- <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/2.6-latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
+ <script type="text/javascript" src="//cdn.mathjax.org/mathjax/2.6-latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
- <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.2/styles/default.min.css">
+ <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.2/styles/default.min.css">
<!-- need to use include to see value of page.chapter variable -->
<style type="text/css">
{% include numbering.css %}
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index a7880c72d7..e58d2d3b43 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -95,6 +95,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
type ThisPlatform = JavaPlatform { val global: Global.this.type }
lazy val platform: ThisPlatform = new GlobalPlatform
+ /* A hook for the REPL to add a classpath entry containing products of previous runs to inliner's bytecode repository*/
+ // Fixes SI-8779
+ def optimizerClassPath(base: ClassPath): ClassPath = base
def classPath: ClassPath = platform.classPath
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index 151926b8e7..121091fe4f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -123,10 +123,19 @@ abstract class BTypes {
* has the method.
*/
val indyLambdaImplMethods: mutable.AnyRefMap[InternalName, mutable.LinkedHashSet[asm.Handle]] = recordPerRunCache(mutable.AnyRefMap())
- def addIndyLambdaImplMethod(hostClass: InternalName, handle: Seq[asm.Handle]): Unit = {
+ def addIndyLambdaImplMethod(hostClass: InternalName, handle: Seq[asm.Handle]): Seq[asm.Handle] = {
+ if (handle.isEmpty) Nil else {
+ val set = indyLambdaImplMethods.getOrElseUpdate(hostClass, mutable.LinkedHashSet())
+ val added = handle.filterNot(set)
+ set ++= handle
+ added
+ }
+ }
+ def removeIndyLambdaImplMethod(hostClass: InternalName, handle: Seq[asm.Handle]): Unit = {
if (handle.nonEmpty)
- indyLambdaImplMethods.getOrElseUpdate(hostClass, mutable.LinkedHashSet()) ++= handle
+ indyLambdaImplMethods.getOrElseUpdate(hostClass, mutable.LinkedHashSet()) --= handle
}
+
def getIndyLambdaImplMethods(hostClass: InternalName): Iterable[asm.Handle] = {
indyLambdaImplMethods.getOrNull(hostClass) match {
case null => Nil
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index edb75514e8..f7ee36c1ba 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -37,7 +37,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val coreBTypes = new CoreBTypesProxy[this.type](this)
import coreBTypes._
- val byteCodeRepository: ByteCodeRepository[this.type] = new ByteCodeRepository(global.classPath, this)
+ val byteCodeRepository: ByteCodeRepository[this.type] = new ByteCodeRepository(global.optimizerClassPath(global.classPath), this)
val localOpt: LocalOpt[this.type] = new LocalOpt(this)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
index 72a371cabc..e6ae073a2a 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
@@ -185,53 +185,61 @@ object BackendReporting {
def name: String
def descriptor: String
- def calleeMethodSig = BackendReporting.methodSignature(calleeDeclarationClass, name, descriptor)
-
- override def toString = this match {
- case IllegalAccessInstruction(_, _, _, callsiteClass, instruction) =>
- s"The callee $calleeMethodSig contains the instruction ${AsmUtils.textify(instruction)}" +
- s"\nthat would cause an IllegalAccessError when inlined into class $callsiteClass."
-
- case IllegalAccessCheckFailed(_, _, _, callsiteClass, instruction, cause) =>
- s"Failed to check if $calleeMethodSig can be safely inlined to $callsiteClass without causing an IllegalAccessError. Checking instruction ${AsmUtils.textify(instruction)} failed:\n" + cause
+ /** Either the callee or the callsite is annotated @inline */
+ def annotatedInline: Boolean
- case MethodWithHandlerCalledOnNonEmptyStack(_, _, _, callsiteClass, callsiteName, callsiteDesc) =>
- s"""The operand stack at the callsite in ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)} contains more values than the
- |arguments expected by the callee $calleeMethodSig. These values would be discarded
- |when entering an exception handler declared in the inlined method.""".stripMargin
-
- case SynchronizedMethod(_, _, _) =>
- s"Method $calleeMethodSig cannot be inlined because it is synchronized."
+ def calleeMethodSig = BackendReporting.methodSignature(calleeDeclarationClass, name, descriptor)
- case StrictfpMismatch(_, _, _, callsiteClass, callsiteName, callsiteDesc) =>
- s"""The callsite method ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)}
- |does not have the same strictfp mode as the callee $calleeMethodSig.
+ override def toString = {
+ val annotWarn = if (annotatedInline) " is annotated @inline but" else ""
+ val warning = s"$calleeMethodSig$annotWarn could not be inlined:\n"
+ val reason = this match {
+ case CalleeNotFinal(_, _, _, _) =>
+ s"The method is not final and may be overridden."
+ case IllegalAccessInstruction(_, _, _, _, callsiteClass, instruction) =>
+ s"The callee $calleeMethodSig contains the instruction ${AsmUtils.textify(instruction)}" +
+ s"\nthat would cause an IllegalAccessError when inlined into class $callsiteClass."
+
+ case IllegalAccessCheckFailed(_, _, _, _, callsiteClass, instruction, cause) =>
+ s"Failed to check if $calleeMethodSig can be safely inlined to $callsiteClass without causing an IllegalAccessError. Checking instruction ${AsmUtils.textify(instruction)} failed:\n" + cause
+
+ case MethodWithHandlerCalledOnNonEmptyStack(_, _, _, _, callsiteClass, callsiteName, callsiteDesc) =>
+ s"""The operand stack at the callsite in ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)} contains more values than the
+ |arguments expected by the callee $calleeMethodSig. These values would be discarded
+ |when entering an exception handler declared in the inlined method.""".stripMargin
+
+ case SynchronizedMethod(_, _, _, _) =>
+ s"Method $calleeMethodSig cannot be inlined because it is synchronized."
+
+ case StrictfpMismatch(_, _, _, _, callsiteClass, callsiteName, callsiteDesc) =>
+ s"""The callsite method ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)}
+ |does not have the same strictfp mode as the callee $calleeMethodSig.
""".stripMargin
- case ResultingMethodTooLarge(_, _, _, callsiteClass, callsiteName, callsiteDesc) =>
- s"""The size of the callsite method ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)}
- |would exceed the JVM method size limit after inlining $calleeMethodSig.
+ case ResultingMethodTooLarge(_, _, _, _, callsiteClass, callsiteName, callsiteDesc) =>
+ s"""The size of the callsite method ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)}
+ |would exceed the JVM method size limit after inlining $calleeMethodSig.
""".stripMargin
+ }
+ warning + reason
}
- def emitWarning(settings: ScalaSettings): Boolean = this match {
- case _: IllegalAccessInstruction | _: MethodWithHandlerCalledOnNonEmptyStack | _: SynchronizedMethod | _: StrictfpMismatch | _: ResultingMethodTooLarge =>
- settings.optWarnings.contains(settings.optWarningsChoices.anyInlineFailed)
-
- case IllegalAccessCheckFailed(_, _, _, _, _, cause) =>
- cause.emitWarning(settings)
+ def emitWarning(settings: ScalaSettings): Boolean = {
+ settings.optWarnings.contains(settings.optWarningsChoices.anyInlineFailed) ||
+ annotatedInline && settings.optWarningEmitAtInlineFailed
}
}
- case class IllegalAccessInstruction(calleeDeclarationClass: InternalName, name: String, descriptor: String,
+ case class CalleeNotFinal(calleeDeclarationClass: InternalName, name: String, descriptor: String, annotatedInline: Boolean) extends CannotInlineWarning
+ case class IllegalAccessInstruction(calleeDeclarationClass: InternalName, name: String, descriptor: String, annotatedInline: Boolean,
callsiteClass: InternalName, instruction: AbstractInsnNode) extends CannotInlineWarning
- case class IllegalAccessCheckFailed(calleeDeclarationClass: InternalName, name: String, descriptor: String,
+ case class IllegalAccessCheckFailed(calleeDeclarationClass: InternalName, name: String, descriptor: String, annotatedInline: Boolean,
callsiteClass: InternalName, instruction: AbstractInsnNode, cause: OptimizerWarning) extends CannotInlineWarning
- case class MethodWithHandlerCalledOnNonEmptyStack(calleeDeclarationClass: InternalName, name: String, descriptor: String,
+ case class MethodWithHandlerCalledOnNonEmptyStack(calleeDeclarationClass: InternalName, name: String, descriptor: String, annotatedInline: Boolean,
callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning
- case class SynchronizedMethod(calleeDeclarationClass: InternalName, name: String, descriptor: String) extends CannotInlineWarning
- case class StrictfpMismatch(calleeDeclarationClass: InternalName, name: String, descriptor: String,
+ case class SynchronizedMethod(calleeDeclarationClass: InternalName, name: String, descriptor: String, annotatedInline: Boolean) extends CannotInlineWarning
+ case class StrictfpMismatch(calleeDeclarationClass: InternalName, name: String, descriptor: String, annotatedInline: Boolean,
callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning
- case class ResultingMethodTooLarge(calleeDeclarationClass: InternalName, name: String, descriptor: String,
+ case class ResultingMethodTooLarge(calleeDeclarationClass: InternalName, name: String, descriptor: String, annotatedInline: Boolean,
callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning
// TODO: this should be a subtype of CannotInlineWarning
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 e0fd77bb54..9c0dfb0ee2 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -137,7 +137,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
Callee(
callee = method,
calleeDeclarationClass = declarationClassBType,
- safeToInline = safeToInline,
+ isStaticallyResolved = isStaticallyResolved,
sourceFilePath = sourceFilePath,
annotatedInline = annotatedInline,
annotatedNoInline = annotatedNoInline,
@@ -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, sourceFilePath: Option[String],
+ private case class CallsiteInfo(isStaticallyResolved: Boolean, sourceFilePath: Option[String],
annotatedInline: Boolean, annotatedNoInline: Boolean,
samParamTypes: IntMap[ClassBType],
warning: Option[CalleeInfoWarning])
@@ -293,7 +293,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// TODO: type analysis can render more calls statically resolved. Example:
// new A.f // can be inlined, the receiver type is known to be exactly A.
val isStaticallyResolved: Boolean = {
- isNonVirtualCall(call) || // SD-86: super calls (invokespecial) can be inlined
+ isNonVirtualCall(call) || // SD-86: super calls (invokespecial) can be inlined -- TODO: check if that's still needed, and if it's correct: scala-dev#143
methodInlineInfo.effectivelyFinal ||
receiverType.info.orThrow.inlineInfo.isEffectivelyFinal // (1)
}
@@ -301,22 +301,13 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
val warning = calleeDeclarationClassBType.info.orThrow.inlineInfo.warning.map(
MethodInlineInfoIncomplete(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, _))
- // (1) For invocations of final trait methods, the callee isStaticallyResolved but also
- // abstract. Such a callee is not safe to inline - it needs to be re-written to the
- // static impl method first (safeToRewrite).
CallsiteInfo(
- safeToInline =
- inlinerHeuristics.canInlineFromSource(calleeSourceFilePath) &&
- isStaticallyResolved && // (1)
- !isAbstract &&
- !BytecodeUtils.isConstructor(calleeMethodNode) &&
- !BytecodeUtils.isNativeMethod(calleeMethodNode) &&
- !BytecodeUtils.hasCallerSensitiveAnnotation(calleeMethodNode),
- sourceFilePath = calleeSourceFilePath,
- annotatedInline = methodInlineInfo.annotatedInline,
- annotatedNoInline = methodInlineInfo.annotatedNoInline,
- samParamTypes = samParamTypes(calleeMethodNode, receiverType),
- warning = warning)
+ isStaticallyResolved = isStaticallyResolved,
+ sourceFilePath = calleeSourceFilePath,
+ annotatedInline = methodInlineInfo.annotatedInline,
+ annotatedNoInline = methodInlineInfo.annotatedNoInline,
+ samParamTypes = samParamTypes(calleeMethodNode, receiverType),
+ warning = warning)
case None =>
val warning = MethodInlineInfoMissing(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, calleeDeclarationClassBType.info.orThrow.inlineInfo.warning)
@@ -353,6 +344,10 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
*/
val inlinedClones = mutable.Set.empty[ClonedCallsite]
+ // an annotation at the callsite takes precedence over an annotation at the definition site
+ def isInlineAnnotated = annotatedInline || (callee.get.annotatedInline && !annotatedNoInline)
+ def isNoInlineAnnotated = annotatedNoInline || (callee.get.annotatedNoInline && !annotatedInline)
+
override def toString =
"Invocation of" +
s" ${callee.map(_.calleeDeclarationClass.internalName).getOrElse("?")}.${callsiteInstruction.name + callsiteInstruction.desc}" +
@@ -378,8 +373,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
* virtual calls, an override of the callee might be invoked. Also,
* the callee can be abstract.
* @param calleeDeclarationClass The class in which the callee is declared
- * @param safeToInline True if the callee can be safely inlined: it cannot be overridden,
- * and the inliner settings (project / global) allow inlining it.
+ * @param isStaticallyResolved True if the callee cannot be overridden
* @param annotatedInline True if the callee is annotated @inline
* @param annotatedNoInline True if the callee is annotated @noinline
* @param samParamTypes A map from parameter positions to SAM parameter types
@@ -387,11 +381,17 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
* gathering the information about this callee.
*/
final case class Callee(callee: MethodNode, calleeDeclarationClass: btypes.ClassBType,
- safeToInline: Boolean, sourceFilePath: Option[String],
+ isStaticallyResolved: Boolean, sourceFilePath: Option[String],
annotatedInline: Boolean, annotatedNoInline: Boolean,
samParamTypes: IntMap[btypes.ClassBType],
calleeInfoWarning: Option[CalleeInfoWarning]) {
override def toString = s"Callee($calleeDeclarationClass.${callee.name})"
+
+ def canInlineFromSource = inlinerHeuristics.canInlineFromSource(sourceFilePath)
+ def isAbstract = isAbstractMethod(callee)
+ def isSpecialMethod = isConstructor(callee) || isNativeMethod(callee) || hasCallerSensitiveAnnotation(callee)
+
+ def safeToInline = isStaticallyResolved && canInlineFromSource && !isAbstract && !isSpecialMethod
}
/**
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 35ee5ba13d..2fca8991ab 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
@@ -359,7 +359,7 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
Callee(
callee = bodyMethodNode,
calleeDeclarationClass = bodyDeclClassType,
- safeToInline = inlinerHeuristics.canInlineFromSource(sourceFilePath),
+ isStaticallyResolved = true,
sourceFilePath = sourceFilePath,
annotatedInline = false,
annotatedNoInline = false,
@@ -392,7 +392,7 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
// (x: T) => ??? has return type Nothing$, and an ATHROW is added (see fixLoadedNothingOrNullValue).
unreachableCodeEliminated -= ownerMethod
- if (hasAdaptedImplMethod(closureInit) && inliner.canInlineBody(bodyMethodCallsite).isEmpty)
+ if (hasAdaptedImplMethod(closureInit) && inliner.canInlineCallsite(bodyMethodCallsite).isEmpty)
inliner.inlineCallsite(bodyMethodCallsite)
}
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 64638ca34d..b9f593a4d8 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -25,49 +25,110 @@ 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
+ sealed trait InlineLog {
+ def request: InlineRequest
+ }
+ final case class InlineLogSuccess(request: InlineRequest, sizeBefore: Int, sizeInlined: Int) extends InlineLog {
+ var downstreamLog: mutable.Buffer[InlineLog] = mutable.ListBuffer.empty
+ }
+ final case class InlineLogFail(request: InlineRequest, warning: CannotInlineWarning) extends InlineLog
+ final case class InlineLogRollback(request: InlineRequest, warnings: List[CannotInlineWarning]) extends InlineLog
+
+ object InlineLog {
+ private def shouldLog(request: InlineRequest): Boolean = {
+ def logEnabled = compilerSettings.YoptLogInline.isSetByUser
+ def matchesName = {
+ val prefix = compilerSettings.YoptLogInline.value match {
+ case "_" => ""
+ case p => p
+ }
+ val name: String = request.callsite.callsiteClass.internalName + "." + request.callsite.callsiteMethod.name
+ name startsWith prefix
+ }
+ logEnabled && (upstream != null || (isTopLevel && matchesName))
+ }
- def runInliner(): Unit = {
- for (request <- collectAndOrderInlineRequests) {
- val Right(callee) = request.callsite.callee // collectAndOrderInlineRequests returns callsites with a known callee
+ // indexed by callsite method
+ private val logs = mutable.Map.empty[MethodNode, mutable.LinkedHashSet[InlineLog]]
- // TODO: if the request has downstream requests, create a snapshot to which we could roll back in case some downstream callsite cannot be inlined
- // (Needs to revert modifications to the callee method, but also the call graph)
- // (This assumes that inlining a request only makes sense if its downstream requests are satisfied - sync with heuristics!)
+ private var upstream: InlineLogSuccess = _
+ private var isTopLevel = true
- val warnings = inline(request)
- for (warning <- warnings) {
- 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)
- }
+ def withInlineLogging[T](request: InlineRequest)(inlineRequest: => Unit)(inlinePost: => T): T = {
+ def doInlinePost(): T = {
+ val savedIsTopLevel = isTopLevel
+ isTopLevel = false
+ try inlinePost
+ finally isTopLevel = savedIsTopLevel
+ }
+ if (shouldLog(request)) {
+ val sizeBefore = request.callsite.callsiteMethod.instructions.size
+ inlineRequest
+ val log = InlineLogSuccess(request, sizeBefore, request.callsite.callee.get.callee.instructions.size)
+ apply(log)
+
+ val savedUpstream = upstream
+ upstream = log
+ try doInlinePost()
+ finally upstream = savedUpstream
+ } else {
+ inlineRequest
+ doInlinePost()
}
}
- 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()
- }
+ def apply(log: => InlineLog): Unit = if (shouldLog(log.request)) {
+ if (upstream != null) upstream.downstreamLog += log
+ else {
+ val methodLogs = logs.getOrElseUpdate(log.request.callsite.callsiteMethod, mutable.LinkedHashSet.empty)
+ methodLogs += log
+ }
+ }
+
+ def entryString(log: InlineLog, indent: Int = 0): String = {
+ val callee = log.request.callsite.callee.get
+ val calleeString = callee.calleeDeclarationClass.internalName + "." + callee.callee.name
+ val indentString = " " * indent
+ log match {
+ case s @ InlineLogSuccess(_, sizeBefore, sizeInlined) =>
+ val self = s"${indentString}inlined $calleeString. Before: $sizeBefore ins, inlined: $sizeInlined ins."
+ if (s.downstreamLog.isEmpty) self
+ else s.downstreamLog.iterator.map(entryString(_, indent + 2)).mkString(self + "\n", "\n", "")
+
+ case InlineLogFail(_, w) =>
+ s"${indentString}failed $calleeString. ${w.toString.replace('\n', ' ')}"
+
+ case InlineLogRollback(_, _) =>
+ s"${indentString}rolling back, nested inline failed."
+ }
+ }
+
+ def print(): Unit = if (compilerSettings.YoptLogInline.isSetByUser) {
+ val byClassAndMethod: List[(InternalName, mutable.Map[MethodNode, mutable.LinkedHashSet[InlineLog]])] = {
+ logs.
+ groupBy(_._2.head.request.callsite.callsiteClass.internalName).
+ toList.sortBy(_._1)
+ }
+ for {
+ (c, methodLogs) <- byClassAndMethod
+ (m, mLogs) <- methodLogs.toList.sortBy(_._1.name)
+ mLog <- mLogs // insertion order
+ } {
+ println(s"Inline into $c.${m.name}: ${entryString(mLog)}")
+ }
+ }
+ }
+
+ def runInliner(): Unit = {
+ for (request <- collectAndOrderInlineRequests) {
+ val Right(callee) = request.callsite.callee // collectAndOrderInlineRequests returns callsites with a known callee
+ val warnings = inline(request)
+ for (warning <- warnings) {
+ if (warning.emitWarning(compilerSettings))
+ backendReporting.inlinerWarning(request.callsite.callsitePosition, warning.toString)
}
}
+ InlineLog.print()
}
/**
@@ -221,26 +282,79 @@ class Inliner[BT <: BTypes](val btypes: BT) {
impl(post, mainCallsite)
}
+ class UndoLog(active: Boolean = true) {
+ import java.util.{ ArrayList => JArrayList }
+
+ private var actions = List.empty[() => Unit]
+ private var methodStateSaved = false
+
+ def apply(a: => Unit): Unit = if (active) actions = (() => a) :: actions
+ def rollback(): Unit = if (active) actions.foreach(_.apply())
+
+ def saveMethodState(methodNode: MethodNode): Unit = if (active && !methodStateSaved) {
+ methodStateSaved = true
+ val currentInstructions = methodNode.instructions.toArray
+ val currentLocalVariables = new JArrayList(methodNode.localVariables)
+ val currentTryCatchBlocks = new JArrayList(methodNode.tryCatchBlocks)
+ val currentMaxLocals = methodNode.maxLocals
+ val currentMaxStack = methodNode.maxStack
+
+ apply {
+ // `methodNode.instructions.clear()` doesn't work: it keeps the `prev` / `next` / `index` of
+ // instruction nodes. `instructions.removeAll(true)` would work, but is not public.
+ methodNode.instructions.iterator.asScala.toList.foreach(methodNode.instructions.remove)
+ for (i <- currentInstructions) methodNode.instructions.add(i)
+
+ methodNode.localVariables.clear()
+ methodNode.localVariables.addAll(currentLocalVariables)
+
+ methodNode.tryCatchBlocks.clear()
+ methodNode.tryCatchBlocks.addAll(currentTryCatchBlocks)
+
+ methodNode.maxLocals = currentMaxLocals
+ methodNode.maxStack = currentMaxStack
+ }
+ }
+ }
+
+ val NoUndoLogging = new UndoLog(active = false)
/**
* Inline the callsite of an inlining request and its post-inlining requests.
*
* @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) =>
- if (compilerSettings.YoptLogInline.isSetByUser) {
- val size = request.callsite.callsiteMethod.instructions.size
- inlineLog ::= InlineLog(request, size, size, 0, Some(w))
+ def inline(request: InlineRequest, undo: UndoLog = NoUndoLogging): List[CannotInlineWarning] = {
+ def doInline(undo: UndoLog, callRollback: Boolean = false): List[CannotInlineWarning] = {
+ InlineLog.withInlineLogging(request) {
+ inlineCallsite(request.callsite, undo)
+ } {
+ val postRequests = request.post.flatMap(adaptPostRequestForMainCallsite(_, request.callsite))
+ val warnings = postRequests.flatMap(inline(_, undo))
+ if (callRollback && warnings.nonEmpty) {
+ undo.rollback()
+ InlineLog(InlineLogRollback(request, warnings))
+ }
+ warnings
}
- 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
+ }
+
+ def inlinedByPost(insns: List[AbstractInsnNode]): Boolean =
+ insns.nonEmpty && insns.forall(ins => request.post.exists(_.callsite.callsiteInstruction == ins))
+
+ canInlineCallsite(request.callsite) match {
+ case None =>
+ doInline(undo)
+
+ case Some((_, illegalAccessInsns)) if inlinedByPost(illegalAccessInsns) =>
+ // speculatively inline, roll back if an illegalAccessInsn cannot be eliminated
+ if (undo == NoUndoLogging) doInline(new UndoLog(), callRollback = true)
+ else doInline(undo)
+
+ case Some((w, _)) =>
+ InlineLog(InlineLogFail(request, w))
+ List(w)
+ }
}
/**
@@ -253,7 +367,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
* @return A map associating instruction nodes of the callee with the corresponding cloned
* instruction in the callsite method.
*/
- def inlineCallsite(callsite: Callsite): Unit = {
+ def inlineCallsite(callsite: Callsite, undo: UndoLog = NoUndoLogging): Unit = {
import callsite.{callsiteClass, callsiteMethod, callsiteInstruction, receiverKnownNotNull, callsiteStackHeight}
val Right(callsiteCallee) = callsite.callee
import callsiteCallee.{callee, calleeDeclarationClass, sourceFilePath}
@@ -380,6 +494,8 @@ class Inliner[BT <: BTypes](val btypes: BT) {
clonedInstructions.insert(postCallLabel, retVarLoad)
}
+ undo.saveMethodState(callsiteMethod)
+
callsiteMethod.instructions.insert(callsiteInstruction, clonedInstructions)
callsiteMethod.instructions.remove(callsiteInstruction)
@@ -406,7 +522,8 @@ class Inliner[BT <: BTypes](val btypes: BT) {
callsiteMethod.maxStack = math.max(callsiteMethod.maxStack, math.max(stackHeightAtNullCheck, maxStackOfInlinedCode))
- addIndyLambdaImplMethod(callsiteClass.internalName, targetHandles)
+ val added = addIndyLambdaImplMethod(callsiteClass.internalName, targetHandles)
+ undo { removeIndyLambdaImplMethod(callsiteClass.internalName, added) }
callGraph.addIfMissing(callee, calleeDeclarationClass)
@@ -426,8 +543,13 @@ class Inliner[BT <: BTypes](val btypes: BT) {
argInfos = argInfos,
callsiteStackHeight = callsiteStackHeight + originalCallsite.callsiteStackHeight
)
- originalCallsite.inlinedClones += ClonedCallsite(newCallsite, callsite)
+ val clonedCallsite = ClonedCallsite(newCallsite, callsite)
+ originalCallsite.inlinedClones += clonedCallsite
callGraph.addCallsite(newCallsite)
+ undo {
+ originalCallsite.inlinedClones -= clonedCallsite
+ callGraph.removeCallsite(newCallsite.callsiteInstruction, newCallsite.callsiteMethod)
+ }
}
callGraph.closureInstantiations(callee).valuesIterator foreach { originalClosureInit =>
@@ -440,10 +562,14 @@ class Inliner[BT <: BTypes](val btypes: BT) {
capturedArgInfos)
originalClosureInit.inlinedClones += newClosureInit
callGraph.addClosureInstantiation(newClosureInit)
+ undo {
+ callGraph.removeClosureInstantiation(newClosureInit.lambdaMetaFactoryCall.indy, newClosureInit.ownerMethod)
+ }
}
// Remove the elided invocation from the call graph
callGraph.removeCallsite(callsiteInstruction, callsiteMethod)
+ undo { callGraph.addCallsite(callsite) }
// Inlining a method body can render some code unreachable, see example above in this method.
unreachableCodeEliminated -= callsiteMethod
@@ -467,10 +593,10 @@ class Inliner[BT <: BTypes](val btypes: BT) {
if (isSynchronizedMethod(callee)) {
// Could be done by locking on the receiver, wrapping the inlined code in a try and unlocking
// in finally. But it's probably not worth the effort, scala never emits synchronized methods.
- Some(SynchronizedMethod(calleeDeclarationClass.internalName, callee.name, callee.desc))
+ Some(SynchronizedMethod(calleeDeclarationClass.internalName, callee.name, callee.desc, callsite.isInlineAnnotated))
} else if (isStrictfpMethod(callsiteMethod) != isStrictfpMethod(callee)) {
Some(StrictfpMismatch(
- calleeDeclarationClass.internalName, callee.name, callee.desc,
+ calleeDeclarationClass.internalName, callee.name, callee.desc, callsite.isInlineAnnotated,
callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc))
} else
None
@@ -486,9 +612,14 @@ class Inliner[BT <: BTypes](val btypes: BT) {
* we don't query it while traversing the call graph and selecting callsites to inline - it might
* rule out callsites that can be inlined just fine.
*
- * @return `Some(message)` if inlining cannot be performed, `None` otherwise
+ * Returns
+ * - `None` if the callsite can be inlined
+ * - `Some((message, Nil))` if there was an issue performing the access checks, for example
+ * because of a missing classfile
+ * - `Some((message, instructions))` if inlining `instructions` into the callsite method would
+ * cause an IllegalAccessError
*/
- def canInlineBody(callsite: Callsite): Option[CannotInlineWarning] = {
+ def canInlineCallsite(callsite: Callsite): Option[(CannotInlineWarning, List[AbstractInsnNode])] = {
import callsite.{callsiteInstruction, callsiteMethod, callsiteClass, callsiteStackHeight}
val Right(callsiteCallee) = callsite.callee
import callsiteCallee.{callee, calleeDeclarationClass}
@@ -519,23 +650,30 @@ class Inliner[BT <: BTypes](val btypes: BT) {
}
if (codeSizeOKForInlining(callsiteMethod, callee)) {
- Some(ResultingMethodTooLarge(
- calleeDeclarationClass.internalName, callee.name, callee.desc,
- callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc))
+ val warning = ResultingMethodTooLarge(
+ calleeDeclarationClass.internalName, callee.name, callee.desc, callsite.isInlineAnnotated,
+ callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc)
+ Some((warning, Nil))
} else if (!callee.tryCatchBlocks.isEmpty && stackHasNonParameters) {
- Some(MethodWithHandlerCalledOnNonEmptyStack(
- calleeDeclarationClass.internalName, callee.name, callee.desc,
- callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc))
- } else findIllegalAccess(callee.instructions, calleeDeclarationClass, callsiteClass) map {
- case (illegalAccessIns, None) =>
- IllegalAccessInstruction(
- calleeDeclarationClass.internalName, callee.name, callee.desc,
- callsiteClass.internalName, illegalAccessIns)
-
- case (illegalAccessIns, Some(warning)) =>
- IllegalAccessCheckFailed(
- calleeDeclarationClass.internalName, callee.name, callee.desc,
- callsiteClass.internalName, illegalAccessIns, warning)
+ val warning = MethodWithHandlerCalledOnNonEmptyStack(
+ calleeDeclarationClass.internalName, callee.name, callee.desc, callsite.isInlineAnnotated,
+ callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc)
+ Some((warning, Nil))
+ } else findIllegalAccess(callee.instructions, calleeDeclarationClass, callsiteClass) match {
+ case Right(Nil) =>
+ None
+
+ case Right(illegalAccessInsns) =>
+ val warning = IllegalAccessInstruction(
+ calleeDeclarationClass.internalName, callee.name, callee.desc, callsite.isInlineAnnotated,
+ callsiteClass.internalName, illegalAccessInsns.head)
+ Some((warning, illegalAccessInsns))
+
+ case Left((illegalAccessIns, cause)) =>
+ val warning = IllegalAccessCheckFailed(
+ calleeDeclarationClass.internalName, callee.name, callee.desc, callsite.isInlineAnnotated,
+ callsiteClass.internalName, illegalAccessIns, cause)
+ Some((warning, Nil))
}
}
@@ -624,13 +762,14 @@ class Inliner[BT <: BTypes](val btypes: BT) {
}
/**
- * Returns the first instruction in the `instructions` list that would cause a
- * [[java.lang.IllegalAccessError]] when inlined into the `destinationClass`.
- *
- * If validity of some instruction could not be checked because an error occurred, the instruction
- * is returned together with a warning message that describes the problem.
+ * Returns
+ * - `Right(Nil)` if all instructions can be safely inlined
+ * - `Right(insns)` if inlining any of `insns` would cause a [[java.lang.IllegalAccessError]]
+ * when inlined into the `destinationClass`
+ * - `Left((insn, warning))` if validity of some instruction could not be checked because an
+ * error occurred
*/
- def findIllegalAccess(instructions: InsnList, calleeDeclarationClass: ClassBType, destinationClass: ClassBType): Option[(AbstractInsnNode, Option[OptimizerWarning])] = {
+ def findIllegalAccess(instructions: InsnList, calleeDeclarationClass: ClassBType, destinationClass: ClassBType): Either[(AbstractInsnNode, OptimizerWarning), List[AbstractInsnNode]] = {
/**
* Check if `instruction` can be transplanted to `destinationClass`.
*
@@ -759,17 +898,15 @@ class Inliner[BT <: BTypes](val btypes: BT) {
}
val it = instructions.iterator.asScala
- @tailrec def find: Option[(AbstractInsnNode, Option[OptimizerWarning])] = {
- if (!it.hasNext) None // all instructions are legal
- else {
- val i = it.next()
- isLegal(i) match {
- case Left(warning) => Some((i, Some(warning))) // checking isLegal for i failed
- case Right(false) => Some((i, None)) // an illegal instruction was found
- case _ => find
- }
+ val illegalAccess = mutable.ListBuffer.empty[AbstractInsnNode]
+ while (it.hasNext) {
+ val i = it.next()
+ isLegal(i) match {
+ case Left(warning) => return Left((i, warning)) // checking isLegal for i failed
+ case Right(false) => illegalAccess += i // an illegal instruction was found
+ case _ =>
}
}
- find
+ Right(illegalAccess.toList)
}
}
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 79e74f3eb7..929e8b5ca4 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
@@ -7,17 +7,18 @@ package scala.tools.nsc
package backend.jvm
package opt
+import scala.annotation.tailrec
import scala.collection.JavaConverters._
import scala.tools.asm.Opcodes
-import scala.tools.asm.tree.{MethodInsnNode, MethodNode}
+import scala.tools.asm.tree.{AbstractInsnNode, MethodInsnNode, MethodNode}
import scala.tools.nsc.backend.jvm.BTypes.InternalName
-import scala.tools.nsc.backend.jvm.BackendReporting.OptimizerWarning
+import scala.tools.nsc.backend.jvm.BackendReporting.{CalleeNotFinal, OptimizerWarning}
class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
import bTypes._
import callGraph._
- case class InlineRequest(callsite: Callsite, post: List[InlineRequest], reason: String) {
+ final 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}")
}
@@ -41,30 +42,18 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
compilingMethods.map(methodNode => {
var requests = Set.empty[InlineRequest]
callGraph.callsites(methodNode).valuesIterator foreach {
- case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, sourceFilePath, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
+ case callsite @ Callsite(_, _, _, Right(Callee(callee, _, _, _, _, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
inlineRequest(callsite, requests) match {
case Some(Right(req)) => requests += req
- case Some(Left(w)) =>
- 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 Some(Left(w)) =>
+ if (w.emitWarning(compilerSettings)) {
+ backendReporting.inlinerWarning(callsite.callsitePosition, w.toString)
}
case None =>
- 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 -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)
- backendReporting.inlinerWarning(pos, s"$initMsg: the method is not final and may be overridden." + warnMsg)
- else
- backendReporting.inlinerWarning(pos, s"$initMsg." + warnMsg)
- } else if (callsiteWarning.isDefined && callsiteWarning.get.emitWarning(compilerSettings)) {
- // when annotatedInline is false, and there is some warning, the callsite metadata is possibly incomplete.
+ if (callsiteWarning.isDefined && callsiteWarning.get.emitWarning(compilerSettings))
backendReporting.inlinerWarning(pos, s"there was a problem determining if method ${callee.name} can be inlined: \n"+ callsiteWarning.get)
- }
}
case Callsite(ins, _, _, Left(warning), _, _, _, pos, _, _) =>
@@ -75,6 +64,42 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
}).filterNot(_._2.isEmpty).toMap
}
+ private def isTraitStaticSuperAccessorName(s: String) = s.endsWith("$")
+
+ private def isTraitSuperAccessor(method: MethodNode, owner: ClassBType): Boolean = {
+ owner.isInterface == Right(true) && BytecodeUtils.isStaticMethod(method) && isTraitStaticSuperAccessorName(method.name)
+ }
+
+ private def findCall(method: MethodNode, such: MethodInsnNode => Boolean): Option[MethodInsnNode] = {
+ @tailrec def noMoreInvoke(insn: AbstractInsnNode): Boolean = {
+ insn == null || (!insn.isInstanceOf[MethodInsnNode] && noMoreInvoke(insn.getNext))
+ }
+ @tailrec def find(insn: AbstractInsnNode): Option[MethodInsnNode] = {
+ if (insn == null) None
+ else insn match {
+ case mi: MethodInsnNode =>
+ if (such(mi) && noMoreInvoke(insn.getNext)) Some(mi)
+ else None
+ case _ =>
+ find(insn.getNext)
+ }
+ }
+ find(method.instructions.getFirst)
+ }
+ private def superAccessorInvocation(method: MethodNode): Option[MethodInsnNode] =
+ findCall(method, mi => mi.itf && mi.getOpcode == Opcodes.INVOKESTATIC && isTraitStaticSuperAccessorName(mi.name))
+
+ private def isMixinForwarder(method: MethodNode, owner: ClassBType): Boolean = {
+ owner.isInterface == Right(false) &&
+ !BytecodeUtils.isStaticMethod(method) &&
+ superAccessorInvocation(method).nonEmpty
+ }
+
+ private def isTraitSuperAccessorOrMixinForwarder(method: MethodNode, owner: ClassBType): Boolean = {
+ isTraitSuperAccessor(method, owner) || isMixinForwarder(method, owner)
+ }
+
+
/**
* Returns the inline request for a callsite if the callsite should be inlined according to the
* current heuristics (`-Yopt-inline-heuristics`).
@@ -90,81 +115,89 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
* `Some(Right)` if the callsite should be and can be inlined
*/
def inlineRequest(callsite: Callsite, selectedRequestsForCallee: Set[InlineRequest]): Option[Either[OptimizerWarning, InlineRequest]] = {
- val callee = callsite.callee.get
- def requestIfCanInline(callsite: Callsite, reason: String): Either[OptimizerWarning, InlineRequest] = inliner.earlyCanInlineCheck(callsite) match {
- case Some(w) => Left(w)
- 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))
-
+ def requestIfCanInline(callsite: Callsite, reason: String): Option[Either[OptimizerWarning, InlineRequest]] = {
+ val callee = callsite.callee.get
+ if (!callee.safeToInline) {
+ if (callsite.isInlineAnnotated && callee.canInlineFromSource) {
+ // By default, we only emit inliner warnings for methods annotated @inline. However, we don't
+ // want to be unnecessarily noisy with `-opt-warnings:_`: for example, the inliner heuristic
+ // would attempty to inline `Function1.apply$sp$II`, as it's higher-order (the receiver is
+ // a function), and it's concrete (forwards to `apply`). But because it's non-final, it cannot
+ // be inlined. So we only create warnings here for methods annotated @inline.
+ Some(Left(CalleeNotFinal(
+ callee.calleeDeclarationClass.internalName,
+ callee.callee.name,
+ callee.callee.desc,
+ callsite.isInlineAnnotated)))
+ } else None
+ } else inliner.earlyCanInlineCheck(callsite) match {
+ case Some(w) => Some(Left(w))
+ case None =>
+ val postInlineRequest: List[InlineRequest] = {
+ val postCall =
+ if (isTraitSuperAccessor(callee.callee, callee.calleeDeclarationClass)) {
+ // scala-dev#259: when inlining a trait super accessor, also inline the callsite to the default method
+ val implName = callee.callee.name.dropRight(1)
+ findCall(callee.callee, mi => mi.itf && mi.getOpcode == Opcodes.INVOKESPECIAL && mi.name == implName)
+ } else {
+ // scala-dev#259: when inlining a mixin forwarder, also inline the callsite to the static super accessor
+ superAccessorInvocation(callee.callee)
+ }
+ postCall.flatMap(call => {
+ callGraph.addIfMissing(callee.callee, callee.calleeDeclarationClass)
+ val maybeCallsite = callGraph.findCallSite(callee.callee, call)
+ maybeCallsite.flatMap(requestIfCanInline(_, reason).flatMap(_.right.toOption))
+ }).toList
+ }
+ Some(Right(InlineRequest(callsite, postInlineRequest, reason)))
+ }
}
- compilerSettings.YoptInlineHeuristics.value match {
- case "everything" =>
- if (callee.safeToInline) {
+ // scala-dev#259: don't inline into static accessors and mixin forwarders
+ if (isTraitSuperAccessorOrMixinForwarder(callsite.callsiteMethod, callsite.callsiteClass)) None
+ else {
+ val callee = callsite.callee.get
+ compilerSettings.YoptInlineHeuristics.value match {
+ case "everything" =>
val reason = if (compilerSettings.YoptLogInline.isSetByUser) "the inline strategy is \"everything\"" else null
- Some(requestIfCanInline(callsite, reason))
- }
- else None
+ requestIfCanInline(callsite, reason)
- case "at-inline-annotated" =>
- if (callee.safeToInline && callee.annotatedInline) {
- val reason = if (compilerSettings.YoptLogInline.isSetByUser) {
- val what = if (callee.safeToInline) "callee" else "callsite"
+ case "at-inline-annotated" =>
+ def reason = if (!compilerSettings.YoptLogInline.isSetByUser) null else {
+ val what = if (callee.annotatedInline) "callee" else "callsite"
s"the $what is annotated `@inline`"
- } else null
- Some(requestIfCanInline(callsite, reason))
- }
- else None
+ }
+ if (callsite.isInlineAnnotated && !callsite.isNoInlineAnnotated) requestIfCanInline(callsite, reason)
+ else None
- case "default" =>
- if (callee.safeToInline && !callee.annotatedNoInline && !callsite.annotatedNoInline) {
- def shouldInlineHO = callee.samParamTypes.nonEmpty && (callee.samParamTypes exists {
- case (index, _) => callsite.argInfos.contains(index)
- })
- 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)
+ case "default" =>
+ def reason = if (!compilerSettings.YoptLogInline.isSetByUser) null else {
+ if (callsite.isInlineAnnotated) {
+ val what = if (callee.annotatedInline) "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"
}
- s"the callee is a higher-order method, ${argInfos.mkString(", ")}"
+ samInfo(i, sam.internalName.split('/').last, argKind)
}
- } else null
- Some(requestIfCanInline(callsite, reason))
+ s"the callee is a higher-order method, ${argInfos.mkString(", ")}"
+ }
}
+ def shouldInlineHO = callee.samParamTypes.nonEmpty && (callee.samParamTypes exists {
+ case (index, _) => callsite.argInfos.contains(index)
+ })
+ if (!callsite.isNoInlineAnnotated && (callsite.isInlineAnnotated || shouldInlineHO)) requestIfCanInline(callsite, reason)
else None
- } else None
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala
index 8df0c3743d..6fefaf0da0 100644
--- a/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala
+++ b/src/compiler/scala/tools/nsc/classpath/VirtualDirectoryClassPath.scala
@@ -1,9 +1,11 @@
package scala.tools.nsc.classpath
import scala.tools.nsc.util.ClassRepresentation
-import scala.reflect.io.{Path, PlainFile, VirtualDirectory, AbstractFile}
+import scala.reflect.io.{AbstractFile, Path, PlainFile, VirtualDirectory}
import FileUtils._
import java.net.URL
+
+import scala.reflect.internal.util.AbstractFileClassLoader
import scala.tools.nsc.util.ClassPath
case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths {
@@ -11,7 +13,7 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi
protected def emptyFiles: Array[AbstractFile] = Array.empty
protected def getSubDir(packageDirName: String): Option[AbstractFile] =
- Option(dir.lookupName(packageDirName, directory = true))
+ Option(AbstractFileClassLoader.lookupPath(dir)(packageDirName.split('/'), directory = true))
protected def listChildren(dir: AbstractFile, filter: Option[AbstractFile => Boolean] = None): Array[F] = filter match {
case Some(f) => dir.iterator.filter(f).toArray
case _ => dir.toArray
@@ -27,10 +29,8 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi
override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl
def findClassFile(className: String): Option[AbstractFile] = {
- val relativePath = FileUtils.dirPath(className)
- val classFile = new PlainFile(Path(s"$dir/$relativePath.class"))
- if (classFile.exists) Some(classFile)
- else None
+ val relativePath = FileUtils.dirPath(className) + ".class"
+ Option(AbstractFileClassLoader.lookupPath(dir)(relativePath split '/', directory = false))
}
private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage)
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 25475515aa..92accaf9dd 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -1278,5 +1278,4 @@ abstract class Erasure extends InfoTransform
}
private class TypeRefAttachment(val tpe: TypeRef)
- class TypeParamVarargsAttachment(val typeParamRef: Type)
}
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index c171050bbd..84f47c1caa 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -1049,7 +1049,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
debuglog(s"specialized overload $om for ${overriding.name.decode} in ${pp(env)}: ${om.info}")
- if (overriding.isAbstractOverride) om.setFlag(ABSOVERRIDE)
+ om.setFlag(overriding.flags & (ABSOVERRIDE | SYNCHRONIZED))
+ om.withAnnotations(overriding.annotations.filter(_.symbol == ScalaStrictFPAttr))
typeEnv(om) = env
addConcreteSpecMethod(overriding)
if (overriding.isDeferred) { // abstract override
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index d8fa7b58e8..ea3c7da014 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -27,6 +27,8 @@ import scala.reflect.internal.util.ListOfNil
* - for every repeated Scala parameter `x: T*' --> x: Seq[T].
* - for every repeated Java parameter `x: T...' --> x: Array[T], except:
* if T is an unbounded abstract type, replace --> x: Array[Object]
+ * - for every method defining repeated parameters annotated with @varargs, generate
+ * a synthetic Java-style vararg method
* - for every argument list that corresponds to a repeated Scala parameter
* (a_1, ..., a_n) => (Seq(a_1, ..., a_n))
* - for every argument list that corresponds to a repeated Java parameter
@@ -44,6 +46,8 @@ import scala.reflect.internal.util.ListOfNil
* def liftedTry$1 = try { x_i } catch { .. }
* meth(x_1, .., liftedTry$1(), .. )
* }
+ * - remove calls to elidable methods and replace their bodies with NOPs when elide-below
+ * requires it
*/
/*</export> */
abstract class UnCurry extends InfoTransform
@@ -577,7 +581,13 @@ abstract class UnCurry extends InfoTransform
case None => literalRhsIfConst
}
)
- addJavaVarargsForwarders(dd, flatdd)
+ // Only class members can reasonably be called from Java due to name mangling.
+ // Additionally, the Uncurry info transformer only adds a forwarder symbol to class members,
+ // since the other symbols are not part of the ClassInfoType (see reflect.internal.transform.UnCurry)
+ if (dd.symbol.owner.isClass)
+ addJavaVarargsForwarders(dd, flatdd)
+ else
+ flatdd
case tree: Try =>
if (tree.catches exists (cd => !treeInfo.isCatchCase(cd)))
@@ -739,68 +749,32 @@ abstract class UnCurry extends InfoTransform
if (!hasRepeated) reporter.error(dd.symbol.pos, "A method without repeated parameters cannot be annotated with the `varargs` annotation.")
}
- /* Called during post transform, after the method argument lists have been flattened.
- * It looks for the method in the `repeatedParams` map, and generates a Java-style
+ /**
+ * Called during post transform, after the method argument lists have been flattened.
+ * It looks for the forwarder symbol in the symbol attachments and generates a Java-style
* varargs forwarder.
+ *
+ * @note The Java-style varargs method symbol is generated in the Uncurry info transformer. If the
+ * symbol can't be found this method reports a warning and carries on.
+ * @see [[scala.reflect.internal.transform.UnCurry]]
*/
private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef): DefDef = {
if (!dd.symbol.hasAnnotation(VarargsClass) || !enteringUncurry(mexists(dd.symbol.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))))
return flatdd
- val forwSym = currentClass.newMethod(dd.name.toTermName, dd.pos, VARARGS | SYNTHETIC | flatdd.symbol.flags & ~DEFERRED)
-
- val isRepeated = enteringUncurry(dd.symbol.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe)))
-
- val oldPs = flatdd.symbol.paramss.head
-
- // see comment in method toArrayType below
- val arrayTypesMappedToObject = mutable.Map.empty[Symbol, Type]
-
- val forwTpe = {
- val (oldTps, tps) = dd.symbol.tpe match {
- case PolyType(oldTps, _) =>
- val newTps = oldTps.map(_.cloneSymbol(forwSym))
- (oldTps, newTps)
-
- case _ => (Nil, Nil)
- }
-
- def toArrayType(tp: Type, newParam: Symbol): Type = {
- val arg = elementType(SeqClass, tp)
- val elem = if (arg.typeSymbol.isTypeParameterOrSkolem && !(arg <:< AnyRefTpe)) {
- // To prevent generation of an `Object` parameter from `Array[T]` parameter later
- // as this would crash the Java compiler which expects an `Object[]` array for varargs
- // e.g. def foo[T](a: Int, b: T*)
- // becomes def foo[T](a: Int, b: Array[Object])
- // instead of def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object)
- //
- // In order for the forwarder method to type check we need to insert a cast:
- // def foo'[T'](a: Int, b: Array[Object]) = foo[T'](a, wrapRefArray(b).asInstanceOf[Seq[T']])
- // The target element type for that cast (T') is stored in the `arrayTypesMappedToObject` map.
- val originalArg = arg.substSym(oldTps, tps)
- arrayTypesMappedToObject(newParam) = originalArg
- // Store the type parameter that was replaced by Object to emit the correct generic signature
- newParam.updateAttachment(new erasure.TypeParamVarargsAttachment(originalArg))
- ObjectTpe
- } else
- arg
- arrayType(elem)
+ val forwSym: Symbol = {
+ currentClass.info // make sure the info is up to date, so the varargs forwarder symbol has been generated
+ flatdd.symbol.attachments.get[VarargsSymbolAttachment] match {
+ case Some(VarargsSymbolAttachment(sym)) => sym
+ case None =>
+ reporter.warning(dd.pos, s"Could not generate Java varargs forwarder for ${flatdd.symbol}. Please file a bug.")
+ return flatdd
}
-
- val ps = map2(oldPs, isRepeated)((oldParam, isRep) => {
- val newParam = oldParam.cloneSymbol(forwSym)
- val tp = if (isRep) toArrayType(oldParam.tpe, newParam) else oldParam.tpe
- newParam.setInfo(tp)
- })
-
- val resTp = dd.symbol.tpe_*.finalResultType.substSym(oldPs, ps)
- val mt = MethodType(ps, resTp)
- val r = if (tps.isEmpty) mt else PolyType(tps, mt)
- r.substSym(oldTps, tps)
}
- forwSym.setInfo(forwTpe)
- val newPs = forwTpe.params
+ val newPs = forwSym.tpe.params
+ val isRepeated = enteringUncurry(dd.symbol.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe)))
+ val oldPs = flatdd.symbol.paramss.head
val theTyper = typer.atOwner(dd, currentClass)
val forwTree = theTyper.typedPos(dd.pos) {
@@ -809,8 +783,8 @@ abstract class UnCurry extends InfoTransform
else {
val parTp = elementType(ArrayClass, param.tpe)
val wrap = gen.mkWrapArray(Ident(param), parTp)
- arrayTypesMappedToObject.get(param) match {
- case Some(tp) => gen.mkCast(wrap, seqType(tp))
+ param.attachments.get[TypeParamVarargsAttachment] match {
+ case Some(TypeParamVarargsAttachment(tp)) => gen.mkCast(wrap, seqType(tp))
case _ => wrap
}
}
@@ -821,13 +795,12 @@ abstract class UnCurry extends InfoTransform
}
// check if the method with that name and those arguments already exists in the template
- currentClass.info.member(forwSym.name).alternatives.find(s => s != forwSym && s.tpe.matches(forwSym.tpe)) match {
- case Some(s) => reporter.error(dd.symbol.pos,
- "A method with a varargs annotation produces a forwarder method with the same signature "
- + s.tpe + " as an existing method.")
+ enteringUncurry(currentClass.info.member(forwSym.name).alternatives.find(s => s != forwSym && s.tpe.matches(forwSym.tpe))) match {
+ case Some(s) =>
+ reporter.error(dd.symbol.pos,
+ s"A method with a varargs annotation produces a forwarder method with the same signature ${s.tpe} as an existing method.")
case None =>
// enter symbol into scope
- currentClass.info.decls enter forwSym
addNewMember(forwTree)
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
index 4ae97ce281..cb3759e5fa 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
@@ -682,7 +682,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type = {
def freshExistentialSubtype(tp: Type): Type = {
// SI-8611 tp.narrow is tempting, but unsuitable. See `testRefinedTypeSI8611` for an explanation.
- NoSymbol.freshExistential("").setInfo(TypeBounds.upper(tp)).tpe
+ NoSymbol.freshExistential("", 0).setInfo(TypeBounds.upper(tp)).tpe
}
if (!t.symbol.isStable) {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index c73ea54c3d..8f0625e58c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -60,7 +60,7 @@ trait Contexts { self: Analyzer =>
private lazy val allImportInfos =
mutable.Map[CompilationUnit, List[ImportInfo]]() withDefaultValue Nil
- def warnUnusedImports(unit: CompilationUnit) = {
+ def warnUnusedImports(unit: CompilationUnit) = if (!unit.isJava) {
for (imps <- allImportInfos.remove(unit)) {
for (imp <- imps.reverse.distinct) {
val used = allUsedSelectors(imp)
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
index 1df3449ce6..cd0c292d90 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
@@ -123,7 +123,7 @@ trait PatternTypers {
}
private def boundedArrayType(bound: Type): Type = {
- val tparam = context.owner freshExistential "" setInfo (TypeBounds upper bound)
+ val tparam = context.owner.freshExistential("", 0) setInfo (TypeBounds upper bound)
newExistentialType(tparam :: Nil, arrayType(tparam.tpe_*))
}
diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala
index 715ba0d4f3..669a018f10 100644
--- a/src/interactive/scala/tools/nsc/interactive/Global.scala
+++ b/src/interactive/scala/tools/nsc/interactive/Global.scala
@@ -1189,7 +1189,8 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
case Nil => entered.isEmpty && matchCount > 0
case head :: tail =>
val enteredAlternatives = Set(entered, entered.capitalize)
- head.inits.filter(_.length <= entered.length).exists(init =>
+ val n = (head, entered).zipped.count {case (c, e) => c == e || (c.isUpper && c == e.toUpper)}
+ head.take(n).inits.exists(init =>
enteredAlternatives.exists(entered =>
lenientMatch(entered.stripPrefix(init), tail, matchCount + (if (init.isEmpty) 0 else 1))
)
diff --git a/src/library/scala/collection/mutable/HashMap.scala b/src/library/scala/collection/mutable/HashMap.scala
index eab4202353..11ff1f0893 100644
--- a/src/library/scala/collection/mutable/HashMap.scala
+++ b/src/library/scala/collection/mutable/HashMap.scala
@@ -72,6 +72,37 @@ extends AbstractMap[A, B]
else Some(e.value)
}
+ override def getOrElseUpdate(key: A, defaultValue: => B): B = {
+ val i = index(elemHashCode(key))
+ val entry = findEntry(key, i)
+ if (entry != null) entry.value
+ else addEntry(createNewEntry(key, defaultValue), i)
+ }
+
+ /* inlined HashTable.findEntry0 to preserve its visibility */
+ private[this] def findEntry(key: A, h: Int): Entry = {
+ var e = table(h).asInstanceOf[Entry]
+ while (notFound(key, e))
+ e = e.next
+ e
+ }
+ private[this] def notFound(key: A, e: Entry): Boolean = (e != null) && !elemEquals(e.key, key)
+
+ /* inlined HashTable.addEntry0 to preserve its visibility */
+ private[this] def addEntry(e: Entry, h: Int): B = {
+ if (tableSize >= threshold) addEntry(e)
+ else addEntry0(e, h)
+ e.value
+ }
+
+ /* extracted to make addEntry inlinable */
+ private[this] def addEntry0(e: Entry, h: Int) {
+ e.next = table(h).asInstanceOf[Entry]
+ table(h) = e
+ tableSize += 1
+ nnSizeMapAdd(h)
+ }
+
override def put(key: A, value: B): Option[B] = {
val e = findOrAddEntry(key, value)
if (e eq null) None
diff --git a/src/library/scala/concurrent/SyncVar.scala b/src/library/scala/concurrent/SyncVar.scala
index 5fabf553bd..0e534a9b22 100644
--- a/src/library/scala/concurrent/SyncVar.scala
+++ b/src/library/scala/concurrent/SyncVar.scala
@@ -91,7 +91,7 @@ class SyncVar[A] {
// [Heather] the reason why: it doesn't take into consideration
// whether or not the SyncVar is already defined. So, set has been
// deprecated in order to eventually be able to make "setting" private
- @deprecated("use `put` instead, as `set` is potentially error-prone", "2.10.0")
+ @deprecated("use `put` to ensure a value cannot be overwritten without a corresponding `take`", "2.10.0")
// NOTE: Used by SBT 0.13.0-M2 and below
def set(x: A): Unit = setVal(x)
@@ -111,7 +111,7 @@ class SyncVar[A] {
// [Heather] the reason why: it doesn't take into consideration
// whether or not the SyncVar is already defined. So, unset has been
// deprecated in order to eventually be able to make "unsetting" private
- @deprecated("use `take` instead, as `unset` is potentially error-prone", "2.10.0")
+ @deprecated("use `take` to ensure a value is never discarded", "2.10.0")
// NOTE: Used by SBT 0.13.0-M2 and below
def unset(): Unit = synchronized {
isDefined = false
diff --git a/src/library/scala/inline.scala b/src/library/scala/inline.scala
index f6d7c7569e..f188ccab07 100644
--- a/src/library/scala/inline.scala
+++ b/src/library/scala/inline.scala
@@ -23,7 +23,7 @@ package scala
* def t2 = f2(1) // not inlined
* def t3 = f3(1) // may be inlined (heuristics)
* def t4 = f1(1): @noinline // not inlined (override at callsite)
- * def t5 = f2(1): @inline // not inlined (cannot override the @noinline at f2's definition)
+ * def t5 = f2(1): @inline // inlined if possible (override at callsite)
* def t6 = f3(1): @inline // inlined if possible
* def t7 = f3(1): @noinline // not inlined
* }
diff --git a/src/library/scala/noinline.scala b/src/library/scala/noinline.scala
index 0cd5ef9f64..6c21ed667d 100644
--- a/src/library/scala/noinline.scala
+++ b/src/library/scala/noinline.scala
@@ -23,7 +23,7 @@ package scala
* def t2 = f2(1) // not inlined
* def t3 = f3(1) // may be inlined (heuristics)
* def t4 = f1(1): @noinline // not inlined (override at callsite)
- * def t5 = f2(1): @inline // not inlined (cannot override the @noinline at f2's definition)
+ * def t5 = f2(1): @inline // inlined if possible (override at callsite)
* def t6 = f3(1): @inline // inlined if possible
* def t7 = f3(1): @noinline // not inlined
* }
diff --git a/src/library/scala/sys/process/ProcessBuilderImpl.scala b/src/library/scala/sys/process/ProcessBuilderImpl.scala
index eef140c16a..0df2e648e0 100644
--- a/src/library/scala/sys/process/ProcessBuilderImpl.scala
+++ b/src/library/scala/sys/process/ProcessBuilderImpl.scala
@@ -53,12 +53,14 @@ private[process] trait ProcessBuilderImpl {
override def run(io: ProcessIO): Process = {
val success = new SyncVar[Boolean]
- success put false
- val t = Spawn({
- runImpl(io)
- success.put(true)
- }, io.daemonizeThreads)
-
+ def go(): Unit = {
+ var ok = false
+ try {
+ runImpl(io)
+ ok = true
+ } finally success.put(ok)
+ }
+ val t = Spawn(go(), io.daemonizeThreads)
new ThreadProcess(t, success)
}
}
diff --git a/src/library/scala/sys/process/ProcessImpl.scala b/src/library/scala/sys/process/ProcessImpl.scala
index 6da0dee056..8a0002b316 100644
--- a/src/library/scala/sys/process/ProcessImpl.scala
+++ b/src/library/scala/sys/process/ProcessImpl.scala
@@ -86,17 +86,20 @@ private[process] trait ProcessImpl {
private[process] abstract class CompoundProcess extends BasicProcess {
def isAlive() = processThread.isAlive()
def destroy() = destroyer()
- def exitValue() = getExitValue._2() getOrElse scala.sys.error("No exit code: process destroyed.")
- def start() = getExitValue
+ def exitValue() = futureValue() getOrElse scala.sys.error("No exit code: process destroyed.")
+ def start() = { futureThread ;() }
- protected lazy val (processThread, getExitValue, destroyer) = {
+ protected lazy val (processThread, (futureThread, futureValue), destroyer) = {
val code = new SyncVar[Option[Int]]()
- code.put(None)
- val thread = Spawn(code.put(runAndExitValue()))
+ val thread = Spawn {
+ var value: Option[Int] = None
+ try value = runAndExitValue()
+ finally code.put(value)
+ }
(
thread,
- Future { thread.join(); code.get },
+ Future(code.get), // thread.join()
() => thread.interrupt()
)
}
@@ -215,13 +218,15 @@ private[process] trait ProcessImpl {
}
/** A thin wrapper around a java.lang.Process. `ioThreads` are the Threads created to do I/O.
- * The implementation of `exitValue` waits until these threads die before returning. */
+ * The implementation of `exitValue` waits until these threads die before returning.
+ */
private[process] class DummyProcess(action: => Int) extends Process {
- private[this] val exitCode = Future(action)
- override def isAlive() = exitCode._1.isAlive()
- override def exitValue() = exitCode._2()
+ private[this] val (thread, value) = Future(action)
+ override def isAlive() = thread.isAlive()
+ override def exitValue() = value()
override def destroy() { }
}
+
/** A thin wrapper around a java.lang.Process. `outputThreads` are the Threads created to read from the
* output and error streams of the process. `inputThread` is the Thread created to write to the input stream of
* the process.
@@ -245,11 +250,8 @@ private[process] trait ProcessImpl {
}
}
private[process] final class ThreadProcess(thread: Thread, success: SyncVar[Boolean]) extends Process {
- override def isAlive() = thread.isAlive()
- override def exitValue() = {
- thread.join()
- if (success.get) 0 else 1
- }
- override def destroy() { thread.interrupt() }
+ override def isAlive() = thread.isAlive()
+ override def exitValue() = if (success.get) 0 else 1 // thread.join()
+ override def destroy() = thread.interrupt()
}
}
diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala
index 78f360409d..fd8f51cfb1 100644
--- a/src/reflect/scala/reflect/internal/StdAttachments.scala
+++ b/src/reflect/scala/reflect/internal/StdAttachments.scala
@@ -78,4 +78,7 @@ trait StdAttachments {
case object OuterArgCanBeElided extends PlainAttachment
case object UseInvokeSpecial extends PlainAttachment
+
+ /** An attachment carrying information between uncurry and erasure */
+ case class TypeParamVarargsAttachment(val typeParamRef: Type)
}
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 56b6dc078d..5077169525 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -34,9 +34,13 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def recursionTable = _recursionTable
def recursionTable_=(value: immutable.Map[Symbol, Int]) = _recursionTable = value
+ @deprecated("Global existential IDs no longer used", "2.12.1")
private var existentialIds = 0
+ @deprecated("Global existential IDs no longer used", "2.12.1")
protected def nextExistentialId() = { existentialIds += 1; existentialIds }
- protected def freshExistentialName(suffix: String) = newTypeName("_" + nextExistentialId() + suffix)
+ @deprecated("Use overload that accepts an id", "2.12.1")
+ protected def freshExistentialName(suffix: String): TypeName = freshExistentialName(suffix, nextExistentialId())
+ protected def freshExistentialName(suffix: String, id: Int): TypeName = newTypeName("_" + id + suffix)
// Set the fields which point companions at one another. Returns the module.
def connectModuleToClass(m: ModuleSymbol, moduleClass: ClassSymbol): ModuleSymbol = {
@@ -440,8 +444,11 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def newGADTSkolem(name: TypeName, origin: Symbol, info: Type): TypeSkolem =
newTypeSkolemSymbol(name, origin, origin.pos, origin.flags & ~(EXISTENTIAL | PARAM) | GADT_SKOLEM_FLAGS) setInfo info
+ @deprecated("Use overload that accepts an id", "2.12.1")
final def freshExistential(suffix: String): TypeSymbol =
newExistential(freshExistentialName(suffix), pos)
+ final def freshExistential(suffix: String, id: Int): TypeSymbol =
+ newExistential(freshExistentialName(suffix, id), pos)
/** Type skolems are type parameters ''seen from the inside''
* Assuming a polymorphic method m[T], its type is a PolyType which has a TypeParameter
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index f8679616d1..5e1bf37b80 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -4487,6 +4487,7 @@ trait Types
debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss")
NoType
case Some(argsst) =>
+ var capturedParamIds = 0
val args = map2(sym.typeParams, argsst) { (tparam, as0) =>
val as = as0.distinct
if (as.size == 1) as.head
@@ -4508,8 +4509,10 @@ trait Types
else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we
// just err on the conservative side, i.e. with a bound that is too high.
// if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251
+ capturedParamIds += 1
+ val capturedParamId = capturedParamIds
- val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l)
+ val qvar = commonOwner(as).freshExistential("", capturedParamId) setInfo TypeBounds(g, l)
capturedParams += qvar
qvar.tpe
}
diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
index ba4f2bec4b..08219c0634 100644
--- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
+++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
@@ -512,6 +512,8 @@ private[internal] trait TypeMaps {
&& isBaseClassOfEnclosingClass(sym.owner)
)
+ private var capturedThisIds= 0
+ private def nextCapturedThisId() = { capturedThisIds += 1; capturedThisIds }
/** Creates an existential representing a type parameter which appears
* in the prefix of a ThisType.
*/
@@ -519,7 +521,7 @@ private[internal] trait TypeMaps {
capturedParams find (_.owner == clazz) match {
case Some(p) => p.tpe
case _ =>
- val qvar = clazz freshExistential nme.SINGLETON_SUFFIX setInfo singletonBounds(pre)
+ val qvar = clazz.freshExistential(nme.SINGLETON_SUFFIX, nextCapturedThisId()) setInfo singletonBounds(pre)
_capturedParams ::= qvar
debuglog(s"Captured This(${clazz.fullNameString}) seen from $seenFromPrefix: ${qvar.defString}")
qvar.tpe
diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala
index a50084f40d..3918723b5c 100644
--- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala
+++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala
@@ -4,6 +4,7 @@ package internal
package transform
import Flags._
+import scala.collection.mutable
trait UnCurry {
@@ -11,6 +12,12 @@ trait UnCurry {
import global._
import definitions._
+ /**
+ * The synthetic Java vararg method symbol corresponding to a Scala vararg method
+ * annotated with @varargs.
+ */
+ case class VarargsSymbolAttachment(varargMethod: Symbol)
+
/** Note: changing tp.normalize to tp.dealias in this method leads to a single
* test failure: run/t5688.scala, where instead of the expected output
* Vector(ta, tb, tab)
@@ -65,18 +72,74 @@ trait UnCurry {
def apply(tp0: Type): Type = {
val tp = expandAlias(tp0)
tp match {
- case ClassInfoType(parents, decls, clazz) =>
+ case ClassInfoType(parents, decls, clazz) if !clazz.isJavaDefined =>
val parents1 = parents mapConserve uncurry
- if (parents1 eq parents) tp
- else ClassInfoType(parents1, decls, clazz) // @MAT normalize in decls??
+ val varargOverloads = mutable.ListBuffer.empty[Symbol]
+
+ // Not using `hasAnnotation` here because of dreaded cyclic reference errors:
+ // it may happen that VarargsClass has not been initialized yet and we get here
+ // while processing one of its superclasses (such as java.lang.Object). Since we
+ // don't need the more precise `matches` semantics, we only check the symbol, which
+ // is anyway faster and safer
+ for (decl <- decls if decl.annotations.exists(_.symbol == VarargsClass)) {
+ if (mexists(decl.paramss)(sym => definitions.isRepeatedParamType(sym.tpe))) {
+ varargOverloads += varargForwarderSym(clazz, decl, exitingPhase(phase)(decl.info))
+ }
+ }
+ if ((parents1 eq parents) && varargOverloads.isEmpty) tp
+ else {
+ val newDecls = decls.cloneScope
+ varargOverloads.foreach(newDecls.enter)
+ ClassInfoType(parents1, newDecls, clazz)
+ } // @MAT normalize in decls??
+
case PolyType(_, _) =>
mapOver(tp)
+
case _ =>
tp
}
}
}
+ private def varargForwarderSym(currentClass: Symbol, origSym: Symbol, newInfo: Type): Symbol = {
+ val forwSym = origSym.cloneSymbol(currentClass, VARARGS | SYNTHETIC | origSym.flags & ~DEFERRED, origSym.name.toTermName).withoutAnnotations
+
+ // we are using `origSym.info`, which contains the type *before* the transformation
+ // so we still see repeated parameter types (uncurry replaces them with Seq)
+ val isRepeated = origSym.info.paramss.flatten.map(sym => definitions.isRepeatedParamType(sym.tpe))
+ val oldPs = newInfo.paramss.head
+ def toArrayType(tp: Type, newParam: Symbol): Type = {
+ val arg = elementType(SeqClass, tp)
+ val elem = if (arg.typeSymbol.isTypeParameterOrSkolem && !(arg <:< AnyRefTpe)) {
+ // To prevent generation of an `Object` parameter from `Array[T]` parameter later
+ // as this would crash the Java compiler which expects an `Object[]` array for varargs
+ // e.g. def foo[T](a: Int, b: T*)
+ // becomes def foo[T](a: Int, b: Array[Object])
+ // instead of def foo[T](a: Int, b: Array[T]) ===> def foo[T](a: Int, b: Object)
+ //
+ // In order for the forwarder method to type check we need to insert a cast:
+ // def foo'[T'](a: Int, b: Array[Object]) = foo[T'](a, wrapRefArray(b).asInstanceOf[Seq[T']])
+ // The target element type for that cast (T') is stored in the TypeParamVarargsAttachment
+// val originalArg = arg.substSym(oldTps, tps)
+ // Store the type parameter that was replaced by Object to emit the correct generic signature
+ newParam.updateAttachment(new TypeParamVarargsAttachment(arg))
+ ObjectTpe
+ } else
+ arg
+ arrayType(elem)
+ }
+
+ foreach2(forwSym.paramss.flatten, isRepeated)((p, isRep) =>
+ if (isRep) {
+ p.setInfo(toArrayType(p.info, p))
+ }
+ )
+
+ origSym.updateAttachment(VarargsSymbolAttachment(forwSym))
+ forwSym
+ }
+
/** - return symbol's transformed type,
* - if symbol is a def parameter with transformed type T, return () => T
*
diff --git a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala
index b5030460b8..3cede1b3c5 100644
--- a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala
+++ b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala
@@ -12,6 +12,20 @@ import java.security.cert.Certificate
import java.security.{ ProtectionDomain, CodeSource }
import java.util.{ Collections => JCollections, Enumeration => JEnumeration }
+object AbstractFileClassLoader {
+ // should be a method on AbstractFile, but adding in `internal.util._` for now as we're in a minor release
+ private[scala] final def lookupPath(base: AbstractFile)(pathParts: Seq[String], directory: Boolean): AbstractFile = {
+ var file: AbstractFile = base
+ for (dirPart <- pathParts.init) {
+ file = file.lookupName(dirPart, directory = true)
+ if (file == null)
+ return null
+ }
+
+ file.lookupName(pathParts.last, directory = directory)
+ }
+}
+
/** A class loader that loads files from a [[scala.reflect.io.AbstractFile]].
*
* @author Lex Spoon
@@ -25,19 +39,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader)
else s"${name.replace('.', '/')}.class"
protected def findAbstractFile(name: String): AbstractFile = {
- var file: AbstractFile = root
- val pathParts = name split '/'
-
- for (dirPart <- pathParts.init) {
- file = file.lookupName(dirPart, directory = true)
- if (file == null)
- return null
- }
-
- file.lookupName(pathParts.last, directory = false) match {
- case null => null
- case file => file
- }
+ AbstractFileClassLoader.lookupPath(root)(name split '/', directory = false)
}
protected def dirNameToPath(name: String): String =
diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala
index a2642628a4..64b6972298 100644
--- a/src/reflect/scala/reflect/internal/util/SourceFile.scala
+++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala
@@ -154,18 +154,23 @@ class BatchSourceFile(val file : AbstractFile, content0: Array[Char]) extends So
case _ => false
}
- def calculateLineIndices(cs: Array[Char]) = {
- val buf = new ArrayBuffer[Int]
- buf += 0
- for (i <- 0 until cs.length) if (isAtEndOfLine(i)) buf += i + 1
- buf += cs.length // sentinel, so that findLine below works smoother
- buf.toArray
+ private lazy val lineIndices: Array[Int] = {
+ def calculateLineIndices(cs: Array[Char]) = {
+ val buf = new ArrayBuffer[Int]
+ buf += 0
+ for (i <- 0 until cs.length) if (isAtEndOfLine(i)) buf += i + 1
+ buf += cs.length // sentinel, so that findLine below works smoother
+ buf.toArray
+ }
+ calculateLineIndices(content)
}
- private lazy val lineIndices: Array[Int] = calculateLineIndices(content)
- def lineToOffset(index : Int): Int = lineIndices(index)
+ def lineToOffset(index: Int): Int = {
+ val offset = lineIndices(index)
+ if (offset < length) offset else throw new IndexOutOfBoundsException(index.toString)
+ }
- private var lastLine = 0
+ private[this] var lastLine = 0
/** Convert offset to line in this source file.
* Lines are numbered from 0.
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index b74ccb9177..dbafbfc6ba 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -47,6 +47,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.InlineCallsiteAttachment
this.OuterArgCanBeElided
this.UseInvokeSpecial
+ this.TypeParamVarargsAttachment
this.noPrint
this.typeDebug
this.Range
@@ -458,6 +459,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.ScalaValueClassesNoUnit
definitions.ScalaValueClasses
+ uncurry.VarargsSymbolAttachment
uncurry.DesugaredParameterType
erasure.GenericArray
erasure.scalaErasure
diff --git a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala
index 237afa082b..4e7ddda54e 100644
--- a/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala
+++ b/src/reflect/scala/reflect/runtime/SynchronizedSymbols.scala
@@ -10,7 +10,9 @@ private[reflect] trait SynchronizedSymbols extends internal.Symbols { self: Symb
private lazy val atomicIds = new java.util.concurrent.atomic.AtomicInteger(0)
override protected def nextId() = atomicIds.incrementAndGet()
+ @deprecated("Global existential IDs no longer used", "2.12.1")
private lazy val atomicExistentialIds = new java.util.concurrent.atomic.AtomicInteger(0)
+ @deprecated("Global existential IDs no longer used", "2.12.1")
override protected def nextExistentialId() = atomicExistentialIds.incrementAndGet()
private lazy val _recursionTable = mkThreadLocalStorage(immutable.Map.empty[Symbol, Int])
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala
index 65f2c95f73..99acc34811 100644
--- a/src/repl/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala
@@ -889,7 +889,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
}
class ClassBasedWrapper extends Wrapper {
- def preambleHeader = "class %s extends _root_.java.io.Serializable { "
+ def preambleHeader = "sealed class %s extends _root_.java.io.Serializable { "
/** Adds an object that instantiates the outer wrapping class. */
def postamble = s"""
diff --git a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala
index cf055e0758..0bb9eb6a0b 100644
--- a/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala
@@ -6,6 +6,9 @@
package scala.tools.nsc
package interpreter
+import scala.tools.nsc.backend.JavaPlatform
+import scala.tools.nsc.classpath.{AggregateClassPath, ClassPathFactory}
+import scala.tools.nsc.util.ClassPath
import typechecker.Analyzer
/** A layer on top of Global so I can guarantee some extra
@@ -31,4 +34,14 @@ trait ReplGlobal extends Global {
new util.AbstractFileClassLoader(virtualDirectory, loader) {}
}
}
+
+ override def optimizerClassPath(base: ClassPath): ClassPath = {
+ settings.outputDirs.getSingleOutput match {
+ case None => base
+ case Some(out) =>
+ // Make bytecode of previous lines available to the inliner
+ val replOutClasspath = ClassPathFactory.newClassPath(settings.outputDirs.getSingleOutput.get, settings)
+ AggregateClassPath.createAggregate(platform.classPath, replOutClasspath)
+ }
+ }
}
diff --git a/test/benchmarks/README.md b/test/benchmarks/README.md
index 370d610bc4..6c77b83605 100644
--- a/test/benchmarks/README.md
+++ b/test/benchmarks/README.md
@@ -5,9 +5,7 @@ that makes use of the [SBT plugin](https://github.com/ktoso/sbt-jmh) for [JMH](h
## Running a benchmark
-The benchmarks require first building Scala into `../../build/pack` with `ant`.
-If you want to build with `sbt dist/mkPack` instead,
-you'll need to change `scalaHome` in this project.
+The benchmarks require first building Scala into `../../build/pack`.
You'll then need to know the fully-qualified name of the benchmark runner class.
The benchmarking classes are organized under `src/main/scala`,
@@ -18,8 +16,7 @@ Using this example, one would simply run
jmh:runMain scala.collection.mutable.OpenHashMapRunner
-in SBT.
-SBT should be run _from this directory_.
+in SBT, run _from this directory_ (`test/benchmarks`).
The JMH results can be found under `target/jmh-results/`.
`target` gets deleted on an SBT `clean`,
diff --git a/test/benchmarks/build.sbt b/test/benchmarks/build.sbt
index fb05fb2c99..ef603e18b3 100644
--- a/test/benchmarks/build.sbt
+++ b/test/benchmarks/build.sbt
@@ -1,5 +1,5 @@
scalaHome := Some(file("../../build/pack"))
-scalaVersion := "2.12.0-dev"
+scalaVersion := "2.12.1-dev"
scalacOptions ++= Seq("-feature", "-opt:l:classpath")
lazy val root = (project in file(".")).
@@ -7,5 +7,5 @@ lazy val root = (project in file(".")).
settings(
name := "test-benchmarks",
version := "0.0.1",
- libraryDependencies += "org.openjdk.jol" % "jol-core" % "0.4"
+ libraryDependencies += "org.openjdk.jol" % "jol-core" % "0.6"
)
diff --git a/test/benchmarks/project/plugins.sbt b/test/benchmarks/project/plugins.sbt
index aa49ad9872..1b79ce888c 100644
--- a/test/benchmarks/project/plugins.sbt
+++ b/test/benchmarks/project/plugins.sbt
@@ -1,2 +1,2 @@
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
-addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.16")
+addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.17")
diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/VectorMapBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorMapBenchmark.scala
new file mode 100644
index 0000000000..61e621dcdf
--- /dev/null
+++ b/test/benchmarks/src/main/scala/scala/collection/immutable/VectorMapBenchmark.scala
@@ -0,0 +1,32 @@
+package scala.collection.immutable
+
+import org.openjdk.jmh.annotations._
+import org.openjdk.jmh.infra._
+import org.openjdk.jmh.runner.IterationType
+import benchmark._
+import java.util.concurrent.TimeUnit
+
+@BenchmarkMode(Array(Mode.AverageTime))
+@Fork(2)
+@Threads(1)
+@Warmup(iterations = 10)
+@Measurement(iterations = 10)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Benchmark)
+class VectorMapBenchmark {
+ @Param(Array("10", "100", "1000"))
+ var size: Int = _
+
+ var values: Vector[Any] = _
+
+ @Setup(Level.Trial) def initKeys(): Unit = {
+ values = (0 to size).map(i => (i % 4) match {
+ case 0 => i.toString
+ case 1 => i.toChar
+ case 2 => i.toDouble
+ case 3 => i.toInt
+ }).toVector
+ }
+
+ @Benchmark def groupBy = values.groupBy(_.getClass)
+}
diff --git a/test/benchmarks/src/main/scala/scala/collection/mutable/HashMapBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/mutable/HashMapBenchmark.scala
new file mode 100644
index 0000000000..3f01d154e9
--- /dev/null
+++ b/test/benchmarks/src/main/scala/scala/collection/mutable/HashMapBenchmark.scala
@@ -0,0 +1,70 @@
+package scala.collection.mutable
+
+import org.openjdk.jmh.annotations._
+import org.openjdk.jmh.infra._
+import org.openjdk.jmh.runner.IterationType
+import benchmark._
+import java.util.concurrent.TimeUnit
+
+import scala.collection.mutable
+
+@BenchmarkMode(Array(Mode.AverageTime))
+@Fork(2)
+@Threads(1)
+@Warmup(iterations = 10)
+@Measurement(iterations = 10)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Benchmark)
+class HashMapBenchmark {
+ @Param(Array("10", "100", "1000"))
+ var size: Int = _
+
+ var existingKeys: Array[Any] = _
+ var missingKeys: Array[Any] = _
+
+ @Setup(Level.Trial) def initKeys(): Unit = {
+ existingKeys = (0 to size).map(i => (i % 4) match {
+ case 0 => i.toString
+ case 1 => i.toChar
+ case 2 => i.toDouble
+ case 3 => i.toInt
+ }).toArray
+ missingKeys = (size to 2 * size).toArray
+ }
+
+ var map = new mutable.HashMap[Any, Any]
+
+ @Setup(Level.Invocation) def initializeMutable = existingKeys.foreach(v => map.put(v, v))
+
+ @TearDown(Level.Invocation) def tearDown = map.clear()
+
+ @Benchmark def getOrElseUpdate(bh: Blackhole): Unit = {
+ var i = 0;
+ while (i < size) {
+ bh.consume(map.getOrElseUpdate(existingKeys(i), -1))
+ bh.consume(map.getOrElseUpdate(missingKeys(i), -1))
+ i += 1
+ }
+ }
+
+ @Benchmark def get(bh: Blackhole): Unit = {
+ var i = 0;
+ while (i < size) {
+ bh.consume(map.get(existingKeys(i), -1))
+ bh.consume(map.get(missingKeys(i), -1))
+ i += 1
+ }
+ }
+
+ @Benchmark def put(bh: Blackhole): Any = {
+ var map = new mutable.HashMap[Any, Any]
+
+ var i = 0;
+ while (i < size) {
+ map.put(existingKeys(i), i)
+ i += 1
+ }
+
+ map
+ }
+}
diff --git a/test/files/jvm/varargs-separate-bytecode.check b/test/files/jvm/varargs-separate-bytecode.check
new file mode 100644
index 0000000000..1507cd48c5
--- /dev/null
+++ b/test/files/jvm/varargs-separate-bytecode.check
@@ -0,0 +1 @@
+Found vararg overload for method create \ No newline at end of file
diff --git a/test/files/jvm/varargs-separate-bytecode/AbstractProps_1.scala b/test/files/jvm/varargs-separate-bytecode/AbstractProps_1.scala
new file mode 100644
index 0000000000..5dfb8d1a9e
--- /dev/null
+++ b/test/files/jvm/varargs-separate-bytecode/AbstractProps_1.scala
@@ -0,0 +1,8 @@
+package foo
+
+import scala.annotation.varargs
+
+trait AbstractProps {
+ @varargs
+ def create(x: String, y: Int*): AbstractProps = null
+}
diff --git a/test/files/jvm/varargs-separate-bytecode/Props_2.scala b/test/files/jvm/varargs-separate-bytecode/Props_2.scala
new file mode 100644
index 0000000000..3fc09586fc
--- /dev/null
+++ b/test/files/jvm/varargs-separate-bytecode/Props_2.scala
@@ -0,0 +1,3 @@
+import foo.AbstractProps
+
+class Props extends AbstractProps \ No newline at end of file
diff --git a/test/files/jvm/varargs-separate-bytecode/Test.scala b/test/files/jvm/varargs-separate-bytecode/Test.scala
new file mode 100644
index 0000000000..a666de7f39
--- /dev/null
+++ b/test/files/jvm/varargs-separate-bytecode/Test.scala
@@ -0,0 +1,15 @@
+import scala.collection.JavaConverters._
+import scala.tools.asm
+import scala.tools.asm.Opcodes
+import scala.tools.partest.BytecodeTest
+
+object Test extends BytecodeTest {
+ def show: Unit = {
+ val classNode = loadClassNode("Props")
+ val methods = classNode.methods.iterator().asScala.filter( m => m.name == "create")
+
+ for (m <- methods if (m.access & Opcodes.ACC_VARARGS) > 0) {
+ println(s"Found vararg overload for method ${m.name}")
+ }
+ }
+}
diff --git a/test/files/neg/sabin2.check b/test/files/neg/sabin2.check
index aa0e8f734c..cd6fde4608 100644
--- a/test/files/neg/sabin2.check
+++ b/test/files/neg/sabin2.check
@@ -1,6 +1,6 @@
sabin2.scala:22: error: type mismatch;
found : Test.Base#T
- required: _5.T where val _5: Test.Base
+ required: _1.T where val _1: Test.Base
a.set(b.get()) // Error
^
one error found
diff --git a/test/files/neg/sealed-final-neg.check b/test/files/neg/sealed-final-neg.check
index e135f38f8b..5e47c69ed8 100644
--- a/test/files/neg/sealed-final-neg.check
+++ b/test/files/neg/sealed-final-neg.check
@@ -1,7 +1,9 @@
-sealed-final-neg.scala:17: warning: neg1/Foo::bar(I)I is annotated @inline but cannot be inlined: the method is not final and may be overridden.
+sealed-final-neg.scala:17: warning: neg1/Foo::bar(I)I is annotated @inline but could not be inlined:
+The method is not final and may be overridden.
def f = Foo.mkFoo() bar 10
^
-sealed-final-neg.scala:37: warning: neg2/Foo::bar(I)I is annotated @inline but cannot be inlined: the method is not final and may be overridden.
+sealed-final-neg.scala:37: warning: neg2/Foo::bar(I)I is annotated @inline but could not be inlined:
+The method is not final and may be overridden.
def f = Foo.mkFoo() bar 10
^
error: No warnings can be incurred under -Xfatal-warnings.
diff --git a/test/files/neg/t0764.check b/test/files/neg/t0764.check
index 830278e715..0c7cff1e1e 100644
--- a/test/files/neg/t0764.check
+++ b/test/files/neg/t0764.check
@@ -1,5 +1,5 @@
t0764.scala:13: error: type mismatch;
- found : Node{type T = _2.type} where val _2: Node{type T = NextType}
+ found : Node{type T = _1.type} where val _1: Node{type T = NextType}
required: Node{type T = Main.this.AType}
(which expands to) Node{type T = Node{type T = NextType}}
new Main[AType]( (value: AType).prepend )
diff --git a/test/files/neg/t1010.check b/test/files/neg/t1010.check
index 2cc8f9d986..d412d8ac1e 100644
--- a/test/files/neg/t1010.check
+++ b/test/files/neg/t1010.check
@@ -1,6 +1,6 @@
t1010.scala:14: error: type mismatch;
found : MailBox#Message
- required: _3.in.Message where val _3: Actor
+ required: _1.in.Message where val _1: Actor
unstable.send(msg) // in.Message becomes unstable.Message, but that's ok since Message is a concrete type member
^
one error found
diff --git a/test/files/neg/t5120.check b/test/files/neg/t5120.check
index 34d4ebde31..b6a3cb96aa 100644
--- a/test/files/neg/t5120.check
+++ b/test/files/neg/t5120.check
@@ -6,7 +6,7 @@ t5120.scala:11: error: type mismatch;
t5120.scala:25: error: type mismatch;
found : Thread
required: h.T
- (which expands to) _2
+ (which expands to) _1
List(str, num).foreach(h => h.f1 = new Thread())
^
two errors found
diff --git a/test/files/neg/t6829.check b/test/files/neg/t6829.check
index 274094f791..5ccd531be1 100644
--- a/test/files/neg/t6829.check
+++ b/test/files/neg/t6829.check
@@ -1,6 +1,6 @@
t6829.scala:35: error: type mismatch;
found : AgentSimulation.this.state.type (with underlying type G#State)
- required: _9.State
+ required: _1.State
lazy val actions: Map[G#Agent,G#Action] = agents.map(a => a -> a.chooseAction(state)).toMap
^
t6829.scala:45: error: trait AgentSimulation takes type parameters
@@ -17,32 +17,32 @@ t6829.scala:49: error: not found: value nextState
^
t6829.scala:50: error: type mismatch;
found : s.type (with underlying type Any)
- required: _30.State where val _30: G
+ required: _1.State where val _1: G
val r = rewards(agent).r(s,a,s2)
^
t6829.scala:50: error: type mismatch;
found : a.type (with underlying type Any)
- required: _30.Action where val _30: G
+ required: _1.Action where val _1: G
val r = rewards(agent).r(s,a,s2)
^
t6829.scala:50: error: type mismatch;
found : s2.type (with underlying type Any)
- required: _30.State where val _30: G
+ required: _1.State where val _1: G
val r = rewards(agent).r(s,a,s2)
^
t6829.scala:51: error: type mismatch;
found : s.type (with underlying type Any)
- required: _25.State
+ required: _1.State
agent.learn(s,a,s2,r): G#Agent
^
t6829.scala:51: error: type mismatch;
found : a.type (with underlying type Any)
- required: _25.Action
+ required: _1.Action
agent.learn(s,a,s2,r): G#Agent
^
t6829.scala:51: error: type mismatch;
found : s2.type (with underlying type Any)
- required: _25.State
+ required: _1.State
agent.learn(s,a,s2,r): G#Agent
^
t6829.scala:53: error: not found: value nextState
diff --git a/test/files/run/reflection-mem-typecheck.scala b/test/files/run/reflection-mem-typecheck.scala
deleted file mode 100644
index 93ec1c937a..0000000000
--- a/test/files/run/reflection-mem-typecheck.scala
+++ /dev/null
@@ -1,28 +0,0 @@
-import scala.tools.partest.MemoryTest
-
-trait A { type T <: A }
-trait B { type T <: B }
-
-object Test extends MemoryTest {
- lazy val tb = {
- import scala.reflect.runtime.universe._
- import scala.reflect.runtime.{currentMirror => cm}
- import scala.tools.reflect.ToolBox
- cm.mkToolBox()
- }
-
- // I'm not sure this is a great way to test for memory leaks,
- // since we're also testing how good the JVM's GC is, and this is not easily reproduced between machines/over time
- override def maxDelta = 12
- override def calcsPerIter = 8
- override def calc() {
- var snippet = """
- trait A { type T <: A }
- trait B { type T <: B }
- def foo[T](x: List[T]) = x
- foo(List(new A {}, new B {}))
- """.trim
- snippet = snippet + "\n" + (List.fill(50)(snippet.split("\n").last) mkString "\n")
- tb.typecheck(tb.parse(snippet))
- }
-} \ No newline at end of file
diff --git a/test/files/run/repl-inline.check b/test/files/run/repl-inline.check
new file mode 100644
index 0000000000..db729a67dd
--- /dev/null
+++ b/test/files/run/repl-inline.check
@@ -0,0 +1,11 @@
+warning: there was one deprecation warning (since 2.11.0); re-run with -deprecation for details
+callerOfCaller: String
+g: String
+h: String
+g: String
+h: String
+callerOfCaller: String
+g: String
+h: String
+g: String
+h: String
diff --git a/test/files/run/repl-inline.scala b/test/files/run/repl-inline.scala
new file mode 100644
index 0000000000..260ed28a4f
--- /dev/null
+++ b/test/files/run/repl-inline.scala
@@ -0,0 +1,27 @@
+import scala.tools.nsc._
+
+object Test {
+ val testCode =
+ """
+def callerOfCaller = Thread.currentThread.getStackTrace.drop(2).head.getMethodName
+def g = callerOfCaller
+def h = g
+assert(h == "g", h)
+@inline def g = callerOfCaller
+def h = g
+assert(h == "h", h)
+ """
+
+ def main(args: Array[String]) {
+ def test(f: Settings => Unit): Unit = {
+ val settings = new Settings()
+ settings.processArgumentString("-opt:l:classpath")
+ f(settings)
+ settings.usejavacp.value = true
+ val repl = new interpreter.IMain(settings)
+ testCode.linesIterator.foreach(repl.interpret(_))
+ }
+ test(_ => ())
+ test(_.Yreplclassbased.value = true)
+ }
+}
diff --git a/test/files/run/t5125b.check b/test/files/run/t5125b.check
index ddbf908f04..29b438a2d6 100644
--- a/test/files/run/t5125b.check
+++ b/test/files/run/t5125b.check
@@ -5,3 +5,6 @@ public void C2.f(scala.collection.Seq)
public void C2$C3.f(java.lang.String[])
public void C2$C3.f(scala.collection.Seq)
public void C4.f(scala.collection.Seq)
+private void C5.f(int,int[])
+private void C5.f(int,scala.collection.Seq)
+public void C5.f(scala.collection.Seq)
diff --git a/test/files/run/t5125b.scala b/test/files/run/t5125b.scala
index 149c49e213..60ab1d9792 100644
--- a/test/files/run/t5125b.scala
+++ b/test/files/run/t5125b.scala
@@ -23,6 +23,17 @@ class C4 {
}
}
+class C5 {
+ def f(values: String*) = println("Calling C5.f(): " + values)
+ @scala.annotation.varargs
+ private def f(v: Int, values: Int*) = println("Calling C5.f(): " + values)
+
+ def method(): Unit = {
+ @scala.annotation.varargs
+ def f(values: String*) = println("Calling C5.<locally>.f(): " + values)
+ }
+}
+
object Test extends App {
def check(c: Class[_]) {
val methodName = "f"
@@ -34,4 +45,5 @@ object Test extends App {
check(classOf[C2])
check(classOf[C2#C3])
check(classOf[C4])
+ check(classOf[C5])
}
diff --git a/test/files/run/t7747-repl.check b/test/files/run/t7747-repl.check
index 621a70205e..ab37da5722 100644
--- a/test/files/run/t7747-repl.check
+++ b/test/files/run/t7747-repl.check
@@ -246,12 +246,12 @@ scala> case class Bingo()
defined class Bingo
scala> List(BippyBups(), PuppyPups(), Bingo()) // show
-class $read extends _root_.java.io.Serializable {
+sealed class $read extends _root_.java.io.Serializable {
def <init>() = {
super.<init>;
()
};
- class $iw extends _root_.java.io.Serializable {
+ sealed class $iw extends _root_.java.io.Serializable {
def <init>() = {
super.<init>;
()
@@ -262,7 +262,7 @@ class $read extends _root_.java.io.Serializable {
import $line45.$read.INSTANCE.$iw.$iw.PuppyPups;
import $line46.$read.INSTANCE.$iw.$iw.Bingo;
import $line46.$read.INSTANCE.$iw.$iw.Bingo;
- class $iw extends _root_.java.io.Serializable {
+ sealed class $iw extends _root_.java.io.Serializable {
def <init>() = {
super.<init>;
()
diff --git a/test/files/run/t9814.scala b/test/files/run/t9814.scala
new file mode 100644
index 0000000000..3aef3928f6
--- /dev/null
+++ b/test/files/run/t9814.scala
@@ -0,0 +1,28 @@
+import java.lang.reflect.Modifier
+
+import scala.annotation.strictfp
+
+class Foo extends (() => Unit) {
+ def apply(): Unit = synchronized {
+ // we're in a specialized subclass
+ assert(Thread.currentThread.getStackTrace.apply(1).getMethodName == "apply$mcV$sp")
+ assert(Thread.holdsLock(this))
+ }
+}
+
+class Bar extends (() => Unit) {
+ @strictfp def apply(): Unit = synchronized {
+ // we're in a specialized subclass
+ assert(Thread.currentThread.getStackTrace.apply(1).getMethodName == "apply$mcV$sp")
+ assert(Thread.holdsLock(this))
+ }
+}
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ new Foo().apply()
+
+ val m = classOf[Bar].getDeclaredMethod("apply$mcV$sp")
+ assert(Modifier.isStrict(m.getModifiers))
+ }
+}
diff --git a/test/junit/scala/reflect/internal/util/SourceFileTest.scala b/test/junit/scala/reflect/internal/util/SourceFileTest.scala
index cad23eba14..2f2029ad2d 100644
--- a/test/junit/scala/reflect/internal/util/SourceFileTest.scala
+++ b/test/junit/scala/reflect/internal/util/SourceFileTest.scala
@@ -5,6 +5,8 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import scala.tools.testing.AssertUtil._
+
@RunWith(classOf[JUnit4])
class SourceFileTest {
def lineContentOf(code: String, offset: Int) =
@@ -57,4 +59,21 @@ class SourceFileTest {
assertEquals("def", lineContentOf("abc\r\ndef", 8))
assertEquals("def", lineContentOf("abc\r\ndef\r\n", 9))
}
+
+ @Test def si9885_lineToOffset(): Unit = {
+ val text = "a\nb\nc\n"
+ val f = new BatchSourceFile("batch", text)
+ assertThrows[IndexOutOfBoundsException] {
+ f.lineToOffset(3)
+ }
+ assertEquals(4, f.lineToOffset(2))
+
+ val p = Position.offset(f, text.length - 1)
+ val q = Position.offset(f, f.lineToOffset(p.line - 1))
+ assertEquals(p.line, q.line)
+ assertEquals(p.column, q.column + 1)
+ assertThrows[IndexOutOfBoundsException] {
+ Position.offset(f, f.lineToOffset(p.line))
+ }
+ }
}
diff --git a/test/junit/scala/sys/process/t7350.scala b/test/junit/scala/sys/process/PipedProcessTest.scala
index 9fdcac8ccc..53f053e9aa 100644
--- a/test/junit/scala/sys/process/t7350.scala
+++ b/test/junit/scala/sys/process/PipedProcessTest.scala
@@ -12,6 +12,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.control.Exception.ignoring
// Each test normally ends in a moment, but for failure cases, waits until one second.
+// SI-7350, SI-8768
@RunWith(classOf[JUnit4])
class PipedProcessTest {
diff --git a/test/junit/scala/sys/process/ProcessTest.scala b/test/junit/scala/sys/process/ProcessTest.scala
new file mode 100644
index 0000000000..f6d779c2c8
--- /dev/null
+++ b/test/junit/scala/sys/process/ProcessTest.scala
@@ -0,0 +1,25 @@
+package scala.sys.process
+
+import java.io.ByteArrayInputStream
+// should test from outside the package to ensure implicits work
+//import scala.sys.process._
+import scala.util.Properties._
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import org.junit.Assert.assertEquals
+
+@RunWith(classOf[JUnit4])
+class ProcessTest {
+ private def testily(body: => Unit) = if (!isWin) body
+ @Test def t10007(): Unit = testily {
+ val res = ("cat" #< new ByteArrayInputStream("lol".getBytes)).!!
+ assertEquals("lol\n", res)
+ }
+ // test non-hanging
+ @Test def t10055(): Unit = testily {
+ val res = ("cat" #< ( () => -1 ) ).!
+ assertEquals(0, res)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala
index 80fbba133e..a74e73afc9 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/CallGraphTest.scala
@@ -72,7 +72,7 @@ class CallGraphTest extends BytecodeTesting {
| @noinline def f5 = try { 0 } catch { case _: Throwable => 1 }
| @noinline final def f6 = try { 0 } catch { case _: Throwable => 1 }
|
- | @inline @noinline def f7 = try { 0 } catch { case _: Throwable => 1 }
+ | @inline @noinline def f7 = try { 0 } catch { case _: Throwable => 1 } // no warning, @noinline takes precedence
|}
|class D extends C {
| @inline override def f1 = try { 0 } catch { case _: Throwable => 1 }
@@ -91,18 +91,17 @@ class CallGraphTest extends BytecodeTesting {
// The callGraph.callsites map is indexed by instructions of those ClassNodes.
val ok = Set(
- "D::f1()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", // only one warning for D.f1: C.f1 is not annotated @inline
- "C::f3()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", // only one warning for C.f3: D.f3 does not have @inline (and it would also be safe to inline)
- "C::f7()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", // two warnings (the error message mentions C.f7 even if the receiver type is D, because f7 is inherited from C)
- "operand stack at the callsite in Test::t1(LC;)I contains more values",
- "operand stack at the callsite in Test::t2(LD;)I contains more values")
+ "D::f1()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden.", // only one warning for D.f1: C.f1 is not annotated @inline
+ "C::f3()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden.", // only one warning for C.f3: D.f3 does not have @inline (and it would also be safe to inline)
+ "C::f4()I is annotated @inline but could not be inlined:\nThe operand stack at the callsite in Test::t1(LC;)I contains more values",
+ "C::f4()I is annotated @inline but could not be inlined:\nThe operand stack at the callsite in Test::t2(LD;)I contains more values")
var msgCount = 0
val checkMsg = (m: StoreReporter#Info) => {
msgCount += 1
ok exists (m.msg contains _)
}
val List(cCls, cMod, dCls, testCls) = compile(code, checkMsg)
- assert(msgCount == 6, msgCount)
+ assert(msgCount == 4, msgCount)
val List(cf1, cf2, cf3, cf4, cf5, cf6, cf7) = getAsmMethods(cCls, _.startsWith("f"))
val List(df1, df3) = getAsmMethods(dCls, _.startsWith("f"))
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala
index 95b47f7d04..b1aa27fd27 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala
@@ -35,9 +35,9 @@ class InlineWarningTest extends BytecodeTesting {
""".stripMargin
var count = 0
val warns = Set(
- "C::m1()I is annotated @inline but cannot be inlined: the method is not final and may be overridden",
- "T::m2()I is annotated @inline but cannot be inlined: the method is not final and may be overridden",
- "D::m2()I is annotated @inline but cannot be inlined: the method is not final and may be overridden")
+ "C::m1()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden.",
+ "T::m2()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden.",
+ "D::m2()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden.")
compileToBytes(code, allowMessage = i => {count += 1; warns.exists(i.msg contains _)})
assert(count == 4, count)
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala
index 3e0b889e9c..bf9da0f48f 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerIllegalAccessTest.scala
@@ -20,7 +20,7 @@ class InlinerIllegalAccessTest extends BytecodeTesting {
import global.genBCode.bTypes._
def addToRepo(cls: List[ClassNode]): Unit = for (c <- cls) byteCodeRepository.add(c, None)
- def assertEmpty(ins: Option[AbstractInsnNode]) = for (i <- ins)
+ def assertEmpty(ins: List[AbstractInsnNode]) = for (i <- ins)
throw new AssertionError(textify(i))
@Test
@@ -28,7 +28,7 @@ class InlinerIllegalAccessTest extends BytecodeTesting {
val code =
"""package a {
| private class C { // the Scala compiler makes all classes public
- | def f1 = new C // NEW a/C
+ | def f1 = new C // NEW a/C, INVOKESPECIAL a/C.<init> ()V
| def f2 = new Array[C](0) // ANEWARRAY a/C
| def f3 = new Array[Array[C]](0) // ANEWARRAY [La/C;
| }
@@ -46,9 +46,9 @@ class InlinerIllegalAccessTest extends BytecodeTesting {
val methods = cClass.methods.asScala.filter(_.name(0) == 'f').toList
- def check(classNode: ClassNode, test: Option[AbstractInsnNode] => Unit) = {
+ def check(classNode: ClassNode, test: List[AbstractInsnNode] => Unit) = {
for (m <- methods)
- test(inliner.findIllegalAccess(m.instructions, classBTypeFromParsedClassfile(cClass.name), classBTypeFromParsedClassfile(classNode.name)).map(_._1))
+ test(inliner.findIllegalAccess(m.instructions, classBTypeFromParsedClassfile(cClass.name), classBTypeFromParsedClassfile(classNode.name)).right.get)
}
check(cClass, assertEmpty)
@@ -65,7 +65,11 @@ class InlinerIllegalAccessTest extends BytecodeTesting {
check(cClass, assertEmpty)
check(dClass, assertEmpty) // accessing a private class in the same package is OK
check(eClass, {
- case Some(ti: TypeInsnNode) if Set("a/C", "[La/C;")(ti.desc) => ()
+ case (ti: TypeInsnNode) :: is if Set("a/C", "[La/C;")(ti.desc) =>
+ is match {
+ case List(mi: MethodInsnNode) => assert(mi.owner == "a/C" && mi.name == "<init>")
+ case Nil =>
+ }
// MatchError otherwise
})
}
@@ -141,12 +145,12 @@ class InlinerIllegalAccessTest extends BytecodeTesting {
val List(rbD, rcD, rfD, rgD) = dCl.methods.asScala.toList.filter(_.name(0) == 'r').sortBy(_.name)
- def check(method: MethodNode, decl: ClassNode, dest: ClassNode, test: Option[AbstractInsnNode] => Unit): Unit = {
- test(inliner.findIllegalAccess(method.instructions, classBTypeFromParsedClassfile(decl.name), classBTypeFromParsedClassfile(dest.name)).map(_._1))
+ def check(method: MethodNode, decl: ClassNode, dest: ClassNode, test: List[AbstractInsnNode] => Unit): Unit = {
+ test(inliner.findIllegalAccess(method.instructions, classBTypeFromParsedClassfile(decl.name), classBTypeFromParsedClassfile(dest.name)).right.get)
}
- val cOrDOwner = (_: Option[AbstractInsnNode] @unchecked) match {
- case Some(mi: MethodInsnNode) if Set("a/C", "a/D")(mi.owner) => ()
+ val cOrDOwner = (_: List[AbstractInsnNode] @unchecked) match {
+ case List(mi: MethodInsnNode) if Set("a/C", "a/D")(mi.owner) => ()
// MatchError otherwise
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala
index 5362585642..9b1609a130 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala
@@ -31,7 +31,7 @@ class InlinerSeparateCompilationTest {
|}
""".stripMargin
- val warn = "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden"
+ val warn = "T::f()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden."
val List(c, o, oMod, t) = compileClassesSeparately(List(codeA, codeB), args + " -opt-warnings", _.msg contains warn)
assertInvoke(getMethod(c, "t1"), "T", "f")
assertNoInvoke(getMethod(c, "t2"))
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
index a844c20a7f..90a938be35 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -67,7 +67,7 @@ class InlinerTest extends BytecodeTesting {
def canInlineTest(code: String, mod: ClassNode => Unit = _ => ()): Option[OptimizerWarning] = {
val cs = gMethAndFCallsite(code, mod)._2
- inliner.earlyCanInlineCheck(cs) orElse inliner.canInlineBody(cs)
+ inliner.earlyCanInlineCheck(cs) orElse inliner.canInlineCallsite(cs).map(_._1)
}
def inlineTest(code: String, mod: ClassNode => Unit = _ => ()): MethodNode = {
@@ -199,8 +199,8 @@ class InlinerTest extends BytecodeTesting {
val List(c, d) = compile(code)
val hMeth = getAsmMethod(d, "h")
val gCall = getCallsite(hMeth, "g")
- val r = inliner.canInlineBody(gCall)
- assert(r.nonEmpty && r.get.isInstanceOf[IllegalAccessInstruction], r)
+ val r = inliner.canInlineCallsite(gCall)
+ assert(r.nonEmpty && r.get._1.isInstanceOf[IllegalAccessInstruction], r)
}
@Test
@@ -340,7 +340,7 @@ class InlinerTest extends BytecodeTesting {
val fMeth = getAsmMethod(c, "f")
val call = getCallsite(fMeth, "lowestOneBit")
- val warning = inliner.canInlineBody(call)
+ val warning = inliner.canInlineCallsite(call)
assert(warning.isEmpty, warning)
inliner.inline(InlineRequest(call, Nil, null))
@@ -475,7 +475,7 @@ class InlinerTest extends BytecodeTesting {
| def t2 = this.f
|}
""".stripMargin
- val warn = "::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden"
+ val warn = "::f()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden."
var count = 0
val List(c, t) = compile(code, allowMessage = i => {count += 1; i.msg contains warn})
assert(count == 2, count)
@@ -513,7 +513,7 @@ class InlinerTest extends BytecodeTesting {
| def t3(t: T) = t.f // no inlining here
|}
""".stripMargin
- val warn = "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden"
+ val warn = "T::f()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden."
var count = 0
val List(c, oMirror, oModule, t) = compile(code, allowMessage = i => {count += 1; i.msg contains warn})
assert(count == 1, count)
@@ -617,7 +617,7 @@ class InlinerTest extends BytecodeTesting {
|}
""".stripMargin
- val warning = "T1::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden"
+ val warning = "T1::f()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden."
var count = 0
val List(ca, cb, t1, t2a, t2b) = compile(code, allowMessage = i => {count += 1; i.msg contains warning})
assert(count == 4, count) // see comments, f is not inlined 4 times
@@ -698,7 +698,7 @@ class InlinerTest extends BytecodeTesting {
| def t1(c: C) = c.foo
|}
""".stripMargin
- val warn = "C::foo()I is annotated @inline but cannot be inlined: the method is not final and may be overridden"
+ val warn = "C::foo()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden."
var c = 0
compile(code, allowMessage = i => {c += 1; i.msg contains warn})
assert(c == 1, c)
@@ -762,7 +762,7 @@ class InlinerTest extends BytecodeTesting {
|}
""".stripMargin
- val List(c, t, u) = compile(code, allowMessage = _.msg contains "i()I is annotated @inline but cannot be inlined")
+ val List(c, t, u) = compile(code, allowMessage = _.msg contains "::i()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden.")
val m1 = getMethod(c, "m1")
assertInvoke(m1, "T", "a")
assertInvoke(m1, "T", "b")
@@ -969,7 +969,7 @@ class InlinerTest extends BytecodeTesting {
val gCall = getCallsite(hMeth, "g")
val hCall = getCallsite(iMeth, "h")
- val warning = inliner.canInlineBody(gCall)
+ val warning = inliner.canInlineCallsite(gCall)
assert(warning.isEmpty, warning)
inliner.inline(InlineRequest(hCall,
@@ -1053,7 +1053,7 @@ class InlinerTest extends BytecodeTesting {
| def t1 = f1(1) // inlined
| def t2 = f2(1) // not inlined
| def t3 = f1(1): @noinline // not inlined
- | def t4 = f2(1): @inline // not inlined (cannot override the def-site @noinline)
+ | def t4 = f2(1): @inline // inlined
| def t5 = f3(1): @inline // inlined
| def t6 = f3(1): @noinline // not inlined
|
@@ -1067,7 +1067,7 @@ class InlinerTest extends BytecodeTesting {
assertNoInvoke(getMethod(c, "t1"))
assertInvoke(getMethod(c, "t2"), "C", "f2")
assertInvoke(getMethod(c, "t3"), "C", "f1")
- assertInvoke(getMethod(c, "t4"), "C", "f2")
+ assertNoInvoke(getMethod(c, "t4"))
assertNoInvoke(getMethod(c, "t5"))
assertInvoke(getMethod(c, "t6"), "C", "f3")
assertNoInvoke(getMethod(c, "t7"))
@@ -1469,8 +1469,8 @@ class InlinerTest extends BytecodeTesting {
|class C extends T1 with T2
""".stripMargin
val List(c, t1, t2) = compile(code, allowMessage = _ => true)
- // the forwarder C.f is inlined, so there's no invocation
- assertSameSummary(getMethod(c, "f"), List(ICONST_1, IRETURN))
+ // we never inline into mixin forwarders, see scala-dev#259
+ assertInvoke(getMethod(c, "f"), "T2", "f$")
}
@Test
@@ -1622,4 +1622,120 @@ class InlinerTest extends BytecodeTesting {
("oneLastMethodWithVeryVeryLongNam_yetAnotherMethodWithVeryVeryLong_oneMoreMethodWithVeryVeryLongNam_anotherMethodWithVeryVeryLongNam_param",10),
("oneLastMethodWithVeryVery_yetAnotherMethodWithVeryV_oneMoreMethodWithVeryVery_anotherMethodWithVeryVery_methodWithVeryVeryLongNam_param",11)))
}
+
+ @Test
+ def sd259(): Unit = {
+ // - trait methods are not inlined into their static super accessors, and also not into mixin forwarders.
+ // - inlining an invocation of a mixin forwarder also inlines the static accessor and the trait method body.
+ val code =
+ """trait T {
+ | def m1a = 1
+ | final def m1b = 1
+ |
+ | @inline def m2a = 2
+ | @inline final def m2b = 2
+ |
+ | def m3a(f: Int => Int) = f(1)
+ | final def m3b(f: Int => Int) = f(1)
+ |}
+ |final class A extends T
+ |class C {
+ | def t1(t: T) = t.m1a
+ | def t2(t: T) = t.m1b
+ | def t3(t: T) = t.m2a
+ | def t4(t: T) = t.m2b
+ | def t5(t: T) = t.m3a(x => x)
+ | def t6(t: T) = t.m3b(x => x)
+ |
+ | def t7(a: A) = a.m1a
+ | def t8(a: A) = a.m1b
+ | def t9(a: A) = a.m2a
+ | def t10(a: A) = a.m2b
+ | def t11(a: A) = a.m3a(x => x)
+ | def t12(a: A) = a.m3b(x => x)
+ |}
+ """.stripMargin
+ val warn = "T::m2a()I is annotated @inline but could not be inlined:\nThe method is not final and may be overridden."
+ var count = 0
+ val List(a, c, t) = compile(code, allowMessage = i => {count += 1; i.msg contains warn})
+ assert(count == 1)
+
+ assertInvoke(getMethod(t, "m1a$"), "T", "m1a")
+ assertInvoke(getMethod(t, "m1b$"), "T", "m1b")
+ assertInvoke(getMethod(t, "m2a$"), "T", "m2a")
+ assertInvoke(getMethod(t, "m2b$"), "T", "m2b")
+ assertInvoke(getMethod(t, "m3a$"), "T", "m3a")
+ assertInvoke(getMethod(t, "m3b$"), "T", "m3b")
+
+ assertInvoke(getMethod(a, "m1a"), "T", "m1a$")
+ assertInvoke(getMethod(a, "m1b"), "T", "m1b$")
+ assertInvoke(getMethod(a, "m2a"), "T", "m2a$")
+ assertInvoke(getMethod(a, "m2b"), "T", "m2b$")
+ assertInvoke(getMethod(a, "m3a"), "T", "m3a$")
+ assertInvoke(getMethod(a, "m3b"), "T", "m3b$")
+
+ assertInvoke(getMethod(c, "t1"), "T", "m1a")
+ assertInvoke(getMethod(c, "t2"), "T", "m1b")
+
+ assertInvoke(getMethod(c, "t3"), "T", "m2a") // could not inline
+ assertNoInvoke(getMethod(c, "t4"))
+
+ assertInvoke(getMethod(c, "t5"), "T", "m3a") // could not inline
+ assertInvoke(getMethod(c, "t6"), "C", "$anonfun$t6$1") // both forwarders inlined, closure eliminated
+
+ assertInvoke(getMethod(c, "t7"), "A", "m1a")
+ assertInvoke(getMethod(c, "t8"), "A", "m1b")
+
+ assertNoInvoke(getMethod(c, "t9"))
+ assertNoInvoke(getMethod(c, "t10"))
+
+ assertInvoke(getMethod(c, "t11"), "C", "$anonfun$t11$1") // both forwarders inlined, closure eliminated
+ assertInvoke(getMethod(c, "t12"), "C", "$anonfun$t12$1") // both forwarders inlined, closure eliminated
+ }
+
+ @Test
+ def sd259b(): Unit = {
+ val code =
+ """trait T {
+ | def get = 1
+ | @inline final def m = try { get } catch { case _: Throwable => 1 }
+ |}
+ |class A extends T
+ |class C {
+ | def t(a: A) = 1 + a.m // cannot inline a try block onto a non-empty stack
+ |}
+ """.stripMargin
+ val warn =
+ """T::m()I is annotated @inline but could not be inlined:
+ |The operand stack at the callsite in C::t(LA;)I contains more values than the
+ |arguments expected by the callee T::m()I. These values would be discarded
+ |when entering an exception handler declared in the inlined method.""".stripMargin
+ val List(a, c, t) = compile(code, allowMessage = _.msg contains warn)
+
+ // inlinig of m$ is rolled back, because <invokespecial T.m> is not legal in class C.
+ assertInvoke(getMethod(c, "t"), "T", "m$")
+ }
+
+ @Test
+ def sd259c(): Unit = {
+ val code =
+ """trait T {
+ | def bar = 1
+ | @inline final def m = {
+ | def impl = bar // private, non-static method
+ | impl
+ | }
+ |}
+ |class A extends T
+ |class C {
+ | def t(a: A) = a.m
+ |}
+ """.stripMargin
+ val warn =
+ """T::m()I is annotated @inline but could not be inlined:
+ |The callee T::m()I contains the instruction INVOKESPECIAL T.impl$1 ()I
+ |that would cause an IllegalAccessError when inlined into class C.""".stripMargin
+ val List(a, c, t) = compile(code, allowMessage = _.msg contains warn)
+ assertInvoke(getMethod(c, "t"), "T", "m$")
+ }
}
diff --git a/test/junit/scala/tools/nsc/classpath/VirtualDirectoryClassPathTest.scala b/test/junit/scala/tools/nsc/classpath/VirtualDirectoryClassPathTest.scala
new file mode 100644
index 0000000000..234f575b79
--- /dev/null
+++ b/test/junit/scala/tools/nsc/classpath/VirtualDirectoryClassPathTest.scala
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014 Contributor. All rights reserved.
+ */
+package scala.tools.nsc.classpath
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+import scala.reflect.io.VirtualDirectory
+
+
+@RunWith(classOf[JUnit4])
+class VirtualDirectoryClassPathTest {
+
+ @Test
+ def virtualDirectoryClassPath_findClassFile(): Unit = {
+ val base = new VirtualDirectory("base", None)
+ val p1 = base subdirectoryNamed "p1"
+ val p1_Test_class = p1.fileNamed("Test.class")
+ val p2 = base subdirectoryNamed "p2"
+ val p3 = p2 subdirectoryNamed "p3"
+ val p4 = p3 subdirectoryNamed "p4"
+ val p4_Test1_class = p4.fileNamed("Test.class")
+ val classPath = VirtualDirectoryClassPath(base)
+
+ assertEquals(Some(p1_Test_class), classPath.findClassFile("p1/Test"))
+
+ assertEquals(None, classPath.findClassFile("p1/DoesNotExist"))
+ assertEquals(None, classPath.findClassFile("DoesNotExist"))
+ assertEquals(None, classPath.findClassFile("p2"))
+ assertEquals(None, classPath.findClassFile("p2/DoesNotExist"))
+ assertEquals(None, classPath.findClassFile("p4/DoesNotExist"))
+
+ assertEquals(List("p1", "p2"), classPath.packages("").toList.map(_.name).sorted)
+ assertEquals(List(), classPath.packages("p1").toList.map(_.name).sorted)
+ assertEquals(List("p2.p3"), classPath.packages("p2").toList.map(_.name).sorted)
+ assertEquals(List("p2.p3.p4"), classPath.packages("p2.p3").toList.map(_.name).sorted)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
index 78ebb7cf9c..a216b319a8 100644
--- a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
+++ b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala
@@ -1,10 +1,11 @@
package scala.tools.nsc.interpreter
-import java.io.{StringWriter, PrintWriter}
+import java.io.{PrintWriter, StringWriter}
import org.junit.Assert.assertEquals
import org.junit.Test
+import scala.reflect.internal.util.BatchSourceFile
import scala.tools.nsc.Settings
class CompletionTest {
@@ -174,6 +175,24 @@ class CompletionTest {
checkExact(completer, "case class D(a: Int, b: Int) { this.a")("a", "asInstanceOf")
}
+ @Test
+ def replGeneratedCodeDeepPackages(): Unit = {
+ val intp = newIMain()
+ val completer = new PresentationCompilerCompleter(intp)
+ intp.compileSources(new BatchSourceFile("<paste>", "package p1.p2.p3; object Ping { object Pong }"))
+ checkExact(completer, "p1.p2.p")("p3")
+ checkExact(completer, "p1.p2.p3.P")("Ping")
+ checkExact(completer, "p1.p2.p3.Ping.Po")("Pong")
+ }
+
+ @Test
+ def performanceOfLenientMatch(): Unit = {
+ val intp = newIMain()
+ val completer = new PresentationCompilerCompleter(intp)
+ val ident: String = "thisIsAReallyLongMethodNameWithManyManyManyManyChunks"
+ checkExact(completer, s"($ident: Int) => tia")(ident)
+ }
+
def checkExact(completer: PresentationCompilerCompleter, before: String, after: String = "")(expected: String*): Unit = {
assertEquals(expected.toSet, completer.complete(before, after).candidates.toSet)
}