summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bincompat-backward.whitelist.conf9
-rw-r--r--bincompat-forward.whitelist.conf97
-rwxr-xr-xbuild.xml16
-rw-r--r--spec/_layouts/default.yml2
-rw-r--r--src/asm/scala/tools/asm/MethodWriter.java10
-rw-r--r--src/compiler/scala/reflect/quasiquotes/Holes.scala (renamed from src/compiler/scala/tools/reflect/quasiquotes/Holes.scala)2
-rw-r--r--src/compiler/scala/reflect/quasiquotes/Parsers.scala (renamed from src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala)2
-rw-r--r--src/compiler/scala/reflect/quasiquotes/Placeholders.scala (renamed from src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala)2
-rw-r--r--src/compiler/scala/reflect/quasiquotes/Quasiquotes.scala (renamed from src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala)2
-rw-r--r--src/compiler/scala/reflect/quasiquotes/Reifiers.scala (renamed from src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala)2
-rw-r--r--src/compiler/scala/tools/cmd/gen/AnyVals.scala4
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala7
-rw-r--r--src/compiler/scala/tools/nsc/CompileServer.scala1
-rw-r--r--src/compiler/scala/tools/nsc/CompileSocket.scala3
-rw-r--r--src/compiler/scala/tools/nsc/CompilerCommand.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala23
-rw-r--r--src/compiler/scala/tools/nsc/MainBench.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Parsing.scala36
-rw-r--r--src/compiler/scala/tools/nsc/Reporting.scala19
-rw-r--r--src/compiler/scala/tools/nsc/ScriptRunner.scala34
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/DocComments.scala9
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala127
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala89
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala513
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala45
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala61
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala930
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala688
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala396
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BackendStats.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala293
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala72
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala43
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala190
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/Inliners.scala2
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaScanners.scala2
-rw-r--r--src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala4
-rw-r--r--src/compiler/scala/tools/nsc/reporters/Reporter.scala4
-rw-r--r--src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala24
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala221
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala70
-rw-r--r--src/compiler/scala/tools/nsc/settings/Warnings.scala192
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala75
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala10
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/TypingTransformers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala112
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala429
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala130
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala142
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala20
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala27
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala88
-rw-r--r--src/compiler/scala/tools/nsc/util/StatisticsInfo.scala4
-rw-r--r--src/compiler/scala/tools/reflect/FastTrack.scala2
-rw-r--r--src/compiler/scala/tools/reflect/ToolBoxFactory.scala2
-rw-r--r--src/eclipse/repl/.classpath16
-rw-r--r--src/interactive/scala/tools/nsc/interactive/Global.scala2
-rw-r--r--src/library/scala/Option.scala22
-rw-r--r--src/library/scala/PartialFunction.scala5
-rw-r--r--src/library/scala/Predef.scala2
-rw-r--r--src/library/scala/StringContext.scala2
-rw-r--r--src/library/scala/collection/GenMapLike.scala2
-rw-r--r--src/library/scala/collection/Iterator.scala40
-rw-r--r--src/library/scala/collection/SeqViewLike.scala23
-rw-r--r--src/library/scala/collection/TraversableLike.scala2
-rw-r--r--src/library/scala/collection/TraversableOnce.scala2
-rw-r--r--src/library/scala/collection/immutable/Stream.scala26
-rw-r--r--src/library/scala/collection/immutable/StringLike.scala4
-rw-r--r--src/library/scala/concurrent/Future.scala4
-rw-r--r--src/library/scala/concurrent/duration/Deadline.scala6
-rw-r--r--src/library/scala/math/BigDecimal.scala6
-rw-r--r--src/library/scala/math/BigInt.scala6
-rw-r--r--src/library/scala/math/Ordering.scala2
-rw-r--r--src/library/scala/math/PartialOrdering.scala17
-rw-r--r--src/library/scala/runtime/ScalaRunTime.scala2
-rw-r--r--src/library/scala/runtime/SeqCharSequence.scala7
-rw-r--r--src/library/scala/sys/Prop.scala2
-rw-r--r--src/library/scala/sys/package.scala9
-rw-r--r--src/library/scala/util/Properties.scala2
-rw-r--r--src/partest-extras/scala/tools/partest/ASMConverters.scala246
-rw-r--r--src/partest-extras/scala/tools/partest/BytecodeTest.scala30
-rw-r--r--src/reflect/scala/reflect/internal/AnnotationInfos.scala4
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala10
-rw-r--r--src/reflect/scala/reflect/internal/ReificationSupport.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Reporting.scala3
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala25
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala23
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala25
-rw-r--r--src/repl/scala/tools/nsc/interpreter/IMain.scala2
-rw-r--r--src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala2
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala7
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala1
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/Settings.scala2
-rwxr-xr-xsrc/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala80
-rw-r--r--test/files/jvm/innerClassAttribute.check54
-rw-r--r--test/files/jvm/innerClassAttribute/Classes_1.scala92
-rw-r--r--test/files/jvm/innerClassAttribute/Java_A_1.java10
-rw-r--r--test/files/jvm/innerClassAttribute/Test.scala218
-rw-r--r--test/files/jvm/javaReflection.check259
-rw-r--r--test/files/jvm/javaReflection/Classes_1.scala84
-rw-r--r--test/files/jvm/javaReflection/Test.scala137
-rw-r--r--test/files/jvm/t6941/test.scala4
-rw-r--r--test/files/jvm/t7253/test.scala6
-rw-r--r--test/files/jvm/unreachable.flags1
-rw-r--r--test/files/jvm/varargs/VaClass.scala4
-rw-r--r--test/files/neg/compile-time-only-a.check5
-rw-r--r--test/files/neg/forgot-interpolator.scala4
-rw-r--r--test/files/neg/macro-basic-mamdmi.check10
-rw-r--r--test/files/neg/t3909.check1
-rw-r--r--test/files/neg/t4851.check4
-rw-r--r--test/files/neg/t4851/J2.java4
-rw-r--r--test/files/neg/t5691.check24
-rw-r--r--test/files/neg/t5691.flags1
-rw-r--r--test/files/neg/t5691.scala27
-rw-r--r--test/files/neg/t6675b.scala2
-rw-r--r--test/files/neg/t8764.check6
-rw-r--r--test/files/neg/t8764.flags1
-rw-r--r--test/files/neg/t8764.scala9
-rw-r--r--test/files/neg/warn-unused-privates.check39
-rw-r--r--test/files/neg/warn-unused-privates.scala1
-rw-r--r--test/files/pos/t8410.flags1
-rw-r--r--test/files/pos/t8410.scala15
-rw-r--r--test/files/pos/t8498.scala6
-rw-r--r--test/files/pos/t8793.scala15
-rw-r--r--test/files/pos/t8828.flags1
-rw-r--r--test/files/pos/t8828.scala20
-rw-r--r--test/files/run/delambdafyLambdaClassNames.check1
-rw-r--r--test/files/run/delambdafyLambdaClassNames.flags1
-rw-r--r--test/files/run/delambdafyLambdaClassNames/A_1.scala5
-rw-r--r--test/files/run/delambdafyLambdaClassNames/Test.scala4
-rw-r--r--test/files/run/nothingTypeDce.flags1
-rw-r--r--test/files/run/nothingTypeDce.scala63
-rw-r--r--test/files/run/nothingTypeNoFramesNoDce.check1
-rw-r--r--test/files/run/nothingTypeNoFramesNoDce.flags1
-rw-r--r--test/files/run/nothingTypeNoFramesNoDce.scala61
-rw-r--r--test/files/run/nothingTypeNoOpt.flags1
-rw-r--r--test/files/run/nothingTypeNoOpt.scala61
-rw-r--r--test/files/run/t4332.scala2
-rw-r--r--test/files/run/t5256c.check2
-rw-r--r--test/files/run/t8764.check5
-rw-r--r--test/files/run/t8764.flags1
-rw-r--r--test/files/run/t8764.scala16
-rw-r--r--test/files/run/t8803.check16
-rw-r--r--test/files/run/t8803.scala57
-rw-r--r--test/files/run/t8823.scala10
-rw-r--r--test/junit/scala/collection/StreamTest.scala18
-rw-r--r--test/junit/scala/tools/nsc/ScriptRunnerTest.scala23
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala56
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala79
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala81
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala92
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala217
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala87
-rw-r--r--test/junit/scala/tools/nsc/settings/SettingsTest.scala109
-rw-r--r--test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala4
-rw-r--r--test/junit/scala/tools/nsc/symtab/StdNamesTest.scala8
-rw-r--r--test/junit/scala/tools/testing/AssertThrowsTest.scala11
-rw-r--r--test/junit/scala/tools/testing/AssertUtil.scala2
-rw-r--r--test/scaladoc/resources/code-indent.scala6
-rw-r--r--test/scaladoc/run/t8113.check1
-rw-r--r--test/scaladoc/run/t8113.scala36
-rw-r--r--test/scaladoc/run/t8314.check3
-rw-r--r--test/scaladoc/run/t8314.scala16
-rw-r--r--test/scaladoc/scalacheck/HtmlFactoryTest.scala7
-rw-r--r--versions.properties2
177 files changed, 5490 insertions, 2770 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf
index ffacbf0442..1c48887b63 100644
--- a/bincompat-backward.whitelist.conf
+++ b/bincompat-backward.whitelist.conf
@@ -185,6 +185,15 @@ filter {
{
matchName="scala.reflect.runtime.SynchronizedOps.newNestedScope"
problemName=MissingMethodProblem
+ },
+ // see github.com/scala/scala/pull/3925, SI-8627, SI-6440
+ {
+ matchName="scala.collection.TraversableLike.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.immutable.Stream.filteredTail"
+ problemName=MissingMethodProblem
}
]
}
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf
index 0b90cf4c8b..ec80e7cce9 100644
--- a/bincompat-forward.whitelist.conf
+++ b/bincompat-forward.whitelist.conf
@@ -271,6 +271,103 @@ filter {
{
matchName="scala.reflect.api.PredefTypeCreator"
problemName=MissingClassProblem
+ },
+ // see github.com/scala/scala/pull/3925, SI-8627, SI-6440
+ {
+ matchName="scala.collection.IterableViewLike#AbstractTransformed.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.AbstractTraversable.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.TraversableViewLike#AbstractTransformed.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.TraversableLike.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.SeqViewLike#AbstractTransformed.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.immutable.TreeSet.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.immutable.Stream.filteredTail"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.immutable.Stream.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.immutable.Stream.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.immutable.StringOps.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.immutable.TreeMap.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.concurrent.TrieMap.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofByte.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofLong.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofUnit.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofInt.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofChar.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofRef.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofDouble.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofFloat.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofBoolean.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.ArrayOps#ofShort.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.collection.mutable.TreeSet.filterImpl"
+ problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.io.AbstractFile.filterImpl"
+ problemName=MissingMethodProblem
}
]
}
diff --git a/build.xml b/build.xml
index b7950f971c..a394546b0a 100755
--- a/build.xml
+++ b/build.xml
@@ -970,6 +970,7 @@ TODO:
<pathelement location="${test.junit.classes}"/>
<path refid="quick.compiler.build.path"/>
<path refid="quick.repl.build.path"/>
+ <path refid="quick.partest-extras.build.path"/>
<path refid="junit.classpath"/>
</path>
@@ -1440,9 +1441,13 @@ TODO:
<stopwatch name="test.junit.timer"/>
<mkdir dir="${test.junit.classes}"/>
<echo message="Note: details of failed tests will be output to ${build-junit.dir}"/>
+
+ <if><isset property="test.method" /><then><property name="test.methods" value="${test.method}" /></then></if>
<junit fork="yes" haltonfailure="yes" printsummary="on">
<classpath refid="test.junit.compiler.build.path"/>
- <batchtest fork="yes" todir="${build-junit.dir}">
+ <test fork="yes" todir="${build-junit.dir}" if="test.class" unless="test.methods" name="${test.class}" />
+ <test fork="yes" todir="${build-junit.dir}" if="test.methods" name="${test.class}" methods="${test.methods}" />
+ <batchtest fork="yes" todir="${build-junit.dir}" unless="test.class">
<fileset dir="${test.junit.classes}">
<include name="**/*Test.class"/>
</fileset>
@@ -1508,7 +1513,12 @@ TODO:
<!-- ===========================================================================
BINARY COMPATIBILITY TESTING
============================================================================ -->
- <target name="bc.init" depends="init" unless="maven-deps-done-mima">
+ <target name="bc.init" depends="init" if="test.bc.skip">
+ <!-- if test.bc.skip is set, make sure that pc.prepare is not executed either -->
+ <property name="maven-deps-done-mima" value="true"/>
+ </target>
+
+ <target name="bc.prepare" depends="bc.init" unless="maven-deps-done-mima">
<property name="bc-reference-version" value="2.11.0"/>
<property name="bc-build.dir" value="${build.dir}/bc"/>
@@ -1526,7 +1536,7 @@ TODO:
</target>
<target name="test.bc-opt" description="Optimized version of test.bc."> <optimized name="test.bc"/></target>
- <target name="test.bc" depends="bc.init, pack.lib, pack.reflect">
+ <target name="test.bc" depends="bc.prepare, pack.lib, pack.reflect" unless="test.bc.skip">
<bc.check project="library"/>
<bc.check project="reflect"/>
</target>
diff --git a/spec/_layouts/default.yml b/spec/_layouts/default.yml
index 7f17ba30b0..d3296c6f5d 100644
--- a/spec/_layouts/default.yml
+++ b/spec/_layouts/default.yml
@@ -11,7 +11,7 @@
}
});
</script>
- <script type="text/javascript" src="https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
+ <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/2.3-latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<!-- need to use include to see value of page.chapter variable -->
diff --git a/src/asm/scala/tools/asm/MethodWriter.java b/src/asm/scala/tools/asm/MethodWriter.java
index 0c4130e499..d30e04c625 100644
--- a/src/asm/scala/tools/asm/MethodWriter.java
+++ b/src/asm/scala/tools/asm/MethodWriter.java
@@ -37,7 +37,7 @@ package scala.tools.asm;
* @author Eric Bruneton
* @author Eugene Kuleshov
*/
-class MethodWriter extends MethodVisitor {
+public class MethodWriter extends MethodVisitor {
/**
* Pseudo access flag used to denote constructors.
@@ -235,11 +235,19 @@ class MethodWriter extends MethodVisitor {
*/
private int maxStack;
+ public int getMaxStack() {
+ return maxStack;
+ }
+
/**
* Maximum number of local variables for this method.
*/
private int maxLocals;
+ public int getMaxLocals() {
+ return maxLocals;
+ }
+
/**
* Number of local variables in the current stack map frame.
*/
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala b/src/compiler/scala/reflect/quasiquotes/Holes.scala
index 68cc728eb3..38b05f9d4b 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Holes.scala
+++ b/src/compiler/scala/reflect/quasiquotes/Holes.scala
@@ -1,4 +1,4 @@
-package scala.tools.reflect
+package scala.reflect
package quasiquotes
import scala.collection.{immutable, mutable}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/reflect/quasiquotes/Parsers.scala
index 392b7fc881..007bac27da 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
+++ b/src/compiler/scala/reflect/quasiquotes/Parsers.scala
@@ -1,4 +1,4 @@
-package scala.tools.reflect
+package scala.reflect
package quasiquotes
import scala.tools.nsc.ast.parser.{Parsers => ScalaParser}
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala b/src/compiler/scala/reflect/quasiquotes/Placeholders.scala
index b287971815..a5b42f8a1f 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Placeholders.scala
+++ b/src/compiler/scala/reflect/quasiquotes/Placeholders.scala
@@ -1,4 +1,4 @@
-package scala.tools.reflect
+package scala.reflect
package quasiquotes
import java.util.UUID.randomUUID
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala b/src/compiler/scala/reflect/quasiquotes/Quasiquotes.scala
index b33069181c..72e6000e9f 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Quasiquotes.scala
+++ b/src/compiler/scala/reflect/quasiquotes/Quasiquotes.scala
@@ -1,4 +1,4 @@
-package scala.tools.reflect
+package scala.reflect
package quasiquotes
import scala.reflect.macros.runtime.Context
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala b/src/compiler/scala/reflect/quasiquotes/Reifiers.scala
index 95113d5b00..07becdc3c6 100644
--- a/src/compiler/scala/tools/reflect/quasiquotes/Reifiers.scala
+++ b/src/compiler/scala/reflect/quasiquotes/Reifiers.scala
@@ -1,4 +1,4 @@
-package scala.tools.reflect
+package scala.reflect
package quasiquotes
import java.lang.UnsupportedOperationException
diff --git a/src/compiler/scala/tools/cmd/gen/AnyVals.scala b/src/compiler/scala/tools/cmd/gen/AnyVals.scala
index 842851b4f6..e78589908c 100644
--- a/src/compiler/scala/tools/cmd/gen/AnyVals.scala
+++ b/src/compiler/scala/tools/cmd/gen/AnyVals.scala
@@ -111,8 +111,8 @@ import scala.language.implicitConversions"""
" */"),
Op(">>", "/**\n" +
- " * Returns this value bit-shifted left by the specified number of bits,\n" +
- " * filling in the right bits with the same value as the left-most bit of this.\n" +
+ " * Returns this value bit-shifted right by the specified number of bits,\n" +
+ " * filling in the left bits with the same value as the left-most bit of this.\n" +
" * The effect of this is to retain the sign of the value.\n" +
" * @example {{{\n" +
" * -21 >> 3 == -3\n" +
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index f23bca77cd..0a356ed7b6 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -135,11 +135,8 @@ trait CompilationUnits { global: Global =>
@deprecated("Call global.currentRun.reporting.uncheckedWarning directly instead.", "2.11.2")
final def uncheckedWarning(pos: Position, msg: String): Unit = currentRun.reporting.uncheckedWarning(pos, msg)
- // called by ScalaDocAnalyzer, overridden by the IDE (in Reporter)
- // TODO: don't use reporter to communicate comments from parser to IDE!
- @deprecated("This method will be removed.", "2.11.2")
- final def comment(pos: Position, msg: String): Unit = reporter.comment(pos, msg)
-
+ @deprecated("This method will be removed. It does nothing.", "2.11.2")
+ final def comment(pos: Position, msg: String): Unit = {}
/** Is this about a .java source file? */
lazy val isJava = source.file.name.endsWith(".java")
diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala
index 1f3a4237eb..029e1c4629 100644
--- a/src/compiler/scala/tools/nsc/CompileServer.scala
+++ b/src/compiler/scala/tools/nsc/CompileServer.scala
@@ -152,6 +152,7 @@ class StandardCompileServer extends SocketServer {
clearCompiler()
case ex: Throwable =>
warn("Compile server encountered fatal condition: " + ex)
+ reporter.error(null, "Compile server encountered fatal condition: " + ex.getMessage)
shutdown = true
throw ex
}
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
index c4f06b59ec..c693fbe8e2 100644
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
@@ -32,7 +32,8 @@ trait HasCompileSocket {
if (isErrorMessage(line))
noErrors = false
- compileSocket.echo(line)
+ // be consistent with scalac: everything goes to stderr
+ compileSocket.warn(line)
loop()
}
try loop()
diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala
index 3ded456378..9b8e9fa330 100644
--- a/src/compiler/scala/tools/nsc/CompilerCommand.scala
+++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala
@@ -107,7 +107,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) {
else {
val sb = new StringBuilder
allSettings foreach {
- case s: MultiChoiceSetting if s.isHelping => sb append s.help
+ case s: MultiChoiceSetting[_] if s.isHelping => sb append s.help
case _ =>
}
sb.toString
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 5bf0d8d9f7..452081cff1 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -45,7 +45,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
with Printers
with DocComments
with Positions
- with Reporting { self =>
+ with Reporting
+ with Parsing { self =>
// the mirror --------------------------------------------------
@@ -218,6 +219,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** Called from parser, which signals hereby that a method definition has been parsed. */
def signalParseProgress(pos: Position) {}
+ /** Called by ScalaDocAnalyzer when a doc comment has been parsed. */
+ def signalParsedDocComment(comment: String, pos: Position) = {
+ // TODO: this is all very borken (only works for scaladoc comments, not regular ones)
+ // --> add hooks to parser and refactor Interactive global to handle comments directly
+ // in any case don't use reporter for parser hooks
+ reporter.comment(pos, comment)
+ }
+
/** Register new context; called for every created context
*/
def registerContext(c: analyzer.Context) {
@@ -395,7 +404,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
reporter.cancelled || unit.isJava && this.id > maxJavaPhase
}
- final def applyPhase(unit: CompilationUnit) {
+ final def withCurrentUnit(unit: CompilationUnit)(task: => Unit) {
if ((unit ne null) && unit.exists)
lastSeenSourceFile = unit.source
@@ -407,7 +416,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
currentRun.currentUnit = unit
if (!cancelled(unit)) {
currentRun.informUnitStarting(this, unit)
- apply(unit)
+ task
}
currentRun.advanceUnit()
} finally {
@@ -415,6 +424,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
currentRun.currentUnit = unit0
}
}
+
+ final def applyPhase(unit: CompilationUnit) = withCurrentUnit(unit)(apply(unit))
}
// phaseName = "parser"
@@ -968,7 +979,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
/** A Run is a single execution of the compiler on a set of units.
*/
- class Run extends RunContextApi with RunReporting {
+ class Run extends RunContextApi with RunReporting with RunParsing {
/** Have been running into too many init order issues with Run
* during erroneous conditions. Moved all these vals up to the
* top of the file so at least they're not trivially null.
@@ -1169,7 +1180,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val erasurePhase = phaseNamed("erasure")
val posterasurePhase = phaseNamed("posterasure")
// val lazyvalsPhase = phaseNamed("lazyvals")
- // val lambdaliftPhase = phaseNamed("lambdalift")
+ val lambdaliftPhase = phaseNamed("lambdalift")
// val constructorsPhase = phaseNamed("constructors")
val flattenPhase = phaseNamed("flatten")
val mixinPhase = phaseNamed("mixin")
@@ -1361,7 +1372,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
runCheckers()
// output collected statistics
- if (settings.Ystatistics)
+ if (settings.YstatisticsEnabled)
statistics.print(phase)
advancePhase()
diff --git a/src/compiler/scala/tools/nsc/MainBench.scala b/src/compiler/scala/tools/nsc/MainBench.scala
index 03190a63f3..f01de0cbe1 100644
--- a/src/compiler/scala/tools/nsc/MainBench.scala
+++ b/src/compiler/scala/tools/nsc/MainBench.scala
@@ -24,7 +24,7 @@ object MainBench extends Driver with EvalLoop {
var start = System.nanoTime()
for (i <- 0 until NIter) {
if (i == NIter-1) {
- theCompiler.settings.Ystatistics.value = true
+ theCompiler.settings.Ystatistics.default.get foreach theCompiler.settings.Ystatistics.add
Statistics.enabled = true
}
process(args)
diff --git a/src/compiler/scala/tools/nsc/Parsing.scala b/src/compiler/scala/tools/nsc/Parsing.scala
new file mode 100644
index 0000000000..4dd3c3f378
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/Parsing.scala
@@ -0,0 +1,36 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL, Typesafe Inc.
+ * @author Adriaan Moors
+ */
+
+package scala
+package tools.nsc
+
+import scala.reflect.internal.Positions
+import scala.tools.nsc.reporters.Reporter
+
+/** Similar to Reporting: gather global functionality specific to parsing.
+ */
+trait Parsing { self : Positions with Reporting =>
+ def currentRun: RunParsing
+
+ trait RunParsing {
+ val parsing: PerRunParsing = new PerRunParsing
+ }
+
+ class PerRunParsing {
+ // for repl
+ private[this] var incompleteHandler: (Position, String) => Unit = null
+ def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = {
+ val saved = incompleteHandler
+ incompleteHandler = handler
+ try thunk
+ finally incompleteHandler = saved
+ }
+
+ def incompleteHandled = incompleteHandler != null
+ def incompleteInputError(pos: Position, msg: String): Unit =
+ if (incompleteHandled) incompleteHandler(pos, msg)
+ else reporter.error(pos, msg)
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala
index 0263586418..c9782de7c8 100644
--- a/src/compiler/scala/tools/nsc/Reporting.scala
+++ b/src/compiler/scala/tools/nsc/Reporting.scala
@@ -33,7 +33,7 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
if (option) reporter.warning(pos, msg)
else if (!(warnings contains pos)) warnings += ((pos, msg))
def summarize() =
- if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings)) {
+ if (warnings.nonEmpty && (option.isDefault || option)) {
val numWarnings = warnings.size
val warningVerb = if (numWarnings == 1) "was" else "were"
val warningCount = countElementsAsString(numWarnings, s"$what warning")
@@ -104,20 +104,5 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
if (settings.fatalWarnings && reporter.hasWarnings)
reporter.error(NoPosition, "No warnings can be incurred under -Xfatal-warnings.")
}
-
- // for repl
- private[this] var incompleteHandler: (Position, String) => Unit = null
- def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = {
- val saved = incompleteHandler
- incompleteHandler = handler
- try thunk
- finally incompleteHandler = saved
- }
-
- def incompleteHandled = incompleteHandler != null
- def incompleteInputError(pos: Position, msg: String): Unit =
- if (incompleteHandled) incompleteHandler(pos, msg)
- else reporter.error(pos, msg)
-
}
-} \ No newline at end of file
+}
diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala
index c2d62db558..7d5c6f6fff 100644
--- a/src/compiler/scala/tools/nsc/ScriptRunner.scala
+++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala
@@ -6,7 +6,7 @@
package scala
package tools.nsc
-import io.{ Directory, File, Path }
+import io.{ AbstractFile, Directory, File, Path }
import java.io.IOException
import scala.tools.nsc.reporters.{Reporter,ConsoleReporter}
import util.Exceptional.unwrap
@@ -111,6 +111,12 @@ class ScriptRunner extends HasCompileSocket {
else None
}
+ def hasClassToRun(d: Directory): Boolean = {
+ import util.ClassPath.{ DefaultJavaContext => ctx }
+ val cp = ctx.newClassPath(AbstractFile.getDirectory(d))
+ cp.findClass(mainClass).isDefined
+ }
+
/* The script runner calls sys.exit to communicate a return value, but this must
* not take place until there are no non-daemon threads running. Tickets #1955, #2006.
*/
@@ -124,15 +130,21 @@ class ScriptRunner extends HasCompileSocket {
compile match {
case Some(compiledPath) =>
- try io.Jar.create(jarFile, compiledPath, mainClass)
- catch { case _: Exception => jarFile.delete() }
-
- if (jarOK) {
- compiledPath.deleteRecursively()
- handler(jarFile.toAbsolute.path)
+ if (!hasClassToRun(compiledPath)) {
+ // it compiled ok, but there is nothing to run;
+ // running an empty script should succeed
+ true
+ } else {
+ try io.Jar.create(jarFile, compiledPath, mainClass)
+ catch { case _: Exception => jarFile.delete() }
+
+ if (jarOK) {
+ compiledPath.deleteRecursively()
+ handler(jarFile.toAbsolute.path)
+ }
+ // jar failed; run directly from the class files
+ else handler(compiledPath.path)
}
- // jar failed; run directly from the class files
- else handler(compiledPath.path)
case _ => false
}
}
@@ -140,8 +152,8 @@ class ScriptRunner extends HasCompileSocket {
if (jarOK) handler(jarFile.toAbsolute.path) // pre-compiled jar is current
else recompile() // jar old - recompile the script.
}
- // don't use a cache jar at all--just use the class files
- else compile exists (cp => handler(cp.path))
+ // don't use a cache jar at all--just use the class files, if they exist
+ else compile exists (cp => !hasClassToRun(cp) || handler(cp.path))
}
}
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala
index 6d9b41ec45..02a199f7ac 100755
--- a/src/compiler/scala/tools/nsc/ast/DocComments.scala
+++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala
@@ -59,14 +59,21 @@ trait DocComments { self: Global =>
comment.defineVariables(sym)
}
+
+ def replaceInheritDocToInheritdoc(docStr: String):String = {
+ docStr.replaceAll("""\{@inheritDoc\p{Zs}*\}""", "@inheritdoc")
+ }
+
/** The raw doc comment of symbol `sym`, minus usecase and define sections, augmented by
* missing sections of an inherited doc comment.
* If a symbol does not have a doc comment but some overridden version of it does,
* the doc comment of the overridden version is copied instead.
*/
def cookedDocComment(sym: Symbol, docStr: String = ""): String = cookedDocComments.getOrElseUpdate(sym, {
- val ownComment = if (docStr.length == 0) docComments get sym map (_.template) getOrElse ""
+ var ownComment = if (docStr.length == 0) docComments get sym map (_.template) getOrElse ""
else DocComment(docStr).template
+ ownComment = replaceInheritDocToInheritdoc(ownComment)
+
superComment(sym) match {
case None =>
if (ownComment.indexOf("@inheritdoc") != -1)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index c90f0d0173..8d810d456e 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -232,7 +232,7 @@ self =>
override def incompleteInputError(msg: String): Unit = {
val offset = source.content.length - 1
if (smartParsing) syntaxErrors += ((offset, msg))
- else currentRun.reporting.incompleteInputError(o2p(offset), msg)
+ else currentRun.parsing.incompleteInputError(o2p(offset), msg)
}
/** parse unit. If there are inbalanced braces,
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 0248806fff..9ebc94b5fc 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -1260,7 +1260,7 @@ trait Scanners extends ScannersCommon {
override def deprecationWarning(off: Offset, msg: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg)
override def error (off: Offset, msg: String) = reporter.error(unit.position(off), msg)
- override def incompleteInputError(off: Offset, msg: String) = currentRun.reporting.incompleteInputError(unit.position(off), msg)
+ override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg)
private var bracePatches: List[BracePatch] = patches
diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
index 64b762696e..df2073785b 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala
@@ -83,7 +83,7 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse
private def initialUnitBody(unit: CompilationUnit): Tree = {
if (unit.isJava) new JavaUnitParser(unit).parse()
- else if (currentRun.reporting.incompleteHandled) newUnitParser(unit).parse()
+ else if (currentRun.parsing.incompleteHandled) newUnitParser(unit).parse()
else newUnitParser(unit).smartParse()
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
new file mode 100644
index 0000000000..0c0d726630
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -0,0 +1,127 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc.backend.jvm
+
+import scala.tools.nsc.Global
+import PartialFunction._
+
+/**
+ * This trait contains code shared between GenBCode and GenASM that depends on types defined in
+ * the compiler cake (Global).
+ */
+final class BCodeAsmCommon[G <: Global](val global: G) {
+ import global._
+
+ /**
+ * True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
+ * member class. This method is used to decide if we should emit an EnclosingMethod attribute.
+ * It is also used to decide whether the "owner" field in the InnerClass attribute should be
+ * null.
+ */
+ def isAnonymousOrLocalClass(classSym: Symbol): Boolean = {
+ assert(classSym.isClass, s"not a class: $classSym")
+ val res = (classSym.isAnonymousClass || !classSym.originalOwner.isClass)
+ // lambda classes are always top-level classes.
+ if (res) assert(!classSym.isDelambdafyFunction)
+ res
+ }
+
+ /**
+ * Returns the enclosing method for non-member classes. In the following example
+ *
+ * class A {
+ * def f = {
+ * class B {
+ * class C
+ * }
+ * }
+ * }
+ *
+ * the method returns Some(f) for B, but None for C, because C is a member class. For non-member
+ * classes that are not enclosed by a method, it returns None:
+ *
+ * class A {
+ * { class B }
+ * }
+ *
+ * In this case, for B, we return None.
+ *
+ * The EnclosingMethod attribute needs to be added to non-member classes (see doc in BTypes).
+ * This is a source-level property, so we need to use the originalOwner chain to reconstruct it.
+ */
+ private def enclosingMethodForEnclosingMethodAttribute(classSym: Symbol): Option[Symbol] = {
+ assert(classSym.isClass, classSym)
+ def enclosingMethod(sym: Symbol): Option[Symbol] = {
+ if (sym.isClass || sym == NoSymbol) None
+ else if (sym.isMethod) Some(sym)
+ else enclosingMethod(sym.originalOwner)
+ }
+ enclosingMethod(classSym.originalOwner)
+ }
+
+ /**
+ * The enclosing class for emitting the EnclosingMethod attribute. Since this is a source-level
+ * property, this method looks at the originalOwner chain. See doc in BTypes.
+ */
+ private def enclosingClassForEnclosingMethodAttribute(classSym: Symbol): Symbol = {
+ assert(classSym.isClass, classSym)
+ def enclosingClass(sym: Symbol): Symbol = {
+ if (sym.isClass) sym
+ else enclosingClass(sym.originalOwner)
+ }
+ enclosingClass(classSym.originalOwner)
+ }
+
+ final case class EnclosingMethodEntry(owner: String, name: String, methodDescriptor: String)
+
+ /**
+ * Data for emitting an EnclosingMethod attribute. None if `classSym` is a member class (not
+ * an anonymous or local class). See doc in BTypes.
+ *
+ * The class is parametrized by two functions to obtain a bytecode class descriptor for a class
+ * symbol, and to obtain a method signature descriptor fro a method symbol. These function depend
+ * on the implementation of GenASM / GenBCode, so they need to be passed in.
+ */
+ def enclosingMethodAttribute(classSym: Symbol, classDesc: Symbol => String, methodDesc: Symbol => String): Option[EnclosingMethodEntry] = {
+ if (isAnonymousOrLocalClass(classSym)) {
+ val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym)
+ debuglog(s"enclosing method for $classSym is $methodOpt (in ${methodOpt.map(_.enclClass)})")
+ Some(EnclosingMethodEntry(
+ classDesc(enclosingClassForEnclosingMethodAttribute(classSym)),
+ methodOpt.map(_.javaSimpleName.toString).orNull,
+ methodOpt.map(methodDesc).orNull))
+ } else {
+ None
+ }
+ }
+
+ /**
+ * This is basically a re-implementation of sym.isStaticOwner, but using the originalOwner chain.
+ *
+ * The problem is that we are interested in a source-level property. Various phases changed the
+ * symbol's properties in the meantime, mostly lambdalift modified (destructively) the owner.
+ * Therefore, `sym.isStatic` is not what we want. For example, in
+ * object T { def f { object U } }
+ * the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here.
+ */
+ def isOriginallyStaticOwner(sym: Symbol): Boolean = {
+ sym.isPackageClass || sym.isModuleClass && isOriginallyStaticOwner(sym.originalOwner)
+ }
+
+ /**
+ * The member classes of a class symbol. Note that the result of this method depends on the
+ * current phase, for example, after lambdalift, all local classes become member of the enclosing
+ * class.
+ */
+ def memberClassesOf(classSymbol: Symbol): List[Symbol] = classSymbol.info.decls.collect({
+ case sym if sym.isClass =>
+ sym
+ case sym if sym.isModule =>
+ val r = exitingPickler(sym.moduleClass)
+ assert(r != NoSymbol, sym.fullLocationString)
+ r
+ })(collection.breakOut)
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index 2d1030121e..daf36ce374 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -9,7 +9,6 @@ package tools.nsc
package backend
package jvm
-import scala.collection.{ mutable, immutable }
import scala.annotation.switch
import scala.tools.asm
@@ -23,8 +22,9 @@ import scala.tools.asm
abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
import global._
import definitions._
- import bCodeICodeCommon._
import bTypes._
+ import bCodeICodeCommon._
+ import coreBTypes._
/*
* Functionality to build the body of ASM MethodNode, except for `synchronized` and `try` expressions.
@@ -92,7 +92,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val thrownKind = tpeTK(expr)
// `throw null` is valid although scala.Null (as defined in src/libray-aux) isn't a subtype of Throwable.
// Similarly for scala.Nothing (again, as defined in src/libray-aux).
- assert(thrownKind.isNullType || thrownKind.isNothingType || exemplars.get(thrownKind).isSubtypeOf(ThrowableReference))
+ assert(thrownKind.isNullType || thrownKind.isNothingType || thrownKind.asClassBType.isSubtypeOf(ThrowableReference))
genLoad(expr, thrownKind)
lineNumber(expr)
emit(asm.Opcodes.ATHROW) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level.
@@ -123,7 +123,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
// binary operation
case rarg :: Nil =>
- resKind = maxType(tpeTK(larg), tpeTK(rarg))
+ resKind = tpeTK(larg).maxType(tpeTK(rarg))
if (scalaPrimitives.isShiftOp(code) || scalaPrimitives.isBitwiseOp(code)) {
assert(resKind.isIntegralType || (resKind == BOOL),
s"$resKind incompatible with arithmetic modulo operation.")
@@ -282,9 +282,10 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val Local(tk, _, idx, isSynth) = locals.getOrMakeLocal(sym)
if (rhs == EmptyTree) { emitZeroOf(tk) }
else { genLoad(rhs, tk) }
+ val localVarStart = currProgramPoint()
bc.store(idx, tk)
if (!isSynth) { // there are case <synthetic> ValDef's emitted by patmat
- varsInScope ::= (sym -> currProgramPoint())
+ varsInScope ::= (sym -> localVarStart)
}
generatedType = UNIT
@@ -321,7 +322,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
mnode.visitVarInsn(asm.Opcodes.ALOAD, 0)
generatedType =
if (tree.symbol == ArrayClass) ObjectReference
- else ClassBType(thisName) // inner class (if any) for claszSymbol already tracked.
+ else classBTypeFromSymbol(claszSymbol)
}
case Select(Ident(nme.EMPTY_PACKAGE_NAME), module) =>
@@ -459,9 +460,10 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case ClazzTag =>
val toPush: BType = {
- val kind = toTypeKind(const.typeValue)
- if (kind.isPrimitive) classLiteral(kind)
- else kind
+ toTypeKind(const.typeValue) match {
+ case kind: PrimitiveBType => boxedClassOfPrimitive(kind)
+ case kind => kind
+ }
}
mnode.visitLdcInsn(toPush.toASMType)
@@ -558,7 +560,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
abort(s"Erasure should have added an unboxing operation to prevent this cast. Tree: $app")
}
else if (r.isPrimitive) {
- bc isInstance classLiteral(r)
+ bc isInstance boxedClassOfPrimitive(r.asPrimitiveBType)
}
else {
assert(r.isRef, r) // ensure that it's not a method
@@ -619,7 +621,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
case rt: ClassBType =>
- assert(exemplar(ctor.owner).c == rt, s"Symbol ${ctor.owner.fullName} is different from $rt")
+ assert(classBTypeFromSymbol(ctor.owner) == rt, s"Symbol ${ctor.owner.fullName} is different from $rt")
mnode.visitTypeInsn(asm.Opcodes.NEW, rt.internalName)
bc dup generatedType
genLoadArguments(args, paramTKs(app))
@@ -632,16 +634,16 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) =>
val nativeKind = tpeTK(expr)
genLoad(expr, nativeKind)
- val MethodNameAndType(mname, mdesc) = asmBoxTo(nativeKind)
- bc.invokestatic(BoxesRunTime.internalName, mname, mdesc)
+ val MethodNameAndType(mname, methodType) = asmBoxTo(nativeKind)
+ bc.invokestatic(BoxesRunTime.internalName, mname, methodType.descriptor)
generatedType = boxResultType(fun.symbol) // was toTypeKind(fun.symbol.tpe.resultType)
case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) =>
genLoad(expr)
val boxType = unboxResultType(fun.symbol) // was toTypeKind(fun.symbol.owner.linkedClassOfClass.tpe)
generatedType = boxType
- val MethodNameAndType(mname, mdesc) = asmUnboxTo(boxType)
- bc.invokestatic(BoxesRunTime.internalName, mname, mdesc)
+ val MethodNameAndType(mname, methodType) = asmUnboxTo(boxType)
+ bc.invokestatic(BoxesRunTime.internalName, mname, methodType.descriptor)
case app @ Apply(fun, args) =>
val sym = fun.symbol
@@ -807,13 +809,57 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
def adapt(from: BType, to: BType) {
- if (!conforms(from, to)) {
+ if (!from.conformsTo(to)) {
to match {
case UNIT => bc drop from
case _ => bc.emitT2T(from, to)
}
} else if (from.isNothingType) {
- emit(asm.Opcodes.ATHROW) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level.
+ /* There are two possibilities for from.isNothingType: emitting a "throw e" expressions and
+ * loading a (phantom) value of type Nothing.
+ *
+ * The Nothing type in Scala's type system does not exist in the JVM. In bytecode, Nothing
+ * is mapped to scala.runtime.Nothing$. To the JVM, a call to Predef.??? looks like it would
+ * return an object of type Nothing$. We need to do something with that phantom object on
+ * the stack. "Phantom" because it never exists: such methods always throw, but the JVM does
+ * not know that.
+ *
+ * Note: The two verifiers (old: type inference, new: type checking) have different
+ * requirements. Very briefly:
+ *
+ * Old (http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.2.1): at
+ * each program point, no matter what branches were taken to get there
+ * - Stack is same size and has same typed values
+ * - Local and stack values need to have consistent types
+ * - In practice, the old verifier seems to ignore unreachable code and accept any
+ * instructions after an ATHROW. For example, there can be another ATHROW (without
+ * loading another throwable first).
+ *
+ * New (http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1)
+ * - Requires consistent stack map frames. GenBCode generates stack frames if -target:jvm-1.6
+ * or higher.
+ * - In practice: the ASM library computes stack map frames for us (ClassWriter). Emitting
+ * correct frames after an ATHROW is probably complex, so ASM uses the following strategy:
+ * - Every time when generating an ATHROW, a new basic block is started.
+ * - During classfile writing, such basic blocks are found to be dead: no branches go there
+ * - Eliminating dead code would probably require complex shifts in the output byte buffer
+ * - But there's an easy solution: replace all code in the dead block with with
+ * `nop; nop; ... nop; athrow`, making sure the bytecode size stays the same
+ * - The corresponding stack frame can be easily generated: on entering a dead the block,
+ * the frame requires a single Throwable on the stack.
+ * - Since there are no branches to the dead block, the frame requirements are never violated.
+ *
+ * To summarize the above: it does matter what we emit after an ATHROW.
+ *
+ * NOW: if we end up here because we emitted a load of a (phantom) value of type Nothing$,
+ * there was no ATHROW emitted. So, we have to make the verifier happy and do something
+ * with that value. Since Nothing$ extends Throwable, the easiest is to just emit an ATHROW.
+ *
+ * If we ended up here because we generated a "throw e" expression, we know the last
+ * emitted instruction was an ATHROW. As explained above, it is OK to emit a second ATHROW,
+ * the verifiers will be happy.
+ */
+ emit(asm.Opcodes.ATHROW)
} else if (from.isNullType) {
bc drop from
emit(asm.Opcodes.ACONST_NULL)
@@ -948,7 +994,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
def genCallMethod(method: Symbol, style: InvokeStyle, hostClass0: Symbol = null, pos: Position = NoPosition) {
val siteSymbol = claszSymbol
- val hostSymbol = if (hostClass0 == null) method.owner else hostClass0;
+ val hostSymbol = if (hostClass0 == null) method.owner else hostClass0
val methodOwner = method.owner
// info calls so that types are up to date; erasure may add lateINTERFACE to traits
hostSymbol.info ; methodOwner.info
@@ -966,8 +1012,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
|| methodOwner == definitions.ObjectClass
)
val receiver = if (useMethodOwner) methodOwner else hostSymbol
- val bmOwner = asmClassType(receiver)
- val jowner = bmOwner.internalName
+ val jowner = internalName(receiver)
val jname = method.javaSimpleName.toString
val bmType = asmMethodType(method)
val mdescr = bmType.descriptor
@@ -1100,7 +1145,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
genCZJUMP(success, failure, op, ObjectReference)
}
else {
- val tk = maxType(tpeTK(l), tpeTK(r))
+ val tk = tpeTK(l).maxType(tpeTK(r))
genLoad(l, tk)
genLoad(r, tk)
genCJUMP(success, failure, op, tk)
@@ -1206,7 +1251,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
genCZJUMP(success, failure, icodes.NE, BOOL)
} else {
// l == r -> if (l eq null) r eq null else l.equals(r)
- val eqEqTempLocal = locals.makeLocal(AnyRefReference, nme.EQEQ_LOCAL_VAR.toString)
+ val eqEqTempLocal = locals.makeLocal(ObjectReference, nme.EQEQ_LOCAL_VAR.toString)
val lNull = new asm.Label
val lNonNull = new asm.Label
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index de1587c7c3..14bffd67ab 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -8,8 +8,7 @@ package tools.nsc
package backend.jvm
import scala.tools.asm
-import scala.annotation.switch
-import scala.collection.{ immutable, mutable }
+import scala.collection.mutable
import scala.tools.nsc.io.AbstractFile
/*
@@ -19,9 +18,10 @@ import scala.tools.nsc.io.AbstractFile
* @version 1.0
*
*/
-abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
+abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
import global._
import bTypes._
+ import coreBTypes._
/*
* must-single-thread
@@ -53,24 +53,6 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
// https://issues.scala-lang.org/browse/SI-3872
// -----------------------------------------------------------------------------------------
- /*
- * can-multi-thread
- */
- def firstCommonSuffix(as: List[Tracked], bs: List[Tracked]): ClassBType = {
- var chainA = as
- var chainB = bs
- var fcs: Tracked = null
- do {
- if (chainB contains chainA.head) fcs = chainA.head
- else if (chainA contains chainB.head) fcs = chainB.head
- else {
- chainA = chainA.tail
- chainB = chainB.tail
- }
- } while (fcs == null)
- fcs.c
- }
-
/* An `asm.ClassWriter` that uses `jvmWiseLUB()`
* The internal name of the least common ancestor of the types given by inameA and inameB.
* It's what ASM needs to know in order to compute stack map frames, http://asm.ow2.org/doc/developer-guide.html#controlflow
@@ -78,57 +60,17 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
final class CClassWriter(flags: Int) extends asm.ClassWriter(flags) {
/**
- * This method is thread re-entrant because chrs never grows during its operation (that's
- * because all TypeNames being looked up have already been entered).
- * To stress this point, rather than using `newTypeName()` we use `lookupTypeName()`
- *
- * can-multi-thread
+ * This method is thread-safe: it depends only on the BTypes component, which does not depend
+ * on global. TODO @lry move to a different place where no global is in scope, on bTypes.
*/
override def getCommonSuperClass(inameA: String, inameB: String): String = {
- val a = ClassBType(lookupTypeName(inameA.toCharArray))
- val b = ClassBType(lookupTypeName(inameB.toCharArray))
- val lca = jvmWiseLUB(a, b)
- val lcaName = lca.internalName // don't call javaName because that side-effects innerClassBuffer.
- assert(lcaName != "scala/Any")
-
- lcaName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things.
- }
-
- }
-
- /**
- * Finding the least upper bound in agreement with the bytecode verifier (given two internal names
- * handed out by ASM)
- * Background:
- * http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
- * http://comments.gmane.org/gmane.comp.java.vm.languages/2293
- * https://issues.scala-lang.org/browse/SI-3872
- *
- * can-multi-thread
- */
- def jvmWiseLUB(a: ClassBType, b: ClassBType): ClassBType = {
-
- assert(a.isNonSpecial, s"jvmWiseLUB() received a non-plain-class $a")
- assert(b.isNonSpecial, s"jvmWiseLUB() received a non-plain-class $b")
-
- val ta = exemplars.get(a)
- val tb = exemplars.get(b)
-
- val res = (ta.isInterface, tb.isInterface) match {
- case (true, true) =>
- // exercised by test/files/run/t4761.scala
- if (tb.isSubtypeOf(ta.c)) ta.c
- else if (ta.isSubtypeOf(tb.c)) tb.c
- else ObjectReference
- case (true, false) =>
- if (tb.isSubtypeOf(a)) a else ObjectReference
- case (false, true) =>
- if (ta.isSubtypeOf(b)) b else ObjectReference
- case _ =>
- firstCommonSuffix(ta :: ta.superClasses, tb :: tb.superClasses)
+ val a = classBTypeFromInternalName(inameA)
+ val b = classBTypeFromInternalName(inameB)
+ val lub = a.jvmWiseLUB(b)
+ val lubName = lub.internalName
+ assert(lubName != "scala/Any")
+ lubName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things.
}
- assert(res.isNonSpecial, "jvmWiseLUB() returned a non-plain-class.")
- res
}
/*
@@ -230,7 +172,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
def fieldSymbols(cls: Symbol): List[Symbol] = {
for (f <- cls.info.decls.toList ;
if !f.isMethod && f.isTerm && !f.isModule
- ) yield f;
+ ) yield f
}
/*
@@ -261,38 +203,16 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*
* can-multi-thread
*/
- final def addInnerClassesASM(jclass: asm.ClassVisitor, refedInnerClasses: Iterable[BType]) {
- // used to detect duplicates.
- val seen = mutable.Map.empty[String, String]
- // result without duplicates, not yet sorted.
- val result = mutable.Set.empty[InnerClassEntry]
-
- for(s: BType <- refedInnerClasses;
- e: InnerClassEntry <- exemplars.get(s).innersChain) {
-
- assert(e.name != null, "saveInnerClassesFor() is broken.") // documentation
- val doAdd = seen.get(e.name) match {
- // TODO is it ok for prevOName to be null? (Someone should really document the invariants of the InnerClasses bytecode attribute)
- case Some(prevOName) =>
- // this occurs e.g. when innerClassBuffer contains both class Thread$State, object Thread$State,
- // i.e. for them it must be the case that oname == java/lang/Thread
- assert(prevOName == e.outerName, "duplicate")
- false
- case None => true
- }
-
- if (doAdd) {
- seen += (e.name -> e.outerName)
- result += e
- }
-
+ final def addInnerClassesASM(jclass: asm.ClassVisitor, refedInnerClasses: List[ClassBType]) {
+ val allNestedClasses = refedInnerClasses.flatMap(_.enclosingNestedClassesChain).distinct
+
+ // sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler
+ for (nestedClass <- allNestedClasses.sortBy(_.internalName.toString)) {
+ // Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes.
+ val Some(e) = nestedClass.innerClassAttributeEntry
+ jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags)
}
- // sorting ensures inner classes are listed after their enclosing class thus satisfying the Eclipse Java compiler
- for(e <- result.toList sortBy (_.name.toString)) {
- jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.access)
- }
-
- } // end of method addInnerClassesASM()
+ }
/*
* Custom attribute (JVMS 4.7.1) "ScalaSig" used as marker only
@@ -323,8 +243,8 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
* can-multi-thread
*/
def createJAttribute(name: String, b: Array[Byte], offset: Int, len: Int): asm.Attribute = {
- val dest = new Array[Byte](len);
- System.arraycopy(b, offset, dest, 0, len);
+ val dest = new Array[Byte](len)
+ System.arraycopy(b, offset, dest, 0, len)
new asm.CustomAttr(name, dest)
}
@@ -385,9 +305,9 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
def debugLevel = settings.debuginfo.indexOfChoice
- val emitSource = debugLevel >= 1
- val emitLines = debugLevel >= 2
- val emitVars = debugLevel >= 3
+ final val emitSource = debugLevel >= 1
+ final val emitLines = debugLevel >= 2
+ final val emitVars = debugLevel >= 3
/*
* Contains class-symbols that:
@@ -396,155 +316,135 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*
* In other words, the lifetime of `innerClassBufferASM` is associated to "the class being generated".
*/
- val innerClassBufferASM = mutable.Set.empty[BType]
+ final val innerClassBufferASM = mutable.Set.empty[ClassBType]
- /*
- * Tracks (if needed) the inner class given by `sym`.
- *
- * must-single-thread
+ /**
+ * The class internal name for a given class symbol. If the symbol describes a nested class, the
+ * ClassBType is added to the innerClassBufferASM.
*/
- final def internalName(sym: Symbol): String = asmClassType(sym).internalName
+ final def internalName(sym: Symbol): String = {
+ // For each java class, the scala compiler creates a class and a module (thus a module class).
+ // If the `sym` is a java module class, we use the java class instead. This ensures that we
+ // register the class (instead of the module class) in innerClassBufferASM.
+ // The two symbols have the same name, so the resulting internalName is the same.
+ val classSym = if (sym.isJavaDefined && sym.isModuleClass) sym.linkedClassOfClass else sym
+ getClassBTypeAndRegisterInnerClass(classSym).internalName
+ }
- /*
- * Tracks (if needed) the inner class given by `sym`.
+ private def assertClassNotArray(sym: Symbol): Unit = {
+ assert(sym.isClass, sym)
+ assert(sym != definitions.ArrayClass || isCompilingArray, sym)
+ }
+
+ private def assertClassNotArrayNotPrimitive(sym: Symbol): Unit = {
+ assertClassNotArray(sym)
+ assert(!primitiveTypeMap.contains(sym) || isCompilingPrimitive, sym)
+ }
+
+ /**
+ * The ClassBType for a class symbol. If the class is nested, the ClassBType is added to the
+ * innerClassBufferASM.
*
- * must-single-thread
+ * The class symbol scala.Nothing is mapped to the class scala.runtime.Nothing$. Similarly,
+ * scala.Null is mapped to scala.runtime.Null$. This is because there exist no class files
+ * for the Nothing / Null. If used for example as a parameter type, we use the runtime classes
+ * in the classfile method signature.
+ *
+ * Note that the referenced class symbol may be an implementation class. For example when
+ * compiling a mixed-in method that forwards to the static method in the implementation class,
+ * the class descriptor of the receiver (the implementation class) is obtained by creating the
+ * ClassBType.
*/
- final def asmClassType(sym: Symbol): ClassBType = {
- assert(
- hasInternalName(sym),
- {
- val msg0 = if (sym.isAbstractType) "An AbstractTypeSymbol (SI-7122) " else "A symbol ";
- msg0 + s"has reached the bytecode emitter, for which no JVM-level internal name can be found: ${sym.fullName}"
- }
- )
- val phantOpt = phantomTypeMap.get(sym)
- if (phantOpt.isDefined) {
- return phantOpt.get
- }
- val tracked = exemplar(sym)
- val tk = tracked.c
- if (tracked.isInnerClass) {
- innerClassBufferASM += tk
- }
+ final def getClassBTypeAndRegisterInnerClass(sym: Symbol): ClassBType = {
+ assertClassNotArrayNotPrimitive(sym)
- tk
+ if (sym == definitions.NothingClass) RT_NOTHING
+ else if (sym == definitions.NullClass) RT_NULL
+ else {
+ val r = classBTypeFromSymbol(sym)
+ if (r.isNestedClass) innerClassBufferASM += r
+ r
+ }
}
- /*
- * Returns the BType for the given type.
- * Tracks (if needed) the inner class given by `t`.
+ /**
+ * This method returns the BType for a type reference, for example a parameter type.
*
- * must-single-thread
+ * If the result is a ClassBType for a nested class, it is added to the innerClassBufferASM.
+ *
+ * If `t` references a class, toTypeKind ensures that the class is not an implementation class.
+ * See also comment on getClassBTypeAndRegisterInnerClass, which is invoked for implementation
+ * classes.
*/
final def toTypeKind(t: Type): BType = {
+ import definitions.ArrayClass
- /* Interfaces have to be handled delicately to avoid introducing spurious errors,
- * but if we treat them all as AnyRef we lose too much information.
+ /**
+ * Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int.
+ * The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType.
*/
- def newReference(sym0: Symbol): BType = {
- assert(!primitiveTypeMap.contains(sym0), "Use primitiveTypeMap instead.")
- assert(sym0 != definitions.ArrayClass, "Use arrayOf() instead.")
-
- if (sym0 == definitions.NullClass) return RT_NULL;
- if (sym0 == definitions.NothingClass) return RT_NOTHING;
-
- val sym = (
- if (!sym0.isPackageClass) sym0
- else sym0.info.member(nme.PACKAGE) match {
- case NoSymbol => abort(s"SI-5604: Cannot use package as value: ${sym0.fullName}")
- case s => abort(s"SI-5604: found package class where package object expected: $s")
- }
- )
-
- // Can't call .toInterface (at this phase) or we trip an assertion.
- // See PackratParser#grow for a method which fails with an apparent mismatch
- // between "object PackratParsers$class" and "trait PackratParsers"
- // TODO @lry do we have a test for that?
- if (sym.isImplClass) {
- // pos/spec-List.scala is the sole failure if we don't check for NoSymbol
- val traitSym = sym.owner.info.decl(tpnme.interfaceName(sym.name))
- if (traitSym != NoSymbol) {
- // this tracks the inner class in innerClassBufferASM, if needed.
- return asmClassType(traitSym)
- }
- }
-
- // TODO @lry: code duplication between here and method asmClassType.
-
- assert(hasInternalName(sym), s"Invoked for a symbol lacking JVM internal name: ${sym.fullName}")
- assert(!phantomTypeMap.contains(sym), "phantom types not supposed to reach here.")
-
- val tracked = exemplar(sym)
- val tk = tracked.c
- if (tracked.isInnerClass) {
- innerClassBufferASM += tk
- }
-
- tk
- }
-
- def primitiveOrRefType(sym: Symbol): BType = {
- assert(sym != definitions.ArrayClass, "Use primitiveOrArrayOrRefType() instead.")
-
- primitiveTypeMap.getOrElse(sym, newReference(sym))
+ def primitiveOrClassToBType(sym: Symbol): BType = {
+ assertClassNotArray(sym)
+ assert(!sym.isImplClass, sym)
+ primitiveTypeMap.getOrElse(sym, getClassBTypeAndRegisterInnerClass(sym))
}
- def primitiveOrRefType2(sym: Symbol): BType = {
- primitiveTypeMap.get(sym) match {
- case Some(pt) => pt
- case None =>
- sym match {
- case definitions.NullClass => RT_NULL
- case definitions.NothingClass => RT_NOTHING
- case _ if sym.isClass => newReference(sym)
- case _ =>
- assert(sym.isType, sym) // it must be compiling Array[a]
- ObjectReference
- }
- }
+ /**
+ * When compiling Array.scala, the type parameter T is not erased and shows up in method
+ * signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference.
+ */
+ def nonClassTypeRefToBType(sym: Symbol): ClassBType = {
+ assert(sym.isType && isCompilingArray, sym)
+ ObjectReference
}
- import definitions.ArrayClass
-
- // Call to .normalize fixes #3003 (follow type aliases). Otherwise, primitiveOrArrayOrRefType() would return ObjectReference.
- t.normalize match {
-
- case ThisType(sym) =>
- if (sym == ArrayClass) ObjectReference
- else phantomTypeMap.getOrElse(sym, exemplar(sym).c)
-
- case SingleType(_, sym) => primitiveOrRefType(sym)
-
- case _: ConstantType => toTypeKind(t.underlying)
-
- case TypeRef(_, sym, args) =>
- if (sym == ArrayClass) ArrayBType(toTypeKind(args.head))
- else primitiveOrRefType2(sym)
-
- case ClassInfoType(_, _, sym) =>
- assert(sym != ArrayClass, "ClassInfoType to ArrayClass!")
- primitiveOrRefType(sym)
-
- // TODO @lry check below comments / todo's
- // !!! Iulian says types which make no sense after erasure should not reach here, which includes the ExistentialType, AnnotatedType, RefinedType.
- case ExistentialType(_, t) => toTypeKind(t) // TODO shouldn't get here but the following does: akka-actor/src/main/scala/akka/util/WildcardTree.scala
- case AnnotatedType(_, w) => toTypeKind(w) // TODO test/files/jvm/annotations.scala causes an AnnotatedType to reach here.
- case RefinedType(parents, _) => parents.map(toTypeKind(_).asClassBType) reduceLeft jvmWiseLUB
-
- // For sure WildcardTypes shouldn't reach here either, but when debugging such situations this may come in handy.
- // case WildcardType => REFERENCE(ObjectClass)
- case norm => abort(
- s"Unknown type: $t, $norm [${t.getClass}, ${norm.getClass}] TypeRef? ${t.isInstanceOf[TypeRef]}"
- )
+ t.dealiasWiden match {
+ case TypeRef(_, ArrayClass, List(arg)) => ArrayBType(toTypeKind(arg)) // Array type such as Array[Int] (kept by erasure)
+ case TypeRef(_, sym, _) if !sym.isClass => nonClassTypeRefToBType(sym) // See comment on nonClassTypeRefToBType
+ case TypeRef(_, sym, _) => primitiveOrClassToBType(sym) // Common reference to a type such as scala.Int or java.lang.String
+ case ClassInfoType(_, _, sym) => primitiveOrClassToBType(sym) // We get here, for example, for genLoadModule, which invokes toTypeKind(moduleClassSymbol.info)
+
+ /* AnnotatedType should (probably) be eliminated by erasure. However we know it happens for
+ * meta-annotated annotations (@(ann @getter) val x = 0), so we don't emit a warning.
+ * The type in the AnnotationInfo is an AnnotatedTpe. Tested in jvm/annotations.scala.
+ */
+ case a @ AnnotatedType(_, t) =>
+ debuglog(s"typeKind of annotated type $a")
+ toTypeKind(t)
+
+ /* ExistentialType should (probably) be eliminated by erasure. We know they get here for
+ * classOf constants:
+ * class C[T]
+ * class T { final val k = classOf[C[_]] }
+ */
+ case e @ ExistentialType(_, t) =>
+ debuglog(s"typeKind of existential type $e")
+ toTypeKind(t)
+
+ /* The cases below should probably never occur. They are kept for now to avoid introducing
+ * new compiler crashes, but we added a warning. The compiler / library bootstrap and the
+ * test suite don't produce any warning.
+ */
+
+ case tp =>
+ currentUnit.warning(tp.typeSymbol.pos,
+ s"an unexpected type representation reached the compiler backend while compiling $currentUnit: $tp. " +
+ "If possible, please file a bug on issues.scala-lang.org.")
+
+ tp match {
+ case ThisType(ArrayClass) => ObjectReference // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test
+ case ThisType(sym) => getClassBTypeAndRegisterInnerClass(sym)
+ case SingleType(_, sym) => primitiveOrClassToBType(sym)
+ case ConstantType(_) => toTypeKind(t.underlying)
+ case RefinedType(parents, _) => parents.map(toTypeKind(_).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b))
+ }
}
-
- } // end of method toTypeKind()
+ }
/*
* must-single-thread
*/
- def asmMethodType(msym: Symbol): MethodBType = {
+ final def asmMethodType(msym: Symbol): MethodBType = {
assert(msym.isMethod, s"not a method-symbol: $msym")
val resT: BType =
if (msym.isClassConstructor || msym.isConstructor) UNIT
@@ -552,45 +452,17 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
MethodBType(msym.tpe.paramTypes map toTypeKind, resT)
}
- /*
- * Returns all direct member inner classes of `csym`,
- * thus making sure they get entries in the InnerClasses JVM attribute
- * even if otherwise not mentioned in the class being built.
- *
- * must-single-thread
- */
- final def trackMemberClasses(csym: Symbol, lateClosuresBTs: List[BType]): List[BType] = {
- val lateInnerClasses = exitingErasure {
- for (sym <- List(csym, csym.linkedClassOfClass); memberc <- sym.info.decls.map(innerClassSymbolFor) if memberc.isClass)
- yield memberc
- }
- // as a precaution, do the following outside the above `exitingErasure` otherwise funny internal names might be computed.
- val result = for(memberc <- lateInnerClasses) yield {
- val tracked = exemplar(memberc)
- val memberCTK = tracked.c
- assert(tracked.isInnerClass, s"saveInnerClassesFor() says this was no inner-class after all: ${memberc.fullName}")
-
- memberCTK
- }
-
- exemplar(csym).directMemberClasses = result
-
- result
- }
-
- /*
- * Tracks (if needed) the inner class given by `t`.
- *
- * must-single-thread
+ /**
+ * The jvm descriptor of a type. If `t` references a nested class, its ClassBType is added to
+ * the innerClassBufferASM.
*/
final def descriptor(t: Type): String = { toTypeKind(t).descriptor }
- /*
- * Tracks (if needed) the inner class given by `sym`.
- *
- * must-single-thread
+ /**
+ * The jvm descriptor for a symbol. If `sym` represents a nested class, its ClassBType is added
+ * to the innerClassBufferASM.
*/
- final def descriptor(sym: Symbol): String = { asmClassType(sym).descriptor }
+ final def descriptor(sym: Symbol): String = { getClassBTypeAndRegisterInnerClass(sym).descriptor }
} // end of trait BCInnerClassGen
@@ -795,7 +667,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*/
// TODO: evaluate the other flags we might be dropping on the floor here.
// TODO: ACC_SYNTHETIC ?
- val flags = PublicStatic | (
+ val flags = GenBCode.PublicStatic | (
if (m.isVarargsMethod) asm.Opcodes.ACC_VARARGS else 0
)
@@ -901,72 +773,18 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
def addSerialVUID(id: Long, jclass: asm.ClassVisitor) {
// add static serialVersionUID field if `clasz` annotated with `@SerialVersionUID(uid: Long)`
jclass.visitField(
- PublicStaticFinal,
+ GenBCode.PublicStaticFinal,
"serialVersionUID",
"J",
null, // no java-generic-signature
new java.lang.Long(id)
).visitEnd()
}
-
- /*
- * @param owner internal name of the enclosing class of the class.
- *
- * @param name the name of the method that contains the class.
-
- * @param methodType the method that contains the class.
- */
- case class EnclMethodEntry(owner: String, name: String, methodType: BType)
-
- /*
- * @return null if the current class is not internal to a method
- *
- * Quoting from JVMS 4.7.7 The EnclosingMethod Attribute
- * A class must have an EnclosingMethod attribute if and only if it is a local class or an anonymous class.
- * A class may have no more than one EnclosingMethod attribute.
- *
- * must-single-thread
- */
- def getEnclosingMethodAttribute(clazz: Symbol): EnclMethodEntry = { // JVMS 4.7.7
-
- def newEEE(eClass: Symbol, m: Symbol) = {
- EnclMethodEntry(
- internalName(eClass),
- m.javaSimpleName.toString,
- asmMethodType(m)
- )
- }
-
- var res: EnclMethodEntry = null
- val sym = clazz.originalEnclosingMethod
- if (sym.isMethod) {
- debuglog(s"enclosing method for $clazz is $sym (in ${sym.enclClass})")
- res = newEEE(sym.enclClass, sym)
- } else if (clazz.isAnonymousClass) {
- val enclClass = clazz.rawowner
- assert(enclClass.isClass, enclClass)
- val sym = enclClass.primaryConstructor
- if (sym == NoSymbol) {
- log(s"Ran out of room looking for an enclosing method for $clazz: no constructor here: $enclClass.")
- } else {
- debuglog(s"enclosing method for $clazz is $sym (in $enclClass)")
- res = newEEE(enclClass, sym)
- }
- }
-
- res
- }
-
} // end of trait BCClassGen
- /* basic functionality for class file building of plain, mirror, and beaninfo classes. */
- abstract class JBuilder extends BCInnerClassGen {
-
- } // end of class JBuilder
-
/* functionality for building plain and mirror classes */
abstract class JCommonBuilder
- extends JBuilder
+ extends BCInnerClassGen
with BCAnnotGen
with BCForwardersGen
with BCPickles { }
@@ -985,11 +803,12 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*
* must-single-thread
*/
- def genMirrorClass(modsym: Symbol, cunit: CompilationUnit): asm.tree.ClassNode = {
- assert(modsym.companionClass == NoSymbol, modsym)
+ def genMirrorClass(moduleClass: Symbol, cunit: CompilationUnit): asm.tree.ClassNode = {
+ assert(moduleClass.isModuleClass)
+ assert(moduleClass.companionClass == NoSymbol, moduleClass)
innerClassBufferASM.clear()
this.cunit = cunit
- val moduleName = internalName(modsym) // + "$"
+ val moduleName = internalName(moduleClass) // + "$"
val mirrorName = moduleName.substring(0, moduleName.length() - 1)
val flags = (asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL)
@@ -999,7 +818,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
flags,
mirrorName,
null /* no java-generic-signature */,
- JAVA_LANG_OBJECT.internalName,
+ ObjectReference.internalName,
EMPTY_STRING_ARRAY
)
@@ -1008,18 +827,18 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
null /* SourceDebugExtension */)
}
- val ssa = getAnnotPickle(mirrorName, modsym.companionSymbol)
+ val ssa = getAnnotPickle(mirrorName, moduleClass.companionSymbol)
mirrorClass.visitAttribute(if (ssa.isDefined) pickleMarkerLocal else pickleMarkerForeign)
- emitAnnotations(mirrorClass, modsym.annotations ++ ssa)
+ emitAnnotations(mirrorClass, moduleClass.annotations ++ ssa)
- addForwarders(isRemote(modsym), mirrorClass, mirrorName, modsym)
+ addForwarders(isRemote(moduleClass), mirrorClass, mirrorName, moduleClass)
- innerClassBufferASM ++= trackMemberClasses(modsym, Nil /* TODO what about Late-Closure-Classes */ )
+ innerClassBufferASM ++= classBTypeFromSymbol(moduleClass).info.memberClasses
addInnerClassesASM(mirrorClass, innerClassBufferASM.toList)
mirrorClass.visitEnd()
- ("" + modsym.name) // this side-effect is necessary, really.
+ ("" + moduleClass.name) // this side-effect is necessary, really.
mirrorClass
}
@@ -1027,7 +846,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
} // end of class JMirrorBuilder
/* builder of bean info classes */
- class JBeanInfoBuilder extends JBuilder {
+ class JBeanInfoBuilder extends BCInnerClassGen {
/*
* Generate a bean info class that describes the given class.
@@ -1042,10 +861,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
innerClassBufferASM.clear()
- val flags = mkFlags(
- javaFlags(cls),
- if (isDeprecated(cls)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
- )
+ val flags = javaFlags(cls)
val beanInfoName = (internalName(cls) + "BeanInfo")
val beanInfoClass = new asm.tree.ClassNode
@@ -1091,9 +907,9 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
EMPTY_STRING_ARRAY // no throwable exceptions
)
- val stringArrayJType: BType = ArrayBType(JAVA_LANG_STRING)
+ val stringArrayJType: BType = ArrayBType(StringReference)
val conJType: BType = MethodBType(
- exemplar(definitions.ClassClass).c :: stringArrayJType :: stringArrayJType :: Nil,
+ classBTypeFromSymbol(definitions.ClassClass) :: stringArrayJType :: stringArrayJType :: Nil,
UNIT
)
@@ -1104,7 +920,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
constructor.visitLdcInsn(new java.lang.Integer(fi))
if (f == null) { constructor.visitInsn(asm.Opcodes.ACONST_NULL) }
else { constructor.visitLdcInsn(f) }
- constructor.visitInsn(JAVA_LANG_STRING.typedOpcode(asm.Opcodes.IASTORE))
+ constructor.visitInsn(StringReference.typedOpcode(asm.Opcodes.IASTORE))
fi += 1
}
}
@@ -1113,16 +929,16 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
constructor.visitVarInsn(asm.Opcodes.ALOAD, 0)
// push the class
- constructor.visitLdcInsn(exemplar(cls).c.toASMType)
+ constructor.visitLdcInsn(classBTypeFromSymbol(cls).toASMType)
// push the string array of field information
constructor.visitLdcInsn(new java.lang.Integer(fieldList.length))
- constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.internalName)
+ constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, StringReference.internalName)
push(fieldList)
// push the string array of method information
constructor.visitLdcInsn(new java.lang.Integer(methodList.length))
- constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.internalName)
+ constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, StringReference.internalName)
push(methodList)
// invoke the superclass constructor, which will do the
@@ -1133,7 +949,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments
constructor.visitEnd()
- innerClassBufferASM ++= trackMemberClasses(cls, Nil /* TODO what about Late-Closure-Classes */ )
+ innerClassBufferASM ++= classBTypeFromSymbol(cls).info.memberClasses
addInnerClassesASM(beanInfoClass, innerClassBufferASM.toList)
beanInfoClass.visitEnd()
@@ -1165,11 +981,11 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
*/
def legacyAddCreatorCode(clinit: asm.MethodVisitor, cnode: asm.tree.ClassNode, thisName: String) {
// this tracks the inner class in innerClassBufferASM, if needed.
- val androidCreatorType = asmClassType(AndroidCreatorClass)
+ val androidCreatorType = getClassBTypeAndRegisterInnerClass(AndroidCreatorClass)
val tdesc_creator = androidCreatorType.descriptor
cnode.visitField(
- PublicStaticFinal,
+ GenBCode.PublicStaticFinal,
"CREATOR",
tdesc_creator,
null, // no java-generic-signature
@@ -1206,5 +1022,4 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters {
}
} // end of trait JAndroidBuilder
-
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index 2343d378db..d58368b19d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
@@ -19,16 +19,11 @@ import scala.collection.mutable
*
*/
abstract class BCodeIdiomatic extends SubComponent {
- protected val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global)
-
- val bTypes = new BTypes[global.type](global) {
- def chrs = global.chrs
- override type BTypeName = global.TypeName
- override def createNewName(s: String) = global.newTypeName(s)
- }
+ val bTypes = new BTypesFromSymbols[global.type](global)
import global._
import bTypes._
+ import coreBTypes._
val classfileVersion: Int = settings.target.value match {
case "jvm-1.5" => asm.Opcodes.V1_5
@@ -40,9 +35,7 @@ abstract class BCodeIdiomatic extends SubComponent {
val majorVersion: Int = (classfileVersion & 0xFF)
val emitStackMapFrame = (majorVersion >= 50)
- def mkFlags(args: Int*) = args.foldLeft(0)(_ | _)
-
- val extraProc: Int = mkFlags(
+ val extraProc: Int = GenBCode.mkFlags(
asm.ClassWriter.COMPUTE_MAXS,
if (emitStackMapFrame) asm.ClassWriter.COMPUTE_FRAMES else 0
)
@@ -52,15 +45,6 @@ abstract class BCodeIdiomatic extends SubComponent {
val CLASS_CONSTRUCTOR_NAME = "<clinit>"
val INSTANCE_CONSTRUCTOR_NAME = "<init>"
- val ObjectReference = ClassBType("java/lang/Object")
- val AnyRefReference = ObjectReference
- val objArrayReference = ArrayBType(ObjectReference)
-
- val JAVA_LANG_OBJECT = ObjectReference
- val JAVA_LANG_STRING = ClassBType("java/lang/String")
-
- var StringBuilderReference: BType = null
-
val EMPTY_STRING_ARRAY = Array.empty[String]
val EMPTY_INT_ARRAY = Array.empty[Int]
val EMPTY_LABEL_ARRAY = Array.empty[asm.Label]
@@ -239,7 +223,7 @@ abstract class BCodeIdiomatic extends SubComponent {
final def genStringConcat(el: BType) {
val jtype =
- if (el.isArray || el.isClass) JAVA_LANG_OBJECT
+ if (el.isArray || el.isClass) ObjectReference
else el
val bt = MethodBType(List(jtype), StringBuilderReference)
@@ -266,7 +250,7 @@ abstract class BCodeIdiomatic extends SubComponent {
assert(
from.isNonVoidPrimitiveType && to.isNonVoidPrimitiveType,
- s"Cannot emit primitive conversion from $from to $to"
+ s"Cannot emit primitive conversion from $from to $to - ${global.currentUnit}"
)
def pickOne(opcs: Array[Int]) { // TODO index on to.sort
@@ -537,7 +521,7 @@ abstract class BCodeIdiomatic extends SubComponent {
final def emitTypeBased(opcs: Array[Int], tk: BType) {
assert(tk != UNIT, tk)
val opc = {
- if (tk.isRef) { opcs(0) }
+ if (tk.isRef) { opcs(0) }
else if (tk.isIntSizedType) {
(tk: @unchecked) match {
case BOOL | BYTE => opcs(1)
@@ -648,7 +632,7 @@ abstract class BCodeIdiomatic extends SubComponent {
*/
final def coercionTo(code: Int): BType = {
import scalaPrimitives._
- (code: @scala.annotation.switch) match {
+ (code: @switch) match {
case B2B | C2B | S2B | I2B | L2B | F2B | D2B => BYTE
case B2C | C2C | S2C | I2C | L2C | F2C | D2C => CHAR
case B2S | C2S | S2S | I2S | L2S | F2S | D2S => SHORT
@@ -659,21 +643,6 @@ abstract class BCodeIdiomatic extends SubComponent {
}
}
- final val typeOfArrayOp: Map[Int, BType] = {
- import scalaPrimitives._
- Map(
- (List(ZARRAY_LENGTH, ZARRAY_GET, ZARRAY_SET) map (_ -> BOOL)) ++
- (List(BARRAY_LENGTH, BARRAY_GET, BARRAY_SET) map (_ -> BYTE)) ++
- (List(SARRAY_LENGTH, SARRAY_GET, SARRAY_SET) map (_ -> SHORT)) ++
- (List(CARRAY_LENGTH, CARRAY_GET, CARRAY_SET) map (_ -> CHAR)) ++
- (List(IARRAY_LENGTH, IARRAY_GET, IARRAY_SET) map (_ -> INT)) ++
- (List(LARRAY_LENGTH, LARRAY_GET, LARRAY_SET) map (_ -> LONG)) ++
- (List(FARRAY_LENGTH, FARRAY_GET, FARRAY_SET) map (_ -> FLOAT)) ++
- (List(DARRAY_LENGTH, DARRAY_GET, DARRAY_SET) map (_ -> DOUBLE)) ++
- (List(OARRAY_LENGTH, OARRAY_GET, OARRAY_SET) map (_ -> ObjectReference)) : _*
- )
- }
-
/*
* Collects (in `result`) all LabelDef nodes enclosed (directly or not) by each node it visits.
*
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index 0d67a07e0f..03bc32061b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -26,6 +26,8 @@ import java.io.PrintWriter
abstract class BCodeSkelBuilder extends BCodeHelpers {
import global._
import bTypes._
+ import coreBTypes._
+ import bCodeAsmCommon._
/*
* There's a dedicated PlainClassBuilder for each CompilationUnit,
@@ -116,7 +118,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
addClassFields()
- innerClassBufferASM ++= trackMemberClasses(claszSymbol, Nil)
+ innerClassBufferASM ++= classBTypeFromSymbol(claszSymbol).info.memberClasses
gen(cd.impl)
addInnerClassesASM(cnode, innerClassBufferASM.toList)
@@ -134,40 +136,28 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
private def initJClass(jclass: asm.ClassVisitor) {
val ps = claszSymbol.info.parents
- val superClass: String = if (ps.isEmpty) JAVA_LANG_OBJECT.internalName else internalName(ps.head.typeSymbol);
- val ifaces: Array[String] = {
- val arrIfacesTr: Array[Tracked] = exemplar(claszSymbol).ifaces
- val arrIfaces = new Array[String](arrIfacesTr.length)
- var i = 0
- while (i < arrIfacesTr.length) {
- val ifaceTr = arrIfacesTr(i)
- val bt = ifaceTr.c
- if (ifaceTr.isInnerClass) { innerClassBufferASM += bt }
- arrIfaces(i) = bt.internalName
- i += 1
- }
- arrIfaces
+ val superClass: String = if (ps.isEmpty) ObjectReference.internalName else internalName(ps.head.typeSymbol)
+ val interfaceNames = classBTypeFromSymbol(claszSymbol).info.interfaces map {
+ case classBType =>
+ if (classBType.isNestedClass) { innerClassBufferASM += classBType }
+ classBType.internalName
}
- // `internalName()` tracks inner classes.
- val flags = mkFlags(
- javaFlags(claszSymbol),
- if (isDeprecated(claszSymbol)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
- )
+ val flags = javaFlags(claszSymbol)
val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner)
cnode.visit(classfileVersion, flags,
thisName, thisSignature,
- superClass, ifaces)
+ superClass, interfaceNames.toArray)
if (emitSource) {
cnode.visitSource(cunit.source.toString, null /* SourceDebugExtension */)
}
- val enclM = getEnclosingMethodAttribute(claszSymbol)
- if (enclM != null) {
- val EnclMethodEntry(className, methodName, methodType) = enclM
- cnode.visitOuterClass(className, methodName, methodType.descriptor)
+ enclosingMethodAttribute(claszSymbol, internalName, asmMethodType(_).descriptor) match {
+ case Some(EnclosingMethodEntry(className, methodName, methodDescriptor)) =>
+ cnode.visitOuterClass(className, methodName, methodDescriptor)
+ case _ => ()
}
val ssa = getAnnotPickle(thisName, claszSymbol)
@@ -207,7 +197,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
*/
private def addModuleInstanceField() {
val fv =
- cnode.visitField(PublicStaticFinal, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
+ cnode.visitField(GenBCode.PublicStaticFinal, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
strMODULE_INSTANCE_FIELD,
"L" + thisName + ";",
null, // no java-generic-signature
@@ -223,7 +213,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
private def fabricateStaticInit() {
val clinit: asm.MethodVisitor = cnode.visitMethod(
- PublicStatic, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
+ GenBCode.PublicStatic, // TODO confirm whether we really don't want ACC_SYNTHETIC nor ACC_DEPRECATED
CLASS_CONSTRUCTOR_NAME,
"()V",
null, // no java-generic-signature
@@ -254,10 +244,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
*/
for (f <- fieldSymbols(claszSymbol)) {
val javagensig = getGenericSignature(f, claszSymbol)
- val flags = mkFlags(
- javaFieldFlags(f),
- if (isDeprecated(f)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
- )
+ val flags = javaFieldFlags(f)
val jfield = new asm.tree.FieldNode(
flags,
@@ -359,6 +346,13 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
/*
* Bookkeeping for method-local vars and method-params.
+ *
+ * TODO: use fewer slots. local variable slots are never re-used in separate blocks.
+ * In the following example, x and y could use the same slot.
+ * def foo() = {
+ * { val x = 1 }
+ * { val y = "a" }
+ * }
*/
object locals {
@@ -576,12 +570,11 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
val isNative = methSymbol.hasAnnotation(definitions.NativeAttr)
val isAbstractMethod = (methSymbol.isDeferred || methSymbol.owner.isInterface)
- val flags = mkFlags(
+ val flags = GenBCode.mkFlags(
javaFlags(methSymbol),
if (claszSymbol.isInterface) asm.Opcodes.ACC_ABSTRACT else 0,
if (methSymbol.isStrictFP) asm.Opcodes.ACC_STRICT else 0,
- if (isNative) asm.Opcodes.ACC_NATIVE else 0, // native methods of objects are generated in mirror classes
- if (isDeprecated(methSymbol)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
+ if (isNative) asm.Opcodes.ACC_NATIVE else 0 // native methods of objects are generated in mirror classes
)
// TODO needed? for(ann <- m.symbol.annotations) { ann.symbol.initialize }
@@ -695,7 +688,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
// android creator code
if (isCZParcelable) {
// add a static field ("CREATOR") to this class to cache android.os.Parcelable$Creator
- val andrFieldDescr = asmClassType(AndroidCreatorClass).descriptor
+ val andrFieldDescr = getClassBTypeAndRegisterInnerClass(AndroidCreatorClass).descriptor
cnode.visitField(
asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL,
"CREATOR",
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
index c271e7b129..7c95b7fc3b 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala
@@ -21,7 +21,7 @@ import scala.tools.asm
abstract class BCodeSyncAndTry extends BCodeBodyBuilder {
import global._
import bTypes._
-
+ import coreBTypes._
/*
* Functionality to lower `synchronized` and `try` expressions.
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
deleted file mode 100644
index b373f8d74d..0000000000
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala
+++ /dev/null
@@ -1,930 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2012 LAMP/EPFL
- * @author Martin Odersky
- */
-
-package scala
-package tools.nsc
-package backend.jvm
-
-import scala.tools.asm
-import scala.collection.{ immutable, mutable }
-
-/*
- * Utilities to mediate between types as represented in Scala ASTs and ASM trees.
- *
- * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded
- * @version 1.0
- *
- */
-abstract class BCodeTypes extends BCodeIdiomatic {
- import global._
- import bTypes._
-
- // Used only for assertions. When compiling the Scala library, some assertions don't hold
- // (e.g., scala.Boolean has null superClass although it's not an interface)
- private val isCompilingStdLib = !(settings.sourcepath.isDefault)
-
- // special names
- var StringReference : ClassBType = null
- var ThrowableReference : ClassBType = null
- var jlCloneableReference : ClassBType = null // java/lang/Cloneable
- var jlNPEReference : ClassBType = null // java/lang/NullPointerException
- var jioSerializableReference : ClassBType = null // java/io/Serializable
- var scalaSerializableReference : ClassBType = null // scala/Serializable
- var classCastExceptionReference : ClassBType = null // java/lang/ClassCastException
-
- /* A map from scala primitive type-symbols to BTypes */
- var primitiveTypeMap: Map[Symbol, BType] = null
- /* A map from scala type-symbols for Nothing and Null to (runtime version) BTypes */
- var phantomTypeMap: Map[Symbol, ClassBType] = null
- /* Maps the method symbol for a box method to the boxed type of the result.
- * For example, the method symbol for `Byte.box()`) is mapped to the BType `Ljava/lang/Integer;`. */
- var boxResultType: Map[Symbol, BType] = null
- /* Maps the method symbol for an unbox method to the primitive type of the result.
- * For example, the method symbol for `Byte.unbox()`) is mapped to the BType BYTE. */
- var unboxResultType: Map[Symbol, BType] = null
-
- var hashMethodSym: Symbol = null // scala.runtime.ScalaRunTime.hash
-
- var AndroidParcelableInterface: Symbol = null
- var AndroidCreatorClass : Symbol = null // this is an inner class, use asmType() to get hold of its BType while tracking in innerClassBufferASM
-
- var BeanInfoAttr: Symbol = null
-
- /* The Object => String overload. */
- var String_valueOf: Symbol = null
-
- var ArrayInterfaces: Set[Tracked] = null
-
- // scala.FunctionX and scala.runtim.AbstractFunctionX
- val FunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1)
- val AbstractFunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1)
- val abstractFunctionArityMap = mutable.Map.empty[BType, Int]
-
- var PartialFunctionReference: ClassBType = null // scala.PartialFunction
- var AbstractPartialFunctionReference: ClassBType = null // scala.runtime.AbstractPartialFunction
-
- var BoxesRunTime: ClassBType = null
-
- /*
- * must-single-thread
- */
- def initBCodeTypes() {
- import definitions._
-
- primitiveTypeMap =
- Map(
- UnitClass -> UNIT,
- BooleanClass -> BOOL,
- CharClass -> CHAR,
- ByteClass -> BYTE,
- ShortClass -> SHORT,
- IntClass -> INT,
- LongClass -> LONG,
- FloatClass -> FLOAT,
- DoubleClass -> DOUBLE
- )
-
- phantomTypeMap =
- Map(
- NothingClass -> RT_NOTHING,
- NullClass -> RT_NULL,
- NothingClass -> RT_NOTHING, // we map on purpose to RT_NOTHING, getting rid of the distinction compile-time vs. runtime for NullClass.
- NullClass -> RT_NULL // ditto.
- )
-
- boxResultType =
- for((csym, msym) <- currentRun.runDefinitions.boxMethod)
- yield (msym -> classLiteral(primitiveTypeMap(csym)))
-
- unboxResultType =
- for((csym, msym) <- currentRun.runDefinitions.unboxMethod)
- yield (msym -> primitiveTypeMap(csym))
-
- // boxed classes are looked up in the `exemplars` map by jvmWiseLUB().
- // Other than that, they aren't needed there (e.g., `isSubtypeOf()` special-cases boxed classes, similarly for others).
- val boxedClasses = List(BoxedBooleanClass, BoxedCharacterClass, BoxedByteClass, BoxedShortClass, BoxedIntClass, BoxedLongClass, BoxedFloatClass, BoxedDoubleClass)
- for(csym <- boxedClasses) {
- val key = ClassBType(csym.javaBinaryName.toTypeName)
- val tr = buildExemplar(key, csym)
- symExemplars.put(csym, tr)
- exemplars.put(tr.c, tr)
- }
-
- // reversePrimitiveMap = (primitiveTypeMap map { case (s, pt) => (s.tpe, pt) } map (_.swap)).toMap
-
- hashMethodSym = getMember(ScalaRunTimeModule, nme.hash_)
-
- // TODO avoiding going through through missingHook for every line in the REPL: https://github.com/scala/scala/commit/8d962ed4ddd310cc784121c426a2e3f56a112540
- AndroidParcelableInterface = rootMirror.getClassIfDefined("android.os.Parcelable")
- AndroidCreatorClass = rootMirror.getClassIfDefined("android.os.Parcelable$Creator")
-
- // the following couldn't be an eager vals in Phase constructors:
- // that might cause cycles before Global has finished initialization.
- BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo")
-
- String_valueOf = {
- getMember(StringModule, nme.valueOf) filter (sym =>
- sym.info.paramTypes match {
- case List(pt) => pt.typeSymbol == ObjectClass
- case _ => false
- }
- )
- }
-
- exemplar(JavaCloneableClass)
- exemplar(JavaSerializableClass)
- exemplar(SerializableClass)
-
- StringReference = exemplar(StringClass).c
- StringBuilderReference = exemplar(StringBuilderClass).c
- ThrowableReference = exemplar(ThrowableClass).c
- jlCloneableReference = exemplar(JavaCloneableClass).c
- jlNPEReference = exemplar(NullPointerExceptionClass).c
- jioSerializableReference = exemplar(JavaSerializableClass).c
- scalaSerializableReference = exemplar(SerializableClass).c
- classCastExceptionReference = exemplar(ClassCastExceptionClass).c
-
- PartialFunctionReference = exemplar(PartialFunctionClass).c
- for(idx <- 0 to definitions.MaxFunctionArity) {
- FunctionReference(idx) = exemplar(FunctionClass(idx))
- AbstractFunctionReference(idx) = exemplar(AbstractFunctionClass(idx))
- abstractFunctionArityMap += (AbstractFunctionReference(idx).c -> idx)
- AbstractPartialFunctionReference = exemplar(AbstractPartialFunctionClass).c
- }
-
- BoxesRunTime = ClassBType("scala/runtime/BoxesRunTime")
- }
-
- /*
- * must-single-thread
- */
- def clearBCodeTypes() {
- symExemplars.clear()
- exemplars.clear()
- }
-
- val PublicStatic = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC
- val PublicStaticFinal = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL
-
- val strMODULE_INSTANCE_FIELD = nme.MODULE_INSTANCE_FIELD.toString
-
- // ------------------------------------------------
- // accessory maps tracking the isInterface, innerClasses, superClass, and supportedInterfaces relations,
- // allowing answering `conforms()` without resorting to typer.
- // ------------------------------------------------
-
- /**
- * Type information for classBTypes.
- *
- * TODO rename Tracked
- */
- val exemplars = new java.util.concurrent.ConcurrentHashMap[ClassBType, Tracked]
-
- /**
- * Maps class symbols to their corresponding `Tracked` instance.
- *
- * This map is only used during the first backend phase (Worker1) where ClassDef trees are
- * transformed into ClassNode asm trees. In this phase, ClassBTypes and their Tracked are created
- * and added to the `exemplars` map. The `symExemplars` map is only used to know if a symbol has
- * already been visited.
- *
- * TODO move this map to the builder class. it's only used during building. can be gc'd with the builder.
- */
- val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked]
-
- /**
- * A `Tracked` instance stores information about a BType. This allows ansering type questions
- * without resolving to the compiler, in a thread-safe manner, in particular isSubtypeOf.
- *
- * @param c the BType described by this `Tracked`
- * @param flags the java flags for the type, computed by BCodeTypes#javaFlags
- * @param sc the bytecode-level superclass if any, null otherwise
- * @param ifaces the interfaces explicitly declared. Not included are those transitively
- * supported, but the utility method `allLeafIfaces()` can be used for that.
- * @param innersChain the containing classes for a non-package-level class `c`, null otherwise.
- *
- * Note: the optimizer may inline anonymous closures, thus eliding those inner classes (no
- * physical class file is emitted for elided classes). Before committing `innersChain` to
- * bytecode, cross-check with the list of elided classes (SI-6546).
- *
- * All methods of this class can-multi-thread
- *
- * TODO @lry c: ClassBType. rename to ClassBTypeInfo
- */
- case class Tracked(c: ClassBType, flags: Int, sc: Tracked, ifaces: Array[Tracked], innersChain: Array[InnerClassEntry]) {
-
- // not a case-field because we initialize it only for JVM classes we emit.
- // TODO @lry make it an Option[List[BType]]
- // TODO: this is currently not used. a commit in the optimizer branch uses this field to
- // re-compute inner classes (ee4c185). leaving it in for now.
- private var _directMemberClasses: List[BType] = null
-
- def directMemberClasses: List[BType] = {
- assert(_directMemberClasses != null, s"getter directMemberClasses() invoked too early for $c")
- _directMemberClasses
- }
-
- def directMemberClasses_=(bs: List[BType]) {
- if (_directMemberClasses != null) {
- // TODO we enter here when both mirror class and plain class are emitted for the same ModuleClassSymbol.
- assert(_directMemberClasses.sameElements(bs))
- }
- _directMemberClasses = bs
- }
-
- /* `isCompilingStdLib` saves the day when compiling:
- * (1) scala.Nothing (the test `c.isNonSpecial` fails for it)
- * (2) scala.Boolean (it has null superClass and is not an interface)
- */
- assert(c.isNonSpecial || isCompilingStdLib /*(1)*/, s"non well-formed plain-type: $this")
- assert(
- if (sc == null) { (c == ObjectReference) || isInterface || isCompilingStdLib /*(2)*/ }
- else { (c != ObjectReference) && !sc.isInterface }
- , "non well-formed plain-type: " + this
- )
- assert(ifaces.forall(i => i.c.isNonSpecial && i.isInterface), s"non well-formed plain-type: $this")
-
- import asm.Opcodes._
- def hasFlags(mask: Int) = (flags & mask) != 0
- def isInterface = hasFlags(ACC_INTERFACE)
- def isFinal = hasFlags(ACC_FINAL)
- def isInnerClass = { innersChain != null }
- def isLambda = {
- // ie isLCC || isTraditionalClosureClass
- isFinal && (c.simpleName.contains(tpnme.ANON_FUN_NAME.toString)) && isFunctionType(c)
- }
-
- /* can-multi-thread */
- def superClasses: List[Tracked] = {
- if (sc == null) Nil else sc :: sc.superClasses
- }
-
- /* can-multi-thread */
- def isSubtypeOf(other: BType): Boolean = {
- assert(other.isNonSpecial, "so called special cases have to be handled in BCodeTypes.conforms()")
-
- if (c == other) return true;
-
- val otherIsIface = exemplars.get(other).isInterface
-
- if (this.isInterface) {
- if (other == ObjectReference) return true;
- if (!otherIsIface) return false;
- }
- else {
- if (sc != null && sc.isSubtypeOf(other)) return true;
- if (!otherIsIface) return false;
- }
-
- var idx = 0
- while (idx < ifaces.length) {
- if (ifaces(idx).isSubtypeOf(other)) return true;
- idx += 1
- }
-
- false
- }
-
- /*
- * The `ifaces` field lists only those interfaces declared by `c`
- * From the set of all supported interfaces, this method discards those which are supertypes of others in the set.
- */
- def allLeafIfaces: Set[Tracked] = {
- if (sc == null) { ifaces.toSet }
- else { minimizeInterfaces(ifaces.toSet ++ sc.allLeafIfaces) }
- }
-
- /*
- * This type may not support in its entirety the interface given by the argument, however it may support some of its super-interfaces.
- * We visualize each such supported subset of the argument's functionality as a "branch". This method returns all such branches.
- *
- * In other words, let Ri be a branch supported by `ib`,
- * this method returns all Ri such that this <:< Ri, where each Ri is maximally deep.
- */
- def supportedBranches(ib: Tracked): Set[Tracked] = {
- assert(ib.isInterface, s"Non-interface argument: $ib")
-
- val result: Set[Tracked] =
- if (this.isSubtypeOf(ib.c)) { Set(ib) }
- else { ib.ifaces.toSet[Tracked].flatMap( bi => supportedBranches(bi) ) }
-
- checkAllInterfaces(result)
-
- result
- }
-
- override def toString = { c.toString }
-
- }
-
- /* must-single-thread */
- final def isDeprecated(sym: Symbol): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr) }
-
- /* must-single-thread */
- final def hasInternalName(sym: Symbol) = sym.isClass || sym.isModuleNotMethod
-
- /* must-single-thread */
- def getSuperInterfaces(csym: Symbol): List[Symbol] = {
-
- // Additional interface parents based on annotations and other cues
- def newParentForAttr(ann: AnnotationInfo): Symbol = ann.symbol match {
- case definitions.RemoteAttr => definitions.RemoteInterfaceClass
- case _ => NoSymbol
- }
-
- /* Drop redundant interfaces (which are implemented by some other parent) from the immediate parents.
- * In other words, no two interfaces in the result are related by subtyping.
- * This method works on Symbols, a similar one (not duplicate) works on Tracked instances.
- */
- def minimizeInterfaces(lstIfaces: List[Symbol]): List[Symbol] = {
- var rest = lstIfaces
- var leaves = List.empty[Symbol]
- while (!rest.isEmpty) {
- val candidate = rest.head
- val nonLeaf = leaves exists { lsym => lsym isSubClass candidate }
- if (!nonLeaf) {
- leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym })
- }
- rest = rest.tail
- }
-
- leaves
- }
-
- val superInterfaces0: List[Symbol] = csym.mixinClasses
- val superInterfaces = existingSymbols(superInterfaces0 ++ csym.annotations.map(newParentForAttr)).distinct
-
- assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}")
- assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}")
-
- minimizeInterfaces(superInterfaces)
- }
-
- /*
- * Records the superClass and supportedInterfaces relations,
- * so that afterwards queries can be answered without resorting to typer.
- * This method does not add to `innerClassBufferASM`, use `internalName()` or `asmType()` or `toTypeKind()` for that.
- * On the other hand, this method does record the inner-class status of the argument, via `buildExemplar()`.
- *
- * must-single-thread
- */
- final def exemplar(csym0: Symbol): Tracked = {
- assert(csym0 != NoSymbol, "NoSymbol can't be tracked")
-
- val csym = {
- if (csym0.isJavaDefined && csym0.isModuleClass) csym0.linkedClassOfClass
- else if (csym0.isModule) csym0.moduleClass
- else csym0 // we track only module-classes and plain-classes
- }
-
- assert(!primitiveTypeMap.contains(csym) || isCompilingStdLib, s"primitive types not tracked here: ${csym.fullName}")
- assert(!phantomTypeMap.contains(csym), s"phantom types not tracked here: ${csym.fullName}")
-
- val opt = symExemplars.get(csym)
- if (opt != null) {
- return opt
- }
- val key = new ClassBType(csym.javaBinaryName.toTypeName)
- assert(key.isNonSpecial || isCompilingStdLib, s"Not a class to track: ${csym.fullName}")
-
- // TODO accomodate the fix for SI-5031 of https://github.com/scala/scala/commit/0527b2549bcada2fda2201daa630369b377d0877
- // TODO Weaken this assertion? buildExemplar() needs to be updated, too. In the meantime, pos/t5031_3 has been moved to test/disabled/pos.
- val whatWasInExemplars = exemplars.get(key)
- assert(whatWasInExemplars == null, "Maps `symExemplars` and `exemplars` got out of synch.")
- val tr = buildExemplar(key, csym)
- symExemplars.put(csym, tr)
- if (csym != csym0) { symExemplars.put(csym0, tr) }
- exemplars.put(tr.c, tr) // tr.c is the hash-consed, internalized, canonical representative for csym's key.
- tr
- }
-
- /*
- * must-single-thread
- */
- private def buildExemplar(key: ClassBType, csym: Symbol): Tracked = {
- val sc =
- if (csym.isImplClass) definitions.ObjectClass
- else csym.superClass
- assert(
- if (csym == definitions.ObjectClass)
- sc == NoSymbol
- else if (csym.isInterface)
- sc == definitions.ObjectClass
- else
- ((sc != NoSymbol) && !sc.isInterface) || isCompilingStdLib,
- "superClass out of order"
- )
- val ifacesArr = getSuperInterfaces(csym).map(exemplar).toArray
-
- val flags = mkFlags(
- javaFlags(csym),
- if (isDeprecated(csym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo access flag
- )
-
- val tsc = if (sc == NoSymbol) null else exemplar(sc)
-
- val innersChain = saveInnerClassesFor(csym, key)
-
- Tracked(key, flags, tsc, ifacesArr, innersChain)
- }
-
- // ---------------- utilities around interfaces represented by Tracked instances. ----------------
-
- /* Drop redundant interfaces (those which are implemented by some other).
- * In other words, no two interfaces in the result are related by subtyping.
- * This method works on Tracked elements, a similar one (not duplicate) works on Symbols.
- */
- def minimizeInterfaces(lstIfaces: Set[Tracked]): Set[Tracked] = {
- checkAllInterfaces(lstIfaces)
- var rest = lstIfaces.toList
- var leaves = List.empty[Tracked]
- while (!rest.isEmpty) {
- val candidate = rest.head
- val nonLeaf = leaves exists { leaf => leaf.isSubtypeOf(candidate.c) }
- if (!nonLeaf) {
- leaves = candidate :: (leaves filterNot { leaf => candidate.isSubtypeOf(leaf.c) })
- }
- rest = rest.tail
- }
-
- leaves.toSet
- }
-
- def allInterfaces(is: Iterable[Tracked]): Boolean = { is forall { i => i.isInterface } }
- def nonInterfaces(is: Iterable[Tracked]): Iterable[Tracked] = { is filterNot { i => i.isInterface } }
-
- def checkAllInterfaces(ifaces: Iterable[Tracked]) {
- assert(allInterfaces(ifaces), s"Non-interfaces: ${nonInterfaces(ifaces).mkString}")
- }
-
- /*
- * Subtype check `a <:< b` on BTypes that takes into account the JVM built-in numeric promotions (e.g. BYTE to INT).
- * Its operation can be visualized more easily in terms of the Java bytecode type hierarchy.
- * This method used to be called, in the ICode world, TypeKind.<:<()
- *
- * can-multi-thread
- */
- final def conforms(a: BType, b: BType): Boolean = {
- if (a.isArray) { // may be null
- /* Array subtyping is covariant here, as in Java bytecode. Also necessary for Java interop. */
- if ((b == jlCloneableReference) ||
- (b == jioSerializableReference) ||
- (b == AnyRefReference)) { true }
- else if (b.isArray) { conforms(a.asArrayBType.componentType, // TODO @lry change to pattern match, get rid of casts
- b.asArrayBType.componentType) }
- else { false }
- }
- else if (a.isBoxed) { // may be null
- if (b.isBoxed) { a == b }
- else if (b == AnyRefReference) { true }
- else if (!(b.isClass)) { false }
- else { exemplars.get(a).isSubtypeOf(b) } // e.g., java/lang/Double conforms to java/lang/Number
- }
- else if (a.isNullType) { // known to be null
- if (b.isNothingType) { false }
- else if (b.isPrimitive) { false }
- else { true }
- }
- else if (a.isNothingType) { // known to be Nothing
- true
- }
- else if (a == UNIT) {
- b == UNIT
- }
- else if (a.isClass) { // may be null
- if (a.isNothingType) { true }
- else if (b.isClass) { exemplars.get(a).isSubtypeOf(b) }
- else if (b.isArray) { a.isNullType } // documentation only, because `if(a.isNullType)` (above) covers this case already.
- else { false }
- }
- else {
-
- def msg = s"(a: $a, b: $b)"
-
- assert(a.isNonVoidPrimitiveType, s"a isn't a non-Unit value type. $msg")
- assert(b.isPrimitive, s"b isn't a value type. $msg")
-
- (a eq b) || (a match {
- case BOOL | BYTE | SHORT | CHAR => b == INT || b == LONG // TODO Actually, BOOL does NOT conform to LONG. Even with adapt().
- case _ => a == b
- })
- }
- }
-
- /* The maxValueType of (Char, Byte) and of (Char, Short) is Int, to encompass the negative values of Byte and Short. See ticket #2087.
- *
- * can-multi-thread
- */
- def maxValueType(a: BType, other: BType): BType = {
- assert(a.isPrimitive, "maxValueType() is defined only for 1st arg valuetypes (2nd arg doesn't matter).")
-
- def uncomparable: Nothing = {
- abort(s"Uncomparable BTypes: $a with $other")
- }
-
- if (a.isNothingType) return other;
- if (other.isNothingType) return a;
- if (a == other) return a;
-
- a match {
-
- case UNIT => uncomparable
- case BOOL => uncomparable
-
- case BYTE =>
- if (other == CHAR) INT
- else if (other.isNumericType) other
- else uncomparable
-
- case SHORT =>
- other match {
- case BYTE => SHORT
- case CHAR => INT
- case INT | LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
- }
-
- case CHAR =>
- other match {
- case BYTE | SHORT => INT
- case INT | LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
- }
-
- case INT =>
- other match {
- case BYTE | SHORT | CHAR => INT
- case LONG | FLOAT | DOUBLE => other
- case _ => uncomparable
- }
-
- case LONG =>
- if (other.isIntegralType) LONG
- else if (other.isRealType) DOUBLE
- else uncomparable
-
- case FLOAT =>
- if (other == DOUBLE) DOUBLE
- else if (other.isNumericType) FLOAT
- else uncomparable
-
- case DOUBLE =>
- if (other.isNumericType) DOUBLE
- else uncomparable
-
- case _ => uncomparable
- }
- }
-
- /* Takes promotions of numeric primitives into account.
- *
- * can-multi-thread
- */
- final def maxType(a: BType, other: BType): BType = {
- if (a.isPrimitive) { maxValueType(a, other) }
- else {
- if (a.isNothingType) return other;
- if (other.isNothingType) return a;
- if (a == other) return a;
- // Approximate `lub`. The common type of two references is always AnyRef.
- // For 'real' least upper bound wrt to subclassing use method 'lub'.
- assert(a.isArray || a.isBoxed || a.isClass, s"This is not a valuetype and it's not something else, what is it? $a")
- // TODO For some reason, ICode thinks `REFERENCE(...).maxType(BOXED(whatever))` is `uncomparable`. Here, that has maxType AnyRefReference.
- // BTW, when swapping arguments, ICode says BOXED(whatever).maxType(REFERENCE(...)) == AnyRefReference, so I guess the above was an oversight in REFERENCE.maxType()
- if (other.isRef) { AnyRefReference }
- else { abort(s"Uncomparable BTypes: $a with $other") }
- }
- }
-
- /*
- * Whether the argument is a subtype of
- * scala.PartialFunction[-A, +B] extends (A => B)
- * N.B.: this method returns true for a scala.runtime.AbstractPartialFunction
- *
- * can-multi-thread
- */
- def isPartialFunctionType(t: BType): Boolean = {
- (t.isClass) && exemplars.get(t).isSubtypeOf(PartialFunctionReference)
- }
-
- /*
- * Whether the argument is a subtype of scala.FunctionX where 0 <= X <= definitions.MaxFunctionArity
- *
- * can-multi-thread
- */
- def isFunctionType(t: BType): Boolean = {
- if (!t.isClass) return false
- var idx = 0
- val et: Tracked = exemplars.get(t)
- while (idx <= definitions.MaxFunctionArity) {
- if (et.isSubtypeOf(FunctionReference(idx).c)) {
- return true
- }
- idx += 1
- }
- false
- }
-
- /**
- * must-single-thread
- *
- * True for module classes of package level objects. The backend will generate a mirror class for
- * such objects.
- */
- def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler {
- // phase travel to pickler required for isNestedClass (looks at owner)
- val r = sym.isModuleClass && !sym.isNestedClass
- // The mixin phase adds the `lateMODULE` flag to trait implementation classes. Since the flag
- // is late, it should not be visible here inside the time travel. We check this.
- if (r) assert(!sym.isImplClass, s"isModuleClass should be false for impl class $sym")
- r
- }
-
- /**
- * must-single-thread
- *
- * True for module classes of modules that are top-level or owned only by objects. Module classes
- * for such objects will get a MODULE$ flag and a corresponding static initializer.
- */
- def isStaticModuleClass(sym: Symbol): Boolean = {
- /* The implementation of this method is tricky because it is a source-level property. Various
- * phases changed the symbol's properties in the meantime.
- *
- * (1) Phase travel to to pickler is required to exclude implementation classes; they have the
- * lateMODULEs after mixin, so isModuleClass would be true.
- *
- * (2) We cannot use `sym.isStatic` because lambdalift modified (destructively) the owner. For
- * example, in
- * object T { def f { object U } }
- * the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here.
- * So we basically re-implement `sym.isStaticOwner`, but using the original owner chain.
- */
-
- def isOriginallyStaticOwner(sym: Symbol): Boolean = {
- sym.isPackageClass || sym.isModuleClass && isOriginallyStaticOwner(sym.originalOwner)
- }
-
- exitingPickler { // (1)
- sym.isModuleClass &&
- isOriginallyStaticOwner(sym.originalOwner) // (2)
- }
- }
-
-
- // ---------------------------------------------------------------------
- // ---------------- InnerClasses attribute (JVMS 4.7.6) ----------------
- // ---------------------------------------------------------------------
-
- val INNER_CLASSES_FLAGS =
- (asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED |
- asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_INTERFACE | asm.Opcodes.ACC_ABSTRACT | asm.Opcodes.ACC_FINAL)
-
- /*
- * @param name the internal name of an inner class.
- * @param outerName the internal name of the class to which the inner class belongs.
- * May be `null` for non-member inner classes (ie for a Java local class or a Java anonymous class).
- * @param innerName the (simple) name of the inner class inside its enclosing class. It's `null` for anonymous inner classes.
- * @param access the access flags of the inner class as originally declared in the enclosing class.
- */
- case class InnerClassEntry(name: String, outerName: String, innerName: String, access: Int) {
- assert(name != null, "Null isn't good as class name in an InnerClassEntry.")
- }
-
- /* For given symbol return a symbol corresponding to a class that should be declared as inner class.
- *
- * For example:
- * class A {
- * class B
- * object C
- * }
- *
- * then method will return:
- * NoSymbol for A,
- * the same symbol for A.B (corresponding to A$B class), and
- * A$C$ symbol for A.C.
- *
- * must-single-thread
- */
- def innerClassSymbolFor(s: Symbol): Symbol =
- if (s.isClass) s else if (s.isModule) s.moduleClass else NoSymbol
-
- /*
- * Computes the chain of inner-class (over the is-member-of relation) for the given argument.
- * The resulting chain will be cached in `exemplars`.
- *
- * The chain thus cached is valid during this compiler run, see in contrast
- * `innerClassBufferASM` for a cache that is valid only for the class being emitted.
- *
- * The argument can be any symbol, but given that this method is invoked only from `buildExemplar()`,
- * in practice it has been vetted to be a class-symbol.
- *
- * Returns:
- *
- * - a non-empty array of entries for an inner-class argument.
- * The array's first element is the outermost top-level class,
- * the array's last element corresponds to csym.
- *
- * - null otherwise.
- *
- * This method does not add to `innerClassBufferASM`, use instead `exemplar()` for that.
- *
- * must-single-thread
- */
- final def saveInnerClassesFor(csym: Symbol, csymTK: BType): Array[InnerClassEntry] = {
-
- val ics = innerClassSymbolFor(csym)
- if (ics == NoSymbol) {
- return null
- }
- assert(ics == csym, s"Disagreement between innerClassSymbolFor() and exemplar()'s tracked symbol for the same input: ${csym.fullName}")
-
- var chain: List[Symbol] = Nil
- var x = ics
- while (x ne NoSymbol) {
- assert(x.isClass, s"not a class symbol: ${x.fullName}")
- // Uses `rawowner` because `owner` reflects changes in the owner chain due to flattening.
- // The owner chain of a class only contains classes. This is because the lambdalift phase
- // changes the `rawowner` destructively to point to the enclosing class. Before, the owner
- // might be for example a method.
- val isInner = !x.rawowner.isPackageClass
- if (isInner) {
- chain ::= x
- x = innerClassSymbolFor(x.rawowner)
- } else {
- x = NoSymbol
- }
- }
-
- if (chain.isEmpty) null
- else chain.map(toInnerClassEntry).toArray
- }
-
- /*
- * must-single-thread
- */
- private def toInnerClassEntry(innerSym: Symbol): InnerClassEntry = {
-
- /* The outer name for this inner class. Note that it returns null
- * when the inner class should not get an index in the constant pool.
- * That means non-member classes (anonymous). See Section 4.7.5 in the JVMS.
- */
- def outerName(innerSym: Symbol): Name = {
- if (innerSym.originalEnclosingMethod != NoSymbol)
- null
- else {
- val outerName = innerSym.rawowner.javaBinaryName
- if (isTopLevelModuleClass(innerSym.rawowner)) nme.stripModuleSuffix(outerName)
- else outerName
- }
- }
-
- def innerName(innerSym: Symbol): String = {
- if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction)
- null
- else
- innerSym.rawname + innerSym.moduleSuffix
- }
-
- // TODO @lry compare with table in spec: for example, deprecated should not be there it seems.
- // http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6-300-D.1-D.1
- // including "deprecated" was added in the initial commit of GenASM, but it was never in GenJVM.
- val flags: Int = mkFlags(
- // TODO @lry adding "static" whenever the class is owned by a module seems wrong.
- // class C { object O { class I } }
- // here, I is marked static in the InnerClass attribute. But the I constructor takes an outer instance.
- // was added in 0469d41
- // what should it be? check what would make sense for java reflection.
- // member of top-level object should be static? how about anonymous / local class that has
- // been lifted to a top-level object?
- // member that is only nested in objects should be static?
- // verify: will ICodeReader still work after that? the code was introduced because of icode reader.
- if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0,
- javaFlags(innerSym),
- if (isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag
- ) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED)
-
- val jname = innerSym.javaBinaryName.toString // never null
- val oname = { // null when method-enclosed
- val on = outerName(innerSym)
- if (on == null) null else on.toString
- }
- val iname = { // null for anonymous inner class
- val in = innerName(innerSym)
- if (in == null) null else in.toString
- }
-
- InnerClassEntry(jname, oname, iname, flags)
- }
-
- // --------------------------------------------
- // ---------------- Java flags ----------------
- // --------------------------------------------
-
- /*
- * can-multi-thread
- */
- final def hasPublicBitSet(flags: Int) = ((flags & asm.Opcodes.ACC_PUBLIC) != 0)
-
- /*
- * must-single-thread
- */
- final def isRemote(s: Symbol) = (s hasAnnotation definitions.RemoteAttr)
-
- /*
- * Return the Java modifiers for the given symbol.
- * Java modifiers for classes:
- * - public, abstract, final, strictfp (not used)
- * for interfaces:
- * - the same as for classes, without 'final'
- * for fields:
- * - public, private (*)
- * - static, final
- * for methods:
- * - the same as for fields, plus:
- * - abstract, synchronized (not used), strictfp (not used), native (not used)
- *
- * (*) protected cannot be used, since inner classes 'see' protected members,
- * and they would fail verification after lifted.
- *
- * must-single-thread
- */
- def javaFlags(sym: Symbol): Int = {
- // constructors of module classes should be private. introduced in b06edbc, probably to prevent
- // creating module instances from java. for nested modules, the constructor needs to be public
- // since they are created by the outer class and stored in a field. a java client can create
- // new instances via outerClassInstance.new InnerModuleClass$().
- // TODO: do this early, mark the symbol private.
- val privateFlag =
- sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass(sym.owner))
-
- // Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also
- // added to modules and module classes, not anymore since 296b706).
- // Note that the presence of the `FINAL` flag on a symbol does not correspond 1:1 to emitting
- // ACC_FINAL in bytecode.
- //
- // Top-level modules are marked ACC_FINAL in bytecode (even without the FINAL flag). Nested
- // objects don't get the flag to allow overriding (under -Yoverride-objects, SI-5676).
- //
- // For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't:
- // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
- // "Another problem is that the specification allows aggressive
- // optimization of final fields. Within a thread, it is permissible to
- // reorder reads of a final field with those modifications of a final
- // field that do not take place in the constructor."
- //
- // A var or lazy val which is marked final still has meaning to the
- // scala compiler. The word final is heavily overloaded unfortunately;
- // for us it means "not overridable". At present you can't override
- // vars regardless; this may change.
- //
- // The logic does not check .isFinal (which checks flags for the FINAL flag,
- // and includes symbols marked lateFINAL) instead inspecting rawflags so
- // we can exclude lateFINAL. Such symbols are eligible for inlining, but to
- // avoid breaking proxy software which depends on subclassing, we do not
- // emit ACC_FINAL.
-
- val finalFlag = (
- (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym))
- && !sym.enclClass.isInterface
- && !sym.isClassConstructor
- && !sym.isMutable // lazy vals and vars both
- )
-
- // Primitives are "abstract final" to prohibit instantiation
- // without having to provide any implementations, but that is an
- // illegal combination of modifiers at the bytecode level so
- // suppress final if abstract if present.
- import asm.Opcodes._
- mkFlags(
- if (privateFlag) ACC_PRIVATE else ACC_PUBLIC,
- if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0,
- if (sym.isInterface) ACC_INTERFACE else 0,
- if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0,
- if (sym.isStaticMember) ACC_STATIC else 0,
- if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
- if (sym.isArtifact) ACC_SYNTHETIC else 0,
- if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
- if (sym.hasEnumFlag) ACC_ENUM else 0,
- if (sym.isVarargsMethod) ACC_VARARGS else 0,
- if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0
- )
- // TODO @lry should probably also check / add "deprectated"
- // all call sites of "javaFlags" seem to check for deprecation rigth after.
- // Exception: the call below in javaFieldFlags. However, the caller of javaFieldFlags then
- // does the check.
- }
-
- /*
- * must-single-thread
- */
- def javaFieldFlags(sym: Symbol) = {
- javaFlags(sym) | mkFlags(
- if (sym hasAnnotation definitions.TransientAttr) asm.Opcodes.ACC_TRANSIENT else 0,
- if (sym hasAnnotation definitions.VolatileAttr) asm.Opcodes.ACC_VOLATILE else 0,
- if (sym.isMutable) 0 else asm.Opcodes.ACC_FINAL
- )
- }
-
-} // end of class BCodeTypes
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index 15bc068533..53ac5bfdc7 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -1,37 +1,57 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
package scala.tools.nsc
package backend.jvm
-import scala.collection.immutable
-import scala.annotation.switch
import scala.tools.asm
import asm.Opcodes
-import scala.collection.mutable.ListBuffer
/**
- * BTypes is a backend component that defines the class BType, a number of basic instances and
- * some utilities.
+ * The BTypes component defines The BType class hierarchy. BTypes encapsulates all type information
+ * that is required after building the ASM nodes. This includes optimizations, geneartion of
+ * InnerClass attributes and generation of stack map frames.
*
- * A BType is essentially an slice of the array `chrs` denoting the name of the type, and a field
- * denoting the kind (object, array, method, or one of the primitive types).
- *
- * BTypes depends on Global just because it re-uses hash-consing of Name. It would be cleaner to
- * create an interface for BTypeName and extend it in scala.reflect.internal.Names#Name, that
- * would simplify testing BTypes (no Global needed).
+ * This representation is immutable and independent of the compiler data structures, hence it can
+ * be queried by concurrent threads.
*/
-abstract class BTypes[G <: Global](val __global_dont_use: G) {
- def chrs: Array[Char]
+abstract class BTypes {
+ /**
+ * A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
+ * construction.
+ *
+ * This map is used when computing stack map frames. The asm.ClassWriter invokes the method
+ * `getCommonSuperClass`. In this method we need to obtain the ClassBType for a given internal
+ * name. The method assumes that every class type that appears in the bytecode exists in the map.
+ *
+ * Concurrent because stack map frames are computed when in the class writer, which might run
+ * on multiple classes concurrently.
+ */
+ protected val classBTypeFromInternalNameMap: collection.concurrent.Map[String, ClassBType]
/**
- * Interface for names stored in `chrs`
+ * The string represented by the `offset` / `length` values of a ClassBType, see comment of that
+ * class.
*/
- type BTypeName <: __global_dont_use.Name
+ protected def internalNameString(offset: Int, lenght: Int): String
/**
- * Create a new name in `chrs`. Names are assumed to be hash-consed. Equality on BType will use
- * reference equality to compare the names.
+ * Obtain a previously constructed ClassBType for a given internal name.
*/
- def createNewName(s: String): BTypeName
+ def classBTypeFromInternalName(internalName: String) = classBTypeFromInternalNameMap(internalName)
+ // Some core BTypes are required here, in class BType, where no Global instance is available.
+ // The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
+ // implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
+ val coreBTypes: CoreBTypesProxyGlobalIndependent[this.type]
+ import coreBTypes._
+
+ /**
+ * A BType is either a primitve type, a ClassBType, an ArrayBType of one of these, or a MethodType
+ * referring to BTypes.
+ */
/*sealed*/ trait BType { // Not sealed for now due to SI-8546
final override def toString: String = this match {
case UNIT => "V"
@@ -73,18 +93,11 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
final def isMethod: Boolean = this.isInstanceOf[MethodBType]
final def isNonVoidPrimitiveType = isPrimitive && this != UNIT
- // TODO @lry should also include !isMethod in isNonSpecial? in this case it would be equivalent to isClass, so we could get rid of it.
- final def isNonSpecial = !isPrimitive && !isArray && !isPhantomType
- final def isNullType = this == RT_NULL || this == CT_NULL
- final def isNothingType = this == RT_NOTHING || this == CT_NOTHING
- final def isPhantomType = isNullType || isNothingType
-
- final def isBoxed = this match {
- case BOXED_UNIT | BOXED_BOOLEAN | BOXED_CHAR |
- BOXED_BYTE | BOXED_SHORT | BOXED_INT |
- BOXED_FLOAT | BOXED_LONG | BOXED_DOUBLE => true
- case _ => false
- }
+
+ final def isNullType = this == RT_NULL
+ final def isNothingType = this == RT_NOTHING
+
+ final def isBoxed = this.isClass && boxedClasses(this.asClassBType)
final def isIntSizedType = this == BOOL || this == CHAR || this == BYTE ||
this == SHORT || this == INT
@@ -94,6 +107,72 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
final def isNumericType = isIntegralType || isRealType
final def isWideType = size == 2
+ /*
+ * Subtype check `this <:< other` on BTypes that takes into account the JVM built-in numeric
+ * promotions (e.g. BYTE to INT). Its operation can be visualized more easily in terms of the
+ * Java bytecode type hierarchy.
+ */
+ final def conformsTo(other: BType): Boolean = {
+ assert(isRef || isPrimitive, s"conformsTo cannot handle $this")
+ assert(other.isRef || other.isPrimitive, s"conformsTo cannot handle $other")
+
+ this match {
+ case ArrayBType(component) =>
+ if (other == ObjectReference || other == jlCloneableReference || other == jioSerializableReference) true
+ else other match {
+ case ArrayBType(otherComponoent) => component.conformsTo(otherComponoent)
+ case _ => false
+ }
+
+ case classType: ClassBType =>
+ if (isBoxed) {
+ if (other.isBoxed) this == other
+ else if (other == ObjectReference) true
+ else other match {
+ case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType) // e.g., java/lang/Double conforms to java/lang/Number
+ case _ => false
+ }
+ } else if (isNullType) {
+ if (other.isNothingType) false
+ else if (other.isPrimitive) false
+ else true // Null conforms to all classes (except Nothing) and arrays.
+ } else if (isNothingType) {
+ true
+ } else other match {
+ case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType)
+ // case ArrayBType(_) => this.isNullType // documentation only, because `if (isNullType)` above covers this case
+ case _ =>
+ // isNothingType || // documentation only, because `if (isNothingType)` above covers this case
+ false
+ }
+
+ case UNIT =>
+ other == UNIT
+ case BOOL | BYTE | SHORT | CHAR =>
+ this == other || other == INT || other == LONG // TODO Actually, BOOL does NOT conform to LONG. Even with adapt().
+ case _ =>
+ assert(isPrimitive && other.isPrimitive, s"Expected primitive types $this - $other")
+ this == other
+ }
+ }
+
+ /**
+ * Compute the upper bound of two types.
+ * Takes promotions of numeric primitives into account.
+ */
+ final def maxType(other: BType): BType = this match {
+ case pt: PrimitiveBType => pt.maxValueType(other)
+
+ case _: ArrayBType | _: ClassBType =>
+ if (isNothingType) return other
+ if (other.isNothingType) return this
+ if (this == other) return this
+
+ assert(other.isRef, s"Cannot compute maxType: $this, $other")
+ // Approximate `lub`. The common type of two references is always ObjectReference.
+ ObjectReference
+ }
+
/**
* See documentation of [[typedOpcode]].
* The numbers are taken from asm.Type.VOID_TYPE ff., the values are those shifted by << 8.
@@ -165,45 +244,77 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
case m: MethodBType => asm.Type.getMethodType(m.descriptor)
}
- def asRefBType : RefBType = this.asInstanceOf[RefBType]
- def asArrayBType: ArrayBType = this.asInstanceOf[ArrayBType]
- def asClassBType: ClassBType = this.asInstanceOf[ClassBType]
+ def asRefBType : RefBType = this.asInstanceOf[RefBType]
+ def asArrayBType : ArrayBType = this.asInstanceOf[ArrayBType]
+ def asClassBType : ClassBType = this.asInstanceOf[ClassBType]
+ def asPrimitiveBType : PrimitiveBType = this.asInstanceOf[PrimitiveBType]
}
- object BType {
+ sealed trait PrimitiveBType extends BType {
+
/**
- * @param chars The character array containing the descriptor
- * @param start The position where the descriptor starts
- * @return The BType and the index of the first character after the consumed descriptor
+ * The upper bound of two primitive types. The `other` type has to be either a primitive
+ * type or Nothing.
+ *
+ * The maxValueType of (Char, Byte) and of (Char, Short) is Int, to encompass the negative
+ * values of Byte and Short. See ticket #2087.
*/
- private[BTypes] def fromNonMethodDescriptor(chars: Array[Char], start: Int): (BType, Int) = {
- chars(start) match {
- case 'L' =>
- var i = start
- while (chars(i) != ';') { i += 1 }
- // Example: chars = "IILpkg/Cls;I"
- // ^ ^
- // start=2 i=10
- // `start + 1` to exclude the 'L', `i - start - 1` excludes the ';'
- (new ClassBType(new String(chars, start + 1, i - start - 1)), i + 1)
- case '[' =>
- val (res, next) = fromNonMethodDescriptor(chars, start + 1)
- (ArrayBType(res), next)
- case 'V' => (UNIT, start + 1)
- case 'Z' => (BOOL, start + 1)
- case 'C' => (CHAR, start + 1)
- case 'B' => (BYTE, start + 1)
- case 'S' => (SHORT, start + 1)
- case 'I' => (INT, start + 1)
- case 'F' => (FLOAT, start + 1)
- case 'J' => (LONG, start + 1)
- case 'D' => (DOUBLE, start + 1)
+ final def maxValueType(other: BType): BType = {
+
+ def uncomparable: Nothing = throw new AssertionError(s"Cannot compute maxValueType: $this, $other")
+
+ if (!other.isPrimitive && !other.isNothingType) uncomparable
+
+ if (other.isNothingType) return this
+ if (this == other) return this
+
+ this match {
+ case BYTE =>
+ if (other == CHAR) INT
+ else if (other.isNumericType) other
+ else uncomparable
+
+ case SHORT =>
+ other match {
+ case BYTE => SHORT
+ case CHAR => INT
+ case INT | LONG | FLOAT | DOUBLE => other
+ case _ => uncomparable
+ }
+
+ case CHAR =>
+ other match {
+ case BYTE | SHORT => INT
+ case INT | LONG | FLOAT | DOUBLE => other
+ case _ => uncomparable
+ }
+
+ case INT =>
+ other match {
+ case BYTE | SHORT | CHAR => INT
+ case LONG | FLOAT | DOUBLE => other
+ case _ => uncomparable
+ }
+
+ case LONG =>
+ if (other.isIntegralType) LONG
+ else if (other.isRealType) DOUBLE
+ else uncomparable
+
+ case FLOAT =>
+ if (other == DOUBLE) DOUBLE
+ else if (other.isNumericType) FLOAT
+ else uncomparable
+
+ case DOUBLE =>
+ if (other.isNumericType) DOUBLE
+ else uncomparable
+
+ case UNIT | BOOL => uncomparable
}
}
}
- sealed trait PrimitiveBType extends BType
-
case object UNIT extends PrimitiveBType
case object BOOL extends PrimitiveBType
case object CHAR extends PrimitiveBType
@@ -261,6 +372,14 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* - instance initializer: exectued when class is initialized (instance creation, static
* field access, ...)
*
+ * - A static nested class can be defined as
+ * - a static member class (explicitly static), or
+ * - a member class of an interface (implicitly static)
+ * - local classes are never static, even if they are defined in a static method.
+ *
+ * Note: it is NOT the case that all inner classes (non-static) have an outer pointer. Example:
+ * class C { static void foo { class D {} } }
+ * The class D is an inner class (non-static), but javac does not add an outer pointer to it.
*
* InnerClass
* ----------
@@ -271,8 +390,8 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* The JLS 13.1, points 9. / 10. requires: a class must reference (in the CP)
* - its immediately enclosing class
* - all of its member classes
- * - all local and anonymous classes that appear elsewhere (method, constructor, initializer
- * block, field initializer)
+ * - all local and anonymous classes that are referenced (or declared) elsewhere (method,
+ * constructor, initializer block, field initializer)
*
* In a comment, the 4.7.6 spec says: this implies an entry in the InnerClass attribute for
* - All enclosing classes (except the outermost, which is top-level)
@@ -284,9 +403,11 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* Fields in the InnerClass entries:
* - inner class: the (nested) class C we are talking about
* - outer class: the class of which C is a member. Has to be null for non-members, i.e. for
- * local and anonymous classes.
+ * local and anonymous classes. NOTE: this co-incides with the presence of an
+ * EnclosingMethod attribute (see below)
* - inner name: A string with the simple name of the inner class. Null for anonymous classes.
- * - flags: access property flags, details in JVMS, table in 4.7.6.
+ * - flags: access property flags, details in JVMS, table in 4.7.6. Static flag: see
+ * discussion below.
*
*
* Note 1: when a nested class is present in the InnerClass attribute, all of its enclosing
@@ -327,6 +448,13 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* JVMS 4.7.7: the attribute must be present "if and only if it represents a local class
* or an anonymous class" (i.e. not for member classes).
*
+ * The attribute is mis-named, it should be called "EnclosingClass". It has to be defined for all
+ * local and anonymous classes, no matter if there is an enclosing method or not. Accordingly, the
+ * "class" field (see below) must be always defined, while the "method" field may be null.
+ *
+ * NOTE: When a EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
+ * field in the InnerClass table must be null.
+ *
* Fields:
* - class: the enclosing class
* - method: the enclosing method (or constructor). Null if the class is not enclosed by a
@@ -337,9 +465,8 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* Note: the field is required for anonymous classes defined within local variable
* initializers (within a method), Java example below (**).
*
- * Currently, the Scala compiler sets "method" to the class constructor for classes
- * defined in initializer blocks or field initializers. This is probably OK, since the
- * Scala compiler desugars these statements into to the primary constructor.
+ * For local and anonymous classes in initializer blocks or field initializers, and
+ * class-level anonymous classes, the scala compiler sets the "method" field to null.
*
*
* (*)
@@ -401,30 +528,37 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
* STATIC flag
* -----------
*
- * Java: static nested classes have the "static" flag in the InnerClass attribute. This is not the
- * case for local classes defined within a static method, even though such classes, as they are
- * defined in a static context, don't take an "outer" instance.
- * Non-static nested classes (inner classes, including local classes defined in a non-static
- * method) take an "outer" instance on construction.
+ * Java: static member classes have the static flag in the InnerClass attribute, for example B in
+ * class A { static class B { } }
+ *
+ * The spec is not very clear about when the static flag should be emitted. It says: "Marked or
+ * implicitly static in source."
+ *
+ * The presence of the static flag does NOT coincide with the absence of an "outer" field in the
+ * class. The java compiler never puts the static flag for local classes, even if they don't have
+ * an outer pointer:
+ *
+ * class A {
+ * void f() { class B {} }
+ * static void g() { calss C {} }
+ * }
+ *
+ * B has an outer pointer, C doesn't. Both B and C are NOT marked static in the InnerClass table.
+ *
+ * It seems sane to follow the same principle in the Scala compiler. So:
*
- * Scala: Explicitouter adds an "outer" parameter to nested classes, except for classes defined
- * in a static context, i.e. when all outer classes are module classes.
* package p
* object O1 {
- * class C1 // static
- * object O2 {
+ * class C1 // static inner class
+ * object O2 { // static inner module
* def f = {
- * class C2 { // static
- * class C3 // non-static, needs outer
+ * class C2 { // non-static inner class, even though there's no outer pointer
+ * class C3 // non-static, has an outer pointer
* }
* }
* }
* }
*
- * Int the InnerClass attribute, the `static` flag is added for all classes defined in a static
- * context, i.e. also for C2. This is different than in Java.
- *
- *
* Mirror Classes
* --------------
*
@@ -432,64 +566,179 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
*/
/**
- * Class or Interface type.
- *
- * The information for creating a ClassBType (superClass, interfaces, etc) is obtained
- * - either from a ClassSymbol, for classes being compiled or referenced from source (see
- * BCodeTypes)
- * - or, during inlining, from ASM ClassNodes that are parsed from class files.
- *
- * The class name is represented as a slice of the `chrs` array. This representation is efficient
- * because the JVM class name is obtained through `classSymbol.javaBinaryName`. This already adds
- * the necessary string to the `chrs` array, so it makes sense to reuse the same name table in the
- * backend.
- *
- * Not a case class because that would expose the constructor that takes (offset, length)
- * parameters (I didn't find a way to make it private, also the factory in the companion).
- *
- * @param offset See below
- * @param length The class name is represented as offset and length in the `chrs` array.
- * The (public) constructors of ClassBType take a BTypeName, which are
- * hash-consed. This ensures that two ClassBType instances for the same name
- * have the same offset and length.
- *
- * Not a case class because that would expose the (Int, Int) constructor (didn't find a way to
- * make it private, also the factory in the companion).
+ * A ClassBType represents a class or interface type. The necessary information to build a
+ * ClassBType is extracted from compiler symbols and types, see BTypesFromSymbols.
+ *
+ * The `offset` and `length` fields are used to represent the internal name of the class. They
+ * are indices into some character array. The internal name can be obtained through the method
+ * `internalNameString`, which is abstract in this component. Name creation is assumed to be
+ * hash-consed, so if two ClassBTypes have the same internal name, they NEED to have the same
+ * `offset` and `length`.
+ *
+ * The actual implementation in subclass BTypesFromSymbols uses the global `chrs` array from the
+ * name table. This representation is efficient because the JVM class name is obtained through
+ * `classSymbol.javaBinaryName`. This already adds the necessary string to the `chrs` array,
+ * so it makes sense to reuse the same name table in the backend.
+ *
+ * ClassBType is not a case class because we want a custom equals method, and because the
+ * extractor extracts the internalName, which is what you typically need.
*/
- class ClassBType private(val offset: Int, val length: Int) extends RefBType {
+ final class ClassBType(val offset: Int, val length: Int) extends RefBType {
/**
- * Construct a ClassBType from the (intenred) internal name of a class.
+ * Write-once variable allows initializing a cyclic graph of infos. This is required for
+ * nested classes. Example: for the definition `class A { class B }` we have
*
- * @param internalName The internal name as a slice of the `chrs` array. The internal name does
- * not have the surrounding 'L' and ';'. Note that
- * `classSymbol.javaBinaryName` returns exactly such a name.
+ * B.info.nestedInfo.outerClass == A
+ * A.info.memberClasses contains B
*/
- def this(internalName: BTypeName) = this(internalName.start, internalName.length)
+ private var _info: ClassInfo = null
- /**
- * Construct a ClassBType from the internal name of a class.
- *
- * @param internalName The internal name of a class has the form "java/lang/String", without the
- * surrounding 'L' and ';'.
- */
- def this(internalName: String) = this({
- assert(!(internalName.head == 'L' && internalName.last == ';'), s"Descriptor instead of internal name: $internalName")
- createNewName(internalName)
- })
+ def info: ClassInfo = {
+ assert(_info != null, s"ClassBType.info not yet assigned: $this")
+ _info
+ }
+
+ def info_=(i: ClassInfo): Unit = {
+ assert(_info == null, s"Cannot set ClassBType.info multiple times: $this")
+ _info = i
+ checkInfoConsistency()
+ }
+
+ classBTypeFromInternalNameMap(internalName) = this
+
+ private def checkInfoConsistency(): Unit = {
+ // we assert some properties. however, some of the linked ClassBType (members, superClass,
+ // interfaces) may not yet have an `_info` (initialization of cyclic structures). so we do a
+ // best-effort verification.
+ def ifInit(c: ClassBType)(p: ClassBType => Boolean): Boolean = c._info == null || p(c)
+
+ def isJLO(t: ClassBType) = t.internalName == "java/lang/Object"
+
+ assert(!ClassBType.isInternalPhantomType(internalName), s"Cannot create ClassBType for phantom type $this")
+
+ assert(
+ if (info.superClass.isEmpty) { isJLO(this) || (isCompilingPrimitive && ClassBType.hasNoSuper(internalName)) }
+ else if (isInterface) isJLO(info.superClass.get)
+ else !isJLO(this) && ifInit(info.superClass.get)(!_.isInterface),
+ s"Invalid superClass in $this: ${info.superClass}"
+ )
+ assert(
+ info.interfaces.forall(c => ifInit(c)(_.isInterface)),
+ s"Invalid interfaces in $this: ${info.interfaces}"
+ )
+
+ assert(info.memberClasses.forall(c => ifInit(c)(_.isNestedClass)), info.memberClasses)
+ }
/**
* The internal name of a class is the string returned by java.lang.Class.getName, with all '.'
* replaced by '/'. For example "java/lang/String".
*/
- def internalName: String = new String(chrs, offset, length)
+ def internalName: String = internalNameString(offset, length)
/**
* @return The class name without the package prefix
*/
def simpleName: String = internalName.split("/").last
+ def isInterface = (info.flags & asm.Opcodes.ACC_INTERFACE) != 0
+
+ def superClassesTransitive: List[ClassBType] = info.superClass match {
+ case None => Nil
+ case Some(sc) => sc :: sc.superClassesTransitive
+ }
+
+ def isNestedClass = info.nestedInfo.isDefined
+
+ def enclosingNestedClassesChain: List[ClassBType] =
+ if (isNestedClass) this :: info.nestedInfo.get.enclosingClass.enclosingNestedClassesChain
+ else Nil
+
+ def innerClassAttributeEntry: Option[InnerClassEntry] = info.nestedInfo map {
+ case NestedInfo(_, outerName, innerName, isStaticNestedClass) =>
+ InnerClassEntry(
+ internalName,
+ outerName.orNull,
+ innerName.orNull,
+ GenBCode.mkFlags(
+ info.flags,
+ if (isStaticNestedClass) asm.Opcodes.ACC_STATIC else 0
+ ) & ClassBType.INNER_CLASSES_FLAGS
+ )
+ }
+
+ def isSubtypeOf(other: ClassBType): Boolean = {
+ if (this == other) return true
+
+ if (isInterface) {
+ if (other == ObjectReference) return true // interfaces conform to Object
+ if (!other.isInterface) return false // this is an interface, the other is some class other than object. interfaces cannot extend classes, so the result is false.
+ // else: this and other are both interfaces. continue to (*)
+ } else {
+ val sc = info.superClass
+ if (sc.isDefined && sc.get.isSubtypeOf(other)) return true // the superclass of this class conforms to other
+ if (!other.isInterface) return false // this and other are both classes, and the superclass of this does not conform
+ // else: this is a class, the other is an interface. continue to (*)
+ }
+
+ // (*) check if some interface of this class conforms to other.
+ info.interfaces.exists(_.isSubtypeOf(other))
+ }
+
/**
- * Custom equals / hashCode are needed because this is not a case class.
+ * Finding the least upper bound in agreement with the bytecode verifier
+ * Background:
+ * http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf
+ * http://comments.gmane.org/gmane.comp.java.vm.languages/2293
+ * https://issues.scala-lang.org/browse/SI-3872
+ */
+ def jvmWiseLUB(other: ClassBType): ClassBType = {
+ def isNotNullOrNothing(c: ClassBType) = !c.isNullType && !c.isNothingType
+ assert(isNotNullOrNothing(this) && isNotNullOrNothing(other), s"jvmWiseLub for null or nothing: $this - $other")
+
+ val res: ClassBType = (this.isInterface, other.isInterface) match {
+ case (true, true) =>
+ // exercised by test/files/run/t4761.scala
+ if (other.isSubtypeOf(this)) this
+ else if (this.isSubtypeOf(other)) other
+ else ObjectReference
+
+ case (true, false) =>
+ if (other.isSubtypeOf(this)) this else ObjectReference
+
+ case (false, true) =>
+ if (this.isSubtypeOf(other)) other else ObjectReference
+
+ case _ =>
+ // TODO @lry I don't really understand the reasoning here.
+ // Both this and other are classes. The code takes (transitively) all superclasses and
+ // finds the first common one.
+ // MOST LIKELY the answer can be found here, see the comments and links by Miguel:
+ // - https://issues.scala-lang.org/browse/SI-3872
+ firstCommonSuffix(this :: this.superClassesTransitive, other :: other.superClassesTransitive)
+ }
+
+ assert(isNotNullOrNothing(res), s"jvmWiseLub computed: $res")
+ res
+ }
+
+ private def firstCommonSuffix(as: List[ClassBType], bs: List[ClassBType]): ClassBType = {
+ var chainA = as
+ var chainB = bs
+ var fcs: ClassBType = null
+ do {
+ if (chainB contains chainA.head) fcs = chainA.head
+ else if (chainA contains chainB.head) fcs = chainB.head
+ else {
+ chainA = chainA.tail
+ chainB = chainB.tail
+ }
+ } while (fcs == null)
+ fcs
+ }
+
+ /**
+ * Custom equals / hashCode: we only compare the name (offset / length)
*/
override def equals(o: Any): Boolean = (this eq o.asInstanceOf[Object]) || (o match {
case c: ClassBType => c.offset == this.offset && c.length == this.length
@@ -506,17 +755,92 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
}
object ClassBType {
- def apply(internalName: BTypeName): ClassBType = new ClassBType(internalName)
- def apply(internalName: String): ClassBType = new ClassBType(internalName)
-
/**
* Pattern matching on a ClassBType extracts the `internalName` of the class.
*/
def unapply(c: ClassBType): Option[String] =
if (c == null) None
else Some(c.internalName)
+
+ /**
+ * Valid flags for InnerClass attribute entry.
+ * See http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6
+ */
+ private val INNER_CLASSES_FLAGS = {
+ asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED |
+ asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL | asm.Opcodes.ACC_INTERFACE |
+ asm.Opcodes.ACC_ABSTRACT | asm.Opcodes.ACC_SYNTHETIC | asm.Opcodes.ACC_ANNOTATION |
+ asm.Opcodes.ACC_ENUM
+ }
+
+ // Primitive classes have no super class. A ClassBType for those is only created when
+ // they are actually being compiled (e.g., when compiling scala/Boolean.scala).
+ private val hasNoSuper = Set(
+ "scala/Unit",
+ "scala/Boolean",
+ "scala/Char",
+ "scala/Byte",
+ "scala/Short",
+ "scala/Int",
+ "scala/Float",
+ "scala/Long",
+ "scala/Double"
+ )
+
+ private val isInternalPhantomType = Set(
+ "scala/Null",
+ "scala/Nothing"
+ )
}
+ /**
+ * The type info for a class. Used for symboltable-independent subtype checks in the backend.
+ *
+ * @param superClass The super class, not defined for class java/lang/Object.
+ * @param interfaces All transitively implemented interfaces, except for those inherited
+ * through the superclass.
+ * @param flags The java flags, obtained through `javaFlags`. Used also to derive
+ * the flags for InnerClass entries.
+ * @param memberClasses Classes nested in this class. Those need to be added to the
+ * InnerClass table, see the InnerClass spec summary above.
+ * @param nestedInfo If this describes a nested class, information for the InnerClass table.
+ */
+ case class ClassInfo(superClass: Option[ClassBType], interfaces: List[ClassBType], flags: Int,
+ memberClasses: List[ClassBType], nestedInfo: Option[NestedInfo])
+
+ /**
+ * Information required to add a class to an InnerClass table.
+ * The spec summary above explains what information is required for the InnerClass entry.
+ *
+ * @param enclosingClass The enclosing class, if it is also nested. When adding a class
+ * to the InnerClass table, enclosing nested classes are also added.
+ * @param outerName The outerName field in the InnerClass entry, may be None.
+ * @param innerName The innerName field, may be None.
+ * @param isStaticNestedClass True if this is a static nested class (not inner class) (*)
+ *
+ * (*) Note that the STATIC flag in ClassInfo.flags, obtained through javaFlags(classSym), is not
+ * correct for the InnerClass entry, see javaFlags. The static flag in the InnerClass describes
+ * a source-level propety: if the class is in a static context (does not have an outer pointer).
+ * This is checked when building the NestedInfo.
+ */
+ case class NestedInfo(enclosingClass: ClassBType,
+ outerName: Option[String],
+ innerName: Option[String],
+ isStaticNestedClass: Boolean)
+
+ /**
+ * This class holds the data for an entry in the InnerClass table. See the InnerClass summary
+ * above in this file.
+ *
+ * There's some overlap with the class NestedInfo, but it's not exactly the same and cleaner to
+ * keep separate.
+ * @param name The internal name of the class.
+ * @param outerName The internal name of the outer class, may be null.
+ * @param innerName The simple name of the inner class, may be null.
+ * @param flags The flags for this class in the InnerClass entry.
+ */
+ case class InnerClassEntry(name: String, outerName: String, innerName: String, flags: Int)
+
case class ArrayBType(componentType: BType) extends RefBType {
def dimension: Int = componentType match {
case a: ArrayBType => 1 + a.dimension
@@ -529,107 +853,23 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
}
}
- case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType {
- private def this(types: (List[BType], BType)) = this(types._1, types._2)
- def this(descriptor: String) = this(MethodBType.decomposeMethodDescriptor(descriptor))
- }
-
- object MethodBType {
- private def decomposeMethodDescriptor(descriptor: String): (List[BType], BType) = {
- val chars = descriptor.toCharArray
- assert(chars(0) == '(', s"Not a valid method descriptor: $descriptor")
- var i = 1
- val argTypes = new ListBuffer[BType]
- while (chars(i) != ')') {
- val (argType, next) = BType.fromNonMethodDescriptor(chars, i)
- argTypes += argType
- i = next
- }
- val (resType, _) = BType.fromNonMethodDescriptor(chars, i + 1) // `i + 1` to skip the ')'
- (argTypes.toList, resType)
- }
- def apply(descriptor: String) = {
- val (argTypes, resType) = decomposeMethodDescriptor(descriptor)
- new MethodBType(argTypes, resType)
- }
- }
+ case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType
- val BOXED_UNIT = ClassBType("java/lang/Void")
- val BOXED_BOOLEAN = ClassBType("java/lang/Boolean")
- val BOXED_BYTE = ClassBType("java/lang/Byte")
- val BOXED_SHORT = ClassBType("java/lang/Short")
- val BOXED_CHAR = ClassBType("java/lang/Character")
- val BOXED_INT = ClassBType("java/lang/Integer")
- val BOXED_LONG = ClassBType("java/lang/Long")
- val BOXED_FLOAT = ClassBType("java/lang/Float")
- val BOXED_DOUBLE = ClassBType("java/lang/Double")
-
- /*
- * RT_NOTHING and RT_NULL exist at run-time only. They are the bytecode-level manifestation (in
- * method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs.
- *
- * Therefore, when RT_NOTHING or RT_NULL are to be emitted, a mapping is needed: the internal
- * names of NothingClass and NullClass can't be emitted as-is.
+ /* Some definitions that are required for the implementation of BTypes. They are abstract because
+ * initializing them requires information from types / symbols, which is not accessible here in
+ * BTypes.
+ *
+ * They are defs (not vals) because they are implemented using vars (see comment on CoreBTypes).
*/
- val RT_NOTHING = ClassBType("scala/runtime/Nothing$")
- val RT_NULL = ClassBType("scala/runtime/Null$")
- val CT_NOTHING = ClassBType("scala/Nothing")
- val CT_NULL = ClassBType("scala/Null")
-
- val srBooleanRef = ClassBType("scala/runtime/BooleanRef")
- val srByteRef = ClassBType("scala/runtime/ByteRef")
- val srCharRef = ClassBType("scala/runtime/CharRef")
- val srIntRef = ClassBType("scala/runtime/IntRef")
- val srLongRef = ClassBType("scala/runtime/LongRef")
- val srFloatRef = ClassBType("scala/runtime/FloatRef")
- val srDoubleRef = ClassBType("scala/runtime/DoubleRef")
/**
- * Map from type kinds to the Java reference types.
- * Useful when pushing class literals onto the operand stack (ldc instruction taking a class
- * literal).
- * @see Predef.classOf
- * @see genConstant()
- *
- * TODO @lry rename to "boxedClassOfPrimitive" or so, check usages
+ * Just a named pair, used in CoreBTypes.asmBoxTo/asmUnboxTo.
*/
- val classLiteral = immutable.Map[BType, ClassBType](
- UNIT -> BOXED_UNIT,
- BOOL -> BOXED_BOOLEAN,
- BYTE -> BOXED_BYTE,
- SHORT -> BOXED_SHORT,
- CHAR -> BOXED_CHAR,
- INT -> BOXED_INT,
- LONG -> BOXED_LONG,
- FLOAT -> BOXED_FLOAT,
- DOUBLE -> BOXED_DOUBLE
- )
-
- case class MethodNameAndType(name: String, descriptor: String)
-
- val asmBoxTo: immutable.Map[BType, MethodNameAndType] = {
- Map(
- BOOL -> MethodNameAndType("boxToBoolean", "(Z)Ljava/lang/Boolean;" ) ,
- BYTE -> MethodNameAndType("boxToByte", "(B)Ljava/lang/Byte;" ) ,
- CHAR -> MethodNameAndType("boxToCharacter", "(C)Ljava/lang/Character;") ,
- SHORT -> MethodNameAndType("boxToShort", "(S)Ljava/lang/Short;" ) ,
- INT -> MethodNameAndType("boxToInteger", "(I)Ljava/lang/Integer;" ) ,
- LONG -> MethodNameAndType("boxToLong", "(J)Ljava/lang/Long;" ) ,
- FLOAT -> MethodNameAndType("boxToFloat", "(F)Ljava/lang/Float;" ) ,
- DOUBLE -> MethodNameAndType("boxToDouble", "(D)Ljava/lang/Double;" )
- )
- }
+ final case class MethodNameAndType(name: String, methodType: MethodBType)
- val asmUnboxTo: immutable.Map[BType, MethodNameAndType] = {
- Map(
- BOOL -> MethodNameAndType("unboxToBoolean", "(Ljava/lang/Object;)Z") ,
- BYTE -> MethodNameAndType("unboxToByte", "(Ljava/lang/Object;)B") ,
- CHAR -> MethodNameAndType("unboxToChar", "(Ljava/lang/Object;)C") ,
- SHORT -> MethodNameAndType("unboxToShort", "(Ljava/lang/Object;)S") ,
- INT -> MethodNameAndType("unboxToInt", "(Ljava/lang/Object;)I") ,
- LONG -> MethodNameAndType("unboxToLong", "(Ljava/lang/Object;)J") ,
- FLOAT -> MethodNameAndType("unboxToFloat", "(Ljava/lang/Object;)F") ,
- DOUBLE -> MethodNameAndType("unboxToDouble", "(Ljava/lang/Object;)D")
- )
- }
+ /**
+ * True if the current compilation unit is of a primitive class (scala.Boolean et al).
+ * Used only in assertions. Abstract here because its implementation depends on global.
+ */
+ def isCompilingPrimitive: Boolean
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
new file mode 100644
index 0000000000..0e2f938602
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -0,0 +1,396 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc
+package backend.jvm
+
+import scala.tools.asm
+
+/**
+ * This class mainly contains the method classBTypeFromSymbol, which extracts the necessary
+ * information from a symbol and its type to create the correpsonding ClassBType. It requires
+ * access to the compiler (global parameter).
+ *
+ * The mixin CoreBTypes defines core BTypes that are used in the backend. Building these BTypes
+ * uses classBTypeFromSymbol, hence requires access to the compiler (global).
+ *
+ * BTypesFromSymbols extends BTypes because the implementation of BTypes requires access to some
+ * of the core btypes. They are declared in BTypes as abstract members. Note that BTypes does
+ * not have access to the compiler instance.
+ */
+class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
+ import global._
+ import definitions._
+
+ val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global)
+ val bCodeAsmCommon: BCodeAsmCommon[global.type] = new BCodeAsmCommon(global)
+ import bCodeAsmCommon._
+
+ // Why the proxy, see documentation of class [[CoreBTypes]].
+ val coreBTypes = new CoreBTypesProxy[this.type](this)
+ import coreBTypes._
+
+ final def intializeCoreBTypes(): Unit = {
+ coreBTypes.setBTypes(new CoreBTypes[this.type](this))
+ }
+
+ def internalNameString(offset: Int, length: Int) = new String(global.chrs, offset, length)
+
+ protected val classBTypeFromInternalNameMap = {
+ global.perRunCaches.recordCache(collection.concurrent.TrieMap.empty[String, ClassBType])
+ }
+
+ /**
+ * Cache for the method classBTypeFromSymbol.
+ */
+ private val convertedClasses = perRunCaches.newMap[Symbol, ClassBType]()
+
+ // helpers that need access to global.
+ // TODO @lry create a separate component, they don't belong to BTypesFromSymbols
+
+ final val strMODULE_INSTANCE_FIELD = nme.MODULE_INSTANCE_FIELD.toString
+
+ private val primitiveCompilationUnits = Set(
+ "Unit.scala",
+ "Boolean.scala",
+ "Char.scala",
+ "Byte.scala",
+ "Short.scala",
+ "Int.scala",
+ "Float.scala",
+ "Long.scala",
+ "Double.scala"
+ )
+
+ /**
+ * True if the current compilation unit is of a primitive class (scala.Boolean et al).
+ * Used only in assertions.
+ */
+ def isCompilingPrimitive = {
+ primitiveCompilationUnits(currentUnit.source.file.name)
+ }
+
+ def isCompilingArray = {
+ currentUnit.source.file.name == "Array.scala"
+ }
+
+ // end helpers
+
+ /**
+ * The ClassBType for a class symbol `sym`.
+ */
+ final def classBTypeFromSymbol(classSym: Symbol): ClassBType = {
+ assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol")
+ assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym")
+ assert(
+ (!primitiveTypeMap.contains(classSym) || isCompilingPrimitive) &&
+ (classSym != NothingClass && classSym != NullClass),
+ s"Cannot create ClassBType for special class symbol ${classSym.fullName}")
+
+ convertedClasses.getOrElse(classSym, {
+ val internalName = classSym.javaBinaryName.toTypeName
+ // We first create and add the ClassBType to the hash map before computing its info. This
+ // allows initializing cylic dependencies, see the comment on variable ClassBType._info.
+ val classBType = new ClassBType(internalName.start, internalName.length)
+ convertedClasses(classSym) = classBType
+ setClassInfo(classSym, classBType)
+ })
+ }
+
+ private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = {
+ val superClassSym = if (classSym.isImplClass) ObjectClass else classSym.superClass
+ assert(
+ if (classSym == ObjectClass)
+ superClassSym == NoSymbol
+ else if (classSym.isInterface)
+ superClassSym == ObjectClass
+ else
+ // A ClassBType for a primitive class (scala.Boolean et al) is only created when compiling these classes.
+ ((superClassSym != NoSymbol) && !superClassSym.isInterface) || (isCompilingPrimitive && primitiveTypeMap.contains(classSym)),
+ s"Bad superClass for $classSym: $superClassSym"
+ )
+ val superClass = if (superClassSym == NoSymbol) None
+ else Some(classBTypeFromSymbol(superClassSym))
+
+ val interfaces = getSuperInterfaces(classSym).map(classBTypeFromSymbol)
+
+ val flags = javaFlags(classSym)
+
+ /* The InnerClass table of a class C must contain all nested classes of C, even if they are only
+ * declared but not otherwise referenced in C (from the bytecode or a method / field signature).
+ * We collect them here.
+ *
+ * Nested classes that are also referenced in C will be added to the innerClassBufferASM during
+ * code generation, but those duplicates will be eliminated when emitting the InnerClass
+ * attribute.
+ *
+ * Why doe we need to collect classes into innerClassBufferASM at all? To collect references to
+ * nested classes, but NOT nested in C, that are used within C.
+ */
+ val nestedClassSymbols = {
+ // The lambdalift phase lifts all nested classes to the enclosing class, so if we collect
+ // member classes right after lambdalift, we obtain all nested classes, including local and
+ // anonymous ones.
+ val nestedClasses = exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(classSym))
+
+ // If this is a top-level class, and it has a companion object, the member classes of the
+ // companion are added as members of the class. For example:
+ // class C { }
+ // object C {
+ // class D
+ // def f = { class E }
+ // }
+ // The class D is added as a member of class C. The reason is that the InnerClass attribute
+ // for D will containt class "C" and NOT the module class "C$" as the outer class of D.
+ // This is done by buildNestedInfo, the reason is Java compatibility, see comment in BTypes.
+ // For consistency, the InnerClass entry for D needs to be present in C - to Java it looks
+ // like D is a member of C, not C$.
+ val linkedClass = exitingPickler(classSym.linkedClassOfClass) // linkedCoC does not work properly in late phases
+ val companionModuleMembers = {
+ // phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes,
+ // not local classes of the companion module (E in the exmaple) that were lifted by lambdalift.
+ if (isTopLevelModuleClass(linkedClass)) exitingPickler(memberClassesOf(linkedClass))
+ else Nil
+ }
+
+ nestedClasses ++ companionModuleMembers
+ }
+
+ /**
+ * For nested java classes, the scala compiler creates both a class and a module (and therefore
+ * a module class) symbol. For example, in `class A { class B {} }`, the nestedClassSymbols
+ * for A contain both the class B and the module class B.
+ * Here we get rid of the module class B, making sure that the class B is present.
+ */
+ val nestedClassSymbolsNoJavaModuleClasses = nestedClassSymbols.filter(s => {
+ if (s.isJavaDefined && s.isModuleClass) {
+ // We could also search in nestedClassSymbols for s.linkedClassOfClass, but sometimes that
+ // returns NoSymbol, so it doesn't work.
+ val nb = nestedClassSymbols.count(mc => mc.name == s.name && mc.owner == s.owner)
+ assert(nb == 2, s"Java member module without member class: $s - $nestedClassSymbols")
+ false
+ } else true
+ })
+
+ val memberClasses = nestedClassSymbolsNoJavaModuleClasses.map(classBTypeFromSymbol)
+
+ val nestedInfo = buildNestedInfo(classSym)
+
+ classBType.info = ClassInfo(superClass, interfaces, flags, memberClasses, nestedInfo)
+ classBType
+ }
+
+ /**
+ * All interfaces implemented by a class, except for those inherited through the superclass.
+ *
+ * TODO @lry share code with GenASM
+ */
+ private def getSuperInterfaces(classSym: Symbol): List[Symbol] = {
+
+ // Additional interface parents based on annotations and other cues
+ def newParentForAnnotation(ann: AnnotationInfo): Symbol = ann.symbol match {
+ case RemoteAttr => RemoteInterfaceClass
+ case _ => NoSymbol
+ }
+
+ /**
+ * Drop redundant interfaces (which are implemented by some other parent) from the immediate
+ * parents. In other words, no two interfaces in the result are related by subtyping.
+ */
+ def dropRedundantInterfaces(lstIfaces: List[Symbol]): List[Symbol] = {
+ var rest = lstIfaces
+ var leaves = List.empty[Symbol]
+ while (!rest.isEmpty) {
+ val candidate = rest.head
+ val nonLeaf = leaves exists { lsym => lsym isSubClass candidate }
+ if (!nonLeaf) {
+ leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym })
+ }
+ rest = rest.tail
+ }
+
+ leaves
+ }
+
+ val superInterfaces0: List[Symbol] = classSym.mixinClasses
+ val superInterfaces = existingSymbols(superInterfaces0 ++ classSym.annotations.map(newParentForAnnotation)).distinct
+
+ assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}")
+ assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}")
+
+ dropRedundantInterfaces(superInterfaces)
+ }
+
+ private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = {
+ assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym")
+
+ val isNested = !innerClassSym.rawowner.isPackageClass
+ if (!isNested) None
+ else {
+ // See comment in BTypes, when is a class marked static in the InnerClass table.
+ val isStaticNestedClass = isOriginallyStaticOwner(innerClassSym.originalOwner)
+
+ // After lambdalift (which is where we are), the rawowoner field contains the enclosing class.
+ val enclosingClassSym = {
+ if (innerClassSym.isJavaDefined && innerClassSym.rawowner.isModuleClass) {
+ // Example java source: class C { static class D { } }
+ // The Scala compiler creates a class and a module symbol for C. Because D is a static
+ // nested class, the symbol for D is nested in the module class C (not in the class C).
+ // For the InnerClass attribute, we use the class symbol C, which represents the situation
+ // in the source code.
+
+ // Cannot use innerClassSym.isStatic: this method looks at the owner, which is a package
+ // at this pahse (after lambdalift, flatten).
+ assert(isOriginallyStaticOwner(innerClassSym.originalOwner), innerClassSym.originalOwner)
+
+ // phase travel for linkedCoC - does not always work in late phases
+ exitingPickler(innerClassSym.rawowner.linkedClassOfClass)
+ }
+ else innerClassSym.rawowner
+ }
+ val enclosingClass: ClassBType = classBTypeFromSymbol(enclosingClassSym)
+
+ val outerName: Option[String] = {
+ if (isAnonymousOrLocalClass(innerClassSym)) {
+ None
+ } else {
+ val outerName = innerClassSym.rawowner.javaBinaryName
+ // Java compatibility. See the big comment in BTypes that summarizes the InnerClass spec.
+ val outerNameModule = if (isTopLevelModuleClass(innerClassSym.rawowner)) outerName.dropModule
+ else outerName
+ Some(outerNameModule.toString)
+ }
+ }
+
+ val innerName: Option[String] = {
+ if (innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction) None
+ else Some(innerClassSym.rawname + innerClassSym.moduleSuffix) // moduleSuffix for module classes
+ }
+
+ Some(NestedInfo(enclosingClass, outerName, innerName, isStaticNestedClass))
+ }
+ }
+
+ /**
+ * True for module classes of package level objects. The backend will generate a mirror class for
+ * such objects.
+ */
+ final def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler {
+ // phase travel to pickler required for isNestedClass (looks at owner)
+ val r = sym.isModuleClass && !sym.isNestedClass
+ // The mixin phase adds the `lateMODULE` flag to trait implementation classes. Since the flag
+ // is late, it should not be visible here inside the time travel. We check this.
+ if (r) assert(!sym.isImplClass, s"isModuleClass should be false for impl class $sym")
+ r
+ }
+
+ /**
+ * True for module classes of modules that are top-level or owned only by objects. Module classes
+ * for such objects will get a MODULE$ flag and a corresponding static initializer.
+ */
+ final def isStaticModuleClass(sym: Symbol): Boolean = {
+ /* (1) Phase travel to to pickler is required to exclude implementation classes; they have the
+ * lateMODULEs after mixin, so isModuleClass would be true.
+ * (2) isStaticModuleClass is a source-level property. See comment on isOriginallyStaticOwner.
+ */
+ exitingPickler { // (1)
+ sym.isModuleClass &&
+ isOriginallyStaticOwner(sym.originalOwner) // (2)
+ }
+ }
+
+ // legacy, to be removed when the @remote annotation gets removed
+ final def isRemote(s: Symbol) = (s hasAnnotation definitions.RemoteAttr)
+ final def hasPublicBitSet(flags: Int) = ((flags & asm.Opcodes.ACC_PUBLIC) != 0)
+
+ /**
+ * Return the Java modifiers for the given symbol.
+ * Java modifiers for classes:
+ * - public, abstract, final, strictfp (not used)
+ * for interfaces:
+ * - the same as for classes, without 'final'
+ * for fields:
+ * - public, private (*)
+ * - static, final
+ * for methods:
+ * - the same as for fields, plus:
+ * - abstract, synchronized (not used), strictfp (not used), native (not used)
+ * for all:
+ * - deprecated
+ *
+ * (*) protected cannot be used, since inner classes 'see' protected members,
+ * and they would fail verification after lifted.
+ */
+ final def javaFlags(sym: Symbol): Int = {
+ // constructors of module classes should be private. introduced in b06edbc, probably to prevent
+ // creating module instances from java. for nested modules, the constructor needs to be public
+ // since they are created by the outer class and stored in a field. a java client can create
+ // new instances via outerClassInstance.new InnerModuleClass$().
+ // TODO: do this early, mark the symbol private.
+ val privateFlag =
+ sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass(sym.owner))
+
+ // Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also
+ // added to modules and module classes, not anymore since 296b706).
+ // Note that the presence of the `FINAL` flag on a symbol does not correspond 1:1 to emitting
+ // ACC_FINAL in bytecode.
+ //
+ // Top-level modules are marked ACC_FINAL in bytecode (even without the FINAL flag). Nested
+ // objects don't get the flag to allow overriding (under -Yoverride-objects, SI-5676).
+ //
+ // For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't:
+ // Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
+ // "Another problem is that the specification allows aggressive
+ // optimization of final fields. Within a thread, it is permissible to
+ // reorder reads of a final field with those modifications of a final
+ // field that do not take place in the constructor."
+ //
+ // A var or lazy val which is marked final still has meaning to the
+ // scala compiler. The word final is heavily overloaded unfortunately;
+ // for us it means "not overridable". At present you can't override
+ // vars regardless; this may change.
+ //
+ // The logic does not check .isFinal (which checks flags for the FINAL flag,
+ // and includes symbols marked lateFINAL) instead inspecting rawflags so
+ // we can exclude lateFINAL. Such symbols are eligible for inlining, but to
+ // avoid breaking proxy software which depends on subclassing, we do not
+ // emit ACC_FINAL.
+
+ val finalFlag = (
+ (((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym))
+ && !sym.enclClass.isInterface
+ && !sym.isClassConstructor
+ && !sym.isMutable // lazy vals and vars both
+ )
+
+ // Primitives are "abstract final" to prohibit instantiation
+ // without having to provide any implementations, but that is an
+ // illegal combination of modifiers at the bytecode level so
+ // suppress final if abstract if present.
+ import asm.Opcodes._
+ GenBCode.mkFlags(
+ if (privateFlag) ACC_PRIVATE else ACC_PUBLIC,
+ if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0,
+ if (sym.isInterface) ACC_INTERFACE else 0,
+ if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0,
+ if (sym.isStaticMember) ACC_STATIC else 0,
+ if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
+ if (sym.isArtifact) ACC_SYNTHETIC else 0,
+ if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
+ if (sym.hasEnumFlag) ACC_ENUM else 0,
+ if (sym.isVarargsMethod) ACC_VARARGS else 0,
+ if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0,
+ if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0
+ )
+ }
+
+ def javaFieldFlags(sym: Symbol) = {
+ javaFlags(sym) | GenBCode.mkFlags(
+ if (sym hasAnnotation TransientAttr) asm.Opcodes.ACC_TRANSIENT else 0,
+ if (sym hasAnnotation VolatileAttr) asm.Opcodes.ACC_VOLATILE else 0,
+ if (sym.isMutable) 0 else asm.Opcodes.ACC_FINAL
+ )
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendStats.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendStats.scala
new file mode 100644
index 0000000000..4b9383c67c
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendStats.scala
@@ -0,0 +1,24 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc
+package backend.jvm
+
+import scala.reflect.internal.util.Statistics
+
+object BackendStats {
+ import Statistics.{newTimer, newSubTimer}
+ val bcodeTimer = newTimer("time in backend", "jvm")
+
+ val bcodeInitTimer = newSubTimer("bcode initialization", bcodeTimer)
+ val bcodeGenStat = newSubTimer("code generation", bcodeTimer)
+ val bcodeDceTimer = newSubTimer("dead code elimination", bcodeTimer)
+ val bcodeWriteTimer = newSubTimer("classfile writing", bcodeTimer)
+
+ def timed[T](timer: Statistics.Timer)(body: => T): T = {
+ val start = Statistics.startTimer(timer)
+ try body finally Statistics.stopTimer(timer, start)
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
new file mode 100644
index 0000000000..fac3c93be2
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
@@ -0,0 +1,293 @@
+package scala.tools.nsc
+package backend.jvm
+
+import scala.annotation.switch
+
+/**
+ * Core BTypes and some other definitions. The initialization of these definitions requies access
+ * to symbols / types (global).
+ *
+ * The symbols used to initialize the ClassBTypes may change from one compiler run to the next. To
+ * make sure the definitions are consistent with the symbols in the current run, the
+ * `intializeCoreBTypes` method in BTypesFromSymbols creates a new instance of CoreBTypes in each
+ * compiler run.
+ *
+ * The class BTypesFromSymbols does not directly reference CoreBTypes, but CoreBTypesProxy. The
+ * reason is that having a `var bTypes: CoreBTypes` would not allow `import bTypes._`. Instead, the
+ * proxy class holds a `CoreBTypes` in a variable field and forwards to this instance.
+ *
+ * The definitions in `CoreBTypes` need to be lazy vals to break an initialization cycle. When
+ * creating a new instance to assign to the proxy, the `classBTypeFromSymbol` invoked in the
+ * constructor will actucally go through the proxy. The lazy vals make sure the instance is assigned
+ * in the proxy before the fields are initialized.
+ *
+ * Note: if we did not re-create the core BTypes on each compiler run, BType.classBTypeFromInternalNameMap
+ * could not be a perRunCache anymore: the classes defeined here need to be in that map, they are
+ * added when the ClassBTypes are created. The per run cache removes them, so they would be missing
+ * in the second run.
+ */
+class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
+ import bTypes._
+ import global._
+ import rootMirror.{requiredClass, getClassIfDefined}
+ import definitions._
+
+ /**
+ * Maps primitive types to their corresponding PrimitiveBType. The map is defined lexically above
+ * the first use of `classBTypeFromSymbol` because that method looks at the map.
+ */
+ lazy val primitiveTypeMap: Map[Symbol, PrimitiveBType] = Map(
+ UnitClass -> UNIT,
+ BooleanClass -> BOOL,
+ CharClass -> CHAR,
+ ByteClass -> BYTE,
+ ShortClass -> SHORT,
+ IntClass -> INT,
+ LongClass -> LONG,
+ FloatClass -> FLOAT,
+ DoubleClass -> DOUBLE
+ )
+
+ lazy val BOXED_UNIT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Void])
+ lazy val BOXED_BOOLEAN : ClassBType = classBTypeFromSymbol(BoxedBooleanClass)
+ lazy val BOXED_BYTE : ClassBType = classBTypeFromSymbol(BoxedByteClass)
+ lazy val BOXED_SHORT : ClassBType = classBTypeFromSymbol(BoxedShortClass)
+ lazy val BOXED_CHAR : ClassBType = classBTypeFromSymbol(BoxedCharacterClass)
+ lazy val BOXED_INT : ClassBType = classBTypeFromSymbol(BoxedIntClass)
+ lazy val BOXED_LONG : ClassBType = classBTypeFromSymbol(BoxedLongClass)
+ lazy val BOXED_FLOAT : ClassBType = classBTypeFromSymbol(BoxedFloatClass)
+ lazy val BOXED_DOUBLE : ClassBType = classBTypeFromSymbol(BoxedDoubleClass)
+
+ /**
+ * Map from primitive types to their boxed class type. Useful when pushing class literals onto the
+ * operand stack (ldc instruction taking a class literal), see genConstant.
+ */
+ lazy val boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = Map(
+ UNIT -> BOXED_UNIT,
+ BOOL -> BOXED_BOOLEAN,
+ BYTE -> BOXED_BYTE,
+ SHORT -> BOXED_SHORT,
+ CHAR -> BOXED_CHAR,
+ INT -> BOXED_INT,
+ LONG -> BOXED_LONG,
+ FLOAT -> BOXED_FLOAT,
+ DOUBLE -> BOXED_DOUBLE
+ )
+
+ lazy val boxedClasses: Set[ClassBType] = boxedClassOfPrimitive.values.toSet
+
+ /**
+ * Maps the method symbol for a box method to the boxed type of the result. For example, the
+ * method symbol for `Byte.box()` is mapped to the ClassBType `java/lang/Byte`.
+ */
+ lazy val boxResultType: Map[Symbol, ClassBType] = {
+ for ((valueClassSym, boxMethodSym) <- currentRun.runDefinitions.boxMethod)
+ yield boxMethodSym -> boxedClassOfPrimitive(primitiveTypeMap(valueClassSym))
+ }
+
+ /**
+ * Maps the method symbol for an unbox method to the primitive type of the result.
+ * For example, the method symbol for `Byte.unbox()`) is mapped to the PrimitiveBType BYTE. */
+ lazy val unboxResultType: Map[Symbol, PrimitiveBType] = {
+ for ((valueClassSym, unboxMethodSym) <- currentRun.runDefinitions.unboxMethod)
+ yield unboxMethodSym -> primitiveTypeMap(valueClassSym)
+ }
+
+ /*
+ * RT_NOTHING and RT_NULL exist at run-time only. They are the bytecode-level manifestation (in
+ * method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs.
+ *
+ * Therefore, when RT_NOTHING or RT_NULL are to be emitted, a mapping is needed: the internal
+ * names of NothingClass and NullClass can't be emitted as-is.
+ * TODO @lry Once there's a 2.11.3 starr, use the commented argument list. The current starr crashes on the type literal `scala.runtime.Nothing$`
+ */
+ lazy val RT_NOTHING : ClassBType = classBTypeFromSymbol(rootMirror.getRequiredClass("scala.runtime.Nothing$")) // (requiredClass[scala.runtime.Nothing$])
+ lazy val RT_NULL : ClassBType = classBTypeFromSymbol(rootMirror.getRequiredClass("scala.runtime.Null$")) // (requiredClass[scala.runtime.Null$])
+
+ lazy val ObjectReference : ClassBType = classBTypeFromSymbol(ObjectClass)
+ lazy val objArrayReference : ArrayBType = ArrayBType(ObjectReference)
+
+ lazy val StringReference : ClassBType = classBTypeFromSymbol(StringClass)
+ lazy val StringBuilderReference : ClassBType = classBTypeFromSymbol(StringBuilderClass)
+ lazy val ThrowableReference : ClassBType = classBTypeFromSymbol(ThrowableClass)
+ lazy val jlCloneableReference : ClassBType = classBTypeFromSymbol(JavaCloneableClass) // java/lang/Cloneable
+ lazy val jlNPEReference : ClassBType = classBTypeFromSymbol(NullPointerExceptionClass) // java/lang/NullPointerException
+ lazy val jioSerializableReference : ClassBType = classBTypeFromSymbol(JavaSerializableClass) // java/io/Serializable
+ lazy val scalaSerializableReference : ClassBType = classBTypeFromSymbol(SerializableClass) // scala/Serializable
+ lazy val classCastExceptionReference : ClassBType = classBTypeFromSymbol(ClassCastExceptionClass) // java/lang/ClassCastException
+
+ lazy val srBooleanRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BooleanRef])
+ lazy val srByteRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.ByteRef])
+ lazy val srCharRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.CharRef])
+ lazy val srIntRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.IntRef])
+ lazy val srLongRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LongRef])
+ lazy val srFloatRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.FloatRef])
+ lazy val srDoubleRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.DoubleRef])
+
+ lazy val hashMethodSym: Symbol = getMember(ScalaRunTimeModule, nme.hash_)
+
+ // TODO @lry avoiding going through through missingHook for every line in the REPL: https://github.com/scala/scala/commit/8d962ed4ddd310cc784121c426a2e3f56a112540
+ lazy val AndroidParcelableInterface : Symbol = getClassIfDefined("android.os.Parcelable")
+ lazy val AndroidCreatorClass : Symbol = getClassIfDefined("android.os.Parcelable$Creator")
+
+ lazy val BeanInfoAttr: Symbol = requiredClass[scala.beans.BeanInfo]
+
+ /* The Object => String overload. */
+ lazy val String_valueOf: Symbol = {
+ getMember(StringModule, nme.valueOf) filter (sym => sym.info.paramTypes match {
+ case List(pt) => pt.typeSymbol == ObjectClass
+ case _ => false
+ })
+ }
+
+ // scala.FunctionX and scala.runtim.AbstractFunctionX
+ lazy val FunctionReference : Vector[ClassBType] = (0 to MaxFunctionArity).map(i => classBTypeFromSymbol(FunctionClass(i)))(collection.breakOut)
+ lazy val AbstractFunctionReference : Vector[ClassBType] = (0 to MaxFunctionArity).map(i => classBTypeFromSymbol(AbstractFunctionClass(i)))(collection.breakOut)
+ lazy val AbstractFunctionArityMap : Map[ClassBType, Int] = AbstractFunctionReference.zipWithIndex.toMap
+
+ lazy val PartialFunctionReference : ClassBType = classBTypeFromSymbol(PartialFunctionClass)
+ lazy val AbstractPartialFunctionReference : ClassBType = classBTypeFromSymbol(AbstractPartialFunctionClass)
+
+ lazy val BoxesRunTime: ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])
+
+ /**
+ * Methods in scala.runtime.BoxesRuntime
+ */
+ lazy val asmBoxTo : Map[BType, MethodNameAndType] = Map(
+ BOOL -> MethodNameAndType("boxToBoolean", MethodBType(List(BOOL), BOXED_BOOLEAN)),
+ BYTE -> MethodNameAndType("boxToByte", MethodBType(List(BYTE), BOXED_BYTE)),
+ CHAR -> MethodNameAndType("boxToCharacter", MethodBType(List(CHAR), BOXED_CHAR)),
+ SHORT -> MethodNameAndType("boxToShort", MethodBType(List(SHORT), BOXED_SHORT)),
+ INT -> MethodNameAndType("boxToInteger", MethodBType(List(INT), BOXED_INT)),
+ LONG -> MethodNameAndType("boxToLong", MethodBType(List(LONG), BOXED_LONG)),
+ FLOAT -> MethodNameAndType("boxToFloat", MethodBType(List(FLOAT), BOXED_FLOAT)),
+ DOUBLE -> MethodNameAndType("boxToDouble", MethodBType(List(DOUBLE), BOXED_DOUBLE))
+ )
+
+ lazy val asmUnboxTo: Map[BType, MethodNameAndType] = Map(
+ BOOL -> MethodNameAndType("unboxToBoolean", MethodBType(List(ObjectReference), BOOL)),
+ BYTE -> MethodNameAndType("unboxToByte", MethodBType(List(ObjectReference), BYTE)),
+ CHAR -> MethodNameAndType("unboxToChar", MethodBType(List(ObjectReference), CHAR)),
+ SHORT -> MethodNameAndType("unboxToShort", MethodBType(List(ObjectReference), SHORT)),
+ INT -> MethodNameAndType("unboxToInt", MethodBType(List(ObjectReference), INT)),
+ LONG -> MethodNameAndType("unboxToLong", MethodBType(List(ObjectReference), LONG)),
+ FLOAT -> MethodNameAndType("unboxToFloat", MethodBType(List(ObjectReference), FLOAT)),
+ DOUBLE -> MethodNameAndType("unboxToDouble", MethodBType(List(ObjectReference), DOUBLE))
+ )
+
+ lazy val typeOfArrayOp: Map[Int, BType] = {
+ import scalaPrimitives._
+ Map(
+ (List(ZARRAY_LENGTH, ZARRAY_GET, ZARRAY_SET) map (_ -> BOOL)) ++
+ (List(BARRAY_LENGTH, BARRAY_GET, BARRAY_SET) map (_ -> BYTE)) ++
+ (List(SARRAY_LENGTH, SARRAY_GET, SARRAY_SET) map (_ -> SHORT)) ++
+ (List(CARRAY_LENGTH, CARRAY_GET, CARRAY_SET) map (_ -> CHAR)) ++
+ (List(IARRAY_LENGTH, IARRAY_GET, IARRAY_SET) map (_ -> INT)) ++
+ (List(LARRAY_LENGTH, LARRAY_GET, LARRAY_SET) map (_ -> LONG)) ++
+ (List(FARRAY_LENGTH, FARRAY_GET, FARRAY_SET) map (_ -> FLOAT)) ++
+ (List(DARRAY_LENGTH, DARRAY_GET, DARRAY_SET) map (_ -> DOUBLE)) ++
+ (List(OARRAY_LENGTH, OARRAY_GET, OARRAY_SET) map (_ -> ObjectReference)) : _*
+ )
+ }
+}
+
+/**
+ * This trait make some core BTypes availalbe that don't depend on a Global instance. Some core
+ * BTypes are required to be accessible in the BTypes trait, which does not have access to Global.
+ *
+ * BTypes cannot refer to CoreBTypesProxy because some of its members depend on global, for example
+ * the type Symbol in
+ * def primitiveTypeMap: Map[Symbol, PrimitiveBType]
+ */
+trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
+ val bTypes: BTS
+ import bTypes._
+
+ def boxedClasses: Set[ClassBType]
+
+ def RT_NOTHING : ClassBType
+ def RT_NULL : ClassBType
+
+ def ObjectReference : ClassBType
+ def jlCloneableReference : ClassBType
+ def jioSerializableReference : ClassBType
+}
+
+/**
+ * See comment in class [[CoreBTypes]].
+ */
+final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) extends CoreBTypesProxyGlobalIndependent[BTFS] {
+ import bTypes._
+ import global._
+
+ private[this] var _coreBTypes: CoreBTypes[bTypes.type] = _
+ def setBTypes(coreBTypes: CoreBTypes[BTFS]): Unit = {
+ _coreBTypes = coreBTypes.asInstanceOf[CoreBTypes[bTypes.type]]
+ }
+
+ def primitiveTypeMap: Map[Symbol, PrimitiveBType] = _coreBTypes.primitiveTypeMap
+
+ def BOXED_UNIT : ClassBType = _coreBTypes.BOXED_UNIT
+ def BOXED_BOOLEAN : ClassBType = _coreBTypes.BOXED_BOOLEAN
+ def BOXED_BYTE : ClassBType = _coreBTypes.BOXED_BYTE
+ def BOXED_SHORT : ClassBType = _coreBTypes.BOXED_SHORT
+ def BOXED_CHAR : ClassBType = _coreBTypes.BOXED_CHAR
+ def BOXED_INT : ClassBType = _coreBTypes.BOXED_INT
+ def BOXED_LONG : ClassBType = _coreBTypes.BOXED_LONG
+ def BOXED_FLOAT : ClassBType = _coreBTypes.BOXED_FLOAT
+ def BOXED_DOUBLE : ClassBType = _coreBTypes.BOXED_DOUBLE
+
+ def boxedClasses: Set[ClassBType] = _coreBTypes.boxedClasses
+
+ def boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = _coreBTypes.boxedClassOfPrimitive
+
+ def boxResultType: Map[Symbol, ClassBType] = _coreBTypes.boxResultType
+
+ def unboxResultType: Map[Symbol, PrimitiveBType] = _coreBTypes.unboxResultType
+
+ def RT_NOTHING : ClassBType = _coreBTypes.RT_NOTHING
+ def RT_NULL : ClassBType = _coreBTypes.RT_NULL
+
+ def ObjectReference : ClassBType = _coreBTypes.ObjectReference
+ def objArrayReference : ArrayBType = _coreBTypes.objArrayReference
+
+ def StringReference : ClassBType = _coreBTypes.StringReference
+ def StringBuilderReference : ClassBType = _coreBTypes.StringBuilderReference
+ def ThrowableReference : ClassBType = _coreBTypes.ThrowableReference
+ def jlCloneableReference : ClassBType = _coreBTypes.jlCloneableReference
+ def jlNPEReference : ClassBType = _coreBTypes.jlNPEReference
+ def jioSerializableReference : ClassBType = _coreBTypes.jioSerializableReference
+ def scalaSerializableReference : ClassBType = _coreBTypes.scalaSerializableReference
+ def classCastExceptionReference : ClassBType = _coreBTypes.classCastExceptionReference
+
+ def srBooleanRef : ClassBType = _coreBTypes.srBooleanRef
+ def srByteRef : ClassBType = _coreBTypes.srByteRef
+ def srCharRef : ClassBType = _coreBTypes.srCharRef
+ def srIntRef : ClassBType = _coreBTypes.srIntRef
+ def srLongRef : ClassBType = _coreBTypes.srLongRef
+ def srFloatRef : ClassBType = _coreBTypes.srFloatRef
+ def srDoubleRef : ClassBType = _coreBTypes.srDoubleRef
+
+ def hashMethodSym: Symbol = _coreBTypes.hashMethodSym
+
+ def AndroidParcelableInterface : Symbol = _coreBTypes.AndroidParcelableInterface
+ def AndroidCreatorClass : Symbol = _coreBTypes.AndroidCreatorClass
+
+ def BeanInfoAttr: Symbol = _coreBTypes.BeanInfoAttr
+
+ def String_valueOf: Symbol = _coreBTypes.String_valueOf
+
+ def FunctionReference : Vector[ClassBType] = _coreBTypes.FunctionReference
+ def AbstractFunctionReference : Vector[ClassBType] = _coreBTypes.AbstractFunctionReference
+ def AbstractFunctionArityMap : Map[ClassBType, Int] = _coreBTypes.AbstractFunctionArityMap
+
+ def PartialFunctionReference : ClassBType = _coreBTypes.PartialFunctionReference
+ def AbstractPartialFunctionReference : ClassBType = _coreBTypes.AbstractPartialFunctionReference
+
+ def BoxesRunTime: ClassBType = _coreBTypes.BoxesRunTime
+
+ def asmBoxTo : Map[BType, MethodNameAndType] = _coreBTypes.asmBoxTo
+ def asmUnboxTo: Map[BType, MethodNameAndType] = _coreBTypes.asmUnboxTo
+
+ def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 2392033760..2593903b9d 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -26,6 +26,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
import icodes.opcodes._
import definitions._
+ val bCodeAsmCommon: BCodeAsmCommon[global.type] = new BCodeAsmCommon(global)
+ import bCodeAsmCommon._
+
// Strangely I can't find this in the asm code
// 255, but reserving 1 for "this"
final val MaximumJvmParameters = 254
@@ -621,7 +624,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
* That means non-member classes (anonymous). See Section 4.7.5 in the JVMS.
*/
def outerName(innerSym: Symbol): String = {
- if (innerSym.originalEnclosingMethod != NoSymbol)
+ if (isAnonymousOrLocalClass(innerSym))
null
else {
val outerName = javaName(innerSym.rawowner)
@@ -636,13 +639,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
else
innerSym.rawname + innerSym.moduleSuffix
- // add inner classes which might not have been referenced yet
- // TODO @lry according to the spec, all nested classes should be added, also local and
- // anonymous. This seems to add only member classes - or not? it's exitingErasure, so maybe
- // local / anonymous classes have been lifted by lambdalift. are they in the "decls" though?
- exitingErasure {
- for (sym <- List(csym, csym.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass)
- innerClassBuffer += m
+ // This collects all inner classes of csym, including local and anonymous: lambdalift makes
+ // them members of their enclosing class.
+ innerClassBuffer ++= exitingPhase(currentRun.lambdaliftPhase)(memberClassesOf(csym))
+
+ // Add members of the companion object (if top-level). why, see comment in BTypes.scala.
+ val linkedClass = exitingPickler(csym.linkedClassOfClass) // linkedCoC does not work properly in late phases
+ if (isTopLevelModule(linkedClass)) {
+ // phase travel to exitingPickler: this makes sure that memberClassesOf only sees member classes,
+ // not local classes that were lifted by lambdalift.
+ innerClassBuffer ++= exitingPickler(memberClassesOf(linkedClass))
}
val allInners: List[Symbol] = innerClassBuffer.toList filterNot deadCode.elidedClosures
@@ -656,7 +662,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
// sort them so inner classes succeed their enclosing class to satisfy the Eclipse Java compiler
for (innerSym <- allInners sortBy (_.name.length)) { // TODO why not sortBy (_.name.toString()) ??
val flagsWithFinal: Int = mkFlags(
- if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0,
+ // See comment in BTypes, when is a class marked static in the InnerClass table.
+ if (isOriginallyStaticOwner(innerSym.originalOwner)) asm.Opcodes.ACC_STATIC else 0,
javaFlags(innerSym),
if(isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag
) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED)
@@ -1222,10 +1229,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
null /* SourceDebugExtension */)
}
- val enclM = getEnclosingMethodAttribute()
- if(enclM != null) {
- val EnclMethodEntry(className, methodName, methodType) = enclM
- jclass.visitOuterClass(className, methodName, methodType.getDescriptor)
+ enclosingMethodAttribute(clasz.symbol, javaName, javaType(_).getDescriptor) match {
+ case Some(EnclosingMethodEntry(className, methodName, methodDescriptor)) =>
+ jclass.visitOuterClass(className, methodName, methodDescriptor)
+ case _ => ()
}
// typestate: entering mode with valid call sequences:
@@ -1286,45 +1293,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol)
}
- /**
- * @param owner internal name of the enclosing class of the class.
- *
- * @param name the name of the method that contains the class.
-
- * @param methodType the method that contains the class.
- */
- case class EnclMethodEntry(owner: String, name: String, methodType: asm.Type)
-
- /**
- * @return null if the current class is not internal to a method
- *
- * Quoting from JVMS 4.7.7 The EnclosingMethod Attribute
- * A class must have an EnclosingMethod attribute if and only if it is a local class or an anonymous class.
- * A class may have no more than one EnclosingMethod attribute.
- *
- */
- private def getEnclosingMethodAttribute(): EnclMethodEntry = { // JVMS 4.7.7
- var res: EnclMethodEntry = null
- val clazz = clasz.symbol
- val sym = clazz.originalEnclosingMethod
- if (sym.isMethod) {
- debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass))
- res = EnclMethodEntry(javaName(sym.enclClass), javaName(sym), javaType(sym))
- } else if (clazz.isAnonymousClass) {
- val enclClass = clazz.rawowner
- assert(enclClass.isClass, enclClass)
- val sym = enclClass.primaryConstructor
- if (sym == NoSymbol) {
- log("Ran out of room looking for an enclosing method for %s: no constructor here.".format(enclClass))
- } else {
- debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass))
- res = EnclMethodEntry(javaName(enclClass), javaName(sym), javaType(sym))
- }
- }
-
- res
- }
-
def genField(f: IField) {
debuglog("Adding field: " + f.symbol.fullName)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index 89866b7ce9..ba94a9c44c 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -11,8 +11,10 @@ package jvm
import scala.collection.{ mutable, immutable }
import scala.annotation.switch
+import scala.reflect.internal.util.Statistics
import scala.tools.asm
+import scala.tools.asm.tree.ClassNode
/*
* Prepare in-memory representations of classfiles using the ASM Tree API, and serialize them to disk.
@@ -46,6 +48,9 @@ import scala.tools.asm
abstract class GenBCode extends BCodeSyncAndTry {
import global._
+ import bTypes._
+ import coreBTypes._
+
val phaseName = "jvm"
override def newPhase(prev: Phase) = new BCodePhase(prev)
@@ -130,7 +135,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
return
}
else {
- try { visit(item) }
+ try { withCurrentUnit(item.cunit)(visit(item)) }
catch {
case ex: Throwable =>
ex.printStackTrace()
@@ -210,6 +215,14 @@ abstract class GenBCode extends BCodeSyncAndTry {
* - converting the plain ClassNode to byte array and placing it on queue-3
*/
class Worker2 {
+ def localOptimizations(classNode: ClassNode): Unit = {
+ def dce(): Boolean = BackendStats.timed(BackendStats.bcodeDceTimer) {
+ if (settings.YoptUnreachableCode) opt.LocalOpt.removeUnreachableCode(classNode)
+ else false
+ }
+
+ dce()
+ }
def run() {
while (true) {
@@ -219,8 +232,10 @@ abstract class GenBCode extends BCodeSyncAndTry {
return
}
else {
- try { addToQ3(item) }
- catch {
+ try {
+ localOptimizations(item.plain)
+ addToQ3(item)
+ } catch {
case ex: Throwable =>
ex.printStackTrace()
error(s"Error while emitting ${item.plain.name}\n${ex.getMessage}")
@@ -269,10 +284,13 @@ abstract class GenBCode extends BCodeSyncAndTry {
*
*/
override def run() {
+ val bcodeStart = Statistics.startTimer(BackendStats.bcodeTimer)
+ val initStart = Statistics.startTimer(BackendStats.bcodeInitTimer)
arrivalPos = 0 // just in case
scalaPrimitives.init()
- initBCodeTypes()
+ bTypes.intializeCoreBTypes()
+ Statistics.stopTimer(BackendStats.bcodeInitTimer, initStart)
// initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated.
bytecodeWriter = initBytecodeWriter(cleanup.getEntryPoints)
@@ -284,6 +302,7 @@ abstract class GenBCode extends BCodeSyncAndTry {
// closing output files.
bytecodeWriter.close()
+ Statistics.stopTimer(BackendStats.bcodeTimer, bcodeStart)
/* TODO Bytecode can be verified (now that all classfiles have been written to disk)
*
@@ -297,9 +316,6 @@ abstract class GenBCode extends BCodeSyncAndTry {
* (2) if requested, check-java-signatures, over and beyond the syntactic checks in `getGenericSignature()`
*
*/
-
- // clearing maps
- clearBCodeTypes()
}
/*
@@ -312,9 +328,15 @@ abstract class GenBCode extends BCodeSyncAndTry {
private def buildAndSendToDisk(needsOutFolder: Boolean) {
feedPipeline1()
+ val genStart = Statistics.startTimer(BackendStats.bcodeGenStat)
(new Worker1(needsOutFolder)).run()
+ Statistics.stopTimer(BackendStats.bcodeGenStat, genStart)
+
(new Worker2).run()
+
+ val writeStart = Statistics.startTimer(BackendStats.bcodeWriteTimer)
drainQ3()
+ Statistics.stopTimer(BackendStats.bcodeWriteTimer, writeStart)
}
@@ -385,3 +407,10 @@ abstract class GenBCode extends BCodeSyncAndTry {
} // end of class BCodePhase
} // end of class GenBCode
+
+object GenBCode {
+ def mkFlags(args: Int*) = args.foldLeft(0)(_ | _)
+
+ final val PublicStatic = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC
+ final val PublicStaticFinal = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL
+}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
new file mode 100644
index 0000000000..3acd2d6154
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
@@ -0,0 +1,190 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import scala.tools.asm.{Opcodes, MethodWriter, ClassWriter}
+import scala.tools.asm.tree.analysis.{Analyzer, BasicValue, BasicInterpreter}
+import scala.tools.asm.tree._
+import scala.collection.convert.decorateAsScala._
+import scala.collection.{ mutable => m }
+
+/**
+ * Intra-Method optimizations.
+ */
+object LocalOpt {
+ /**
+ * Remove unreachable instructions from all (non-abstract) methods.
+ *
+ * @param clazz The class whose methods are optimized
+ * @return `true` if unreachable code was elminated in some method, `false` otherwise.
+ */
+ def removeUnreachableCode(clazz: ClassNode): Boolean = {
+ clazz.methods.asScala.foldLeft(false) {
+ case (changed, method) => removeUnreachableCode(method, clazz.name) || changed
+ }
+ }
+
+ /**
+ * Remove unreachable code from a method.
+ * We rely on dead code elimination provided by the ASM framework, as described in the ASM User
+ * Guide (http://asm.ow2.org/index.html), Section 8.2.1. It runs a data flow analysis, which only
+ * computes Frame information for reachable instructions. Instructions for which no Frame data is
+ * available after the analyis are unreachable.
+ *
+ * TODO doc: it also removes empty handlers, unused local vars
+ *
+ * Returns `true` if dead code in `method` has been eliminated.
+ */
+ private def removeUnreachableCode(method: MethodNode, ownerClassName: String): Boolean = {
+ if (method.instructions.size == 0) return false // fast path for abstract methods
+
+ val codeRemoved = removeUnreachableCodeImpl(method, ownerClassName)
+
+ // unreachable-code also removes unused local variable nodes and empty exception handlers.
+ // This is required for correctness: such nodes are not allowed to refer to instruction offsets
+ // that don't exist (because they have been eliminated).
+ val localsRemoved = removeUnusedLocalVariableNodes(method)
+ val handlersRemoved = removeEmptyExceptionHandlers(method)
+
+ // When eliminating a handler, the catch block becomes unreachable. The recursive invocation
+ // removes these blocks.
+ // Note that invoking removeUnreachableCode*Impl* a second time is not enough: removing the dead
+ // catch block can render other handlers empty, which also have to be removed in turn.
+ if (handlersRemoved) removeUnreachableCode(method, ownerClassName)
+
+ // assert that we can leave local variable annotations as-is
+ def nullOrEmpty[T](l: java.util.List[T]) = l == null || l.isEmpty
+ assert(nullOrEmpty(method.visibleLocalVariableAnnotations), method.visibleLocalVariableAnnotations)
+ assert(nullOrEmpty(method.invisibleLocalVariableAnnotations), method.invisibleLocalVariableAnnotations)
+
+ codeRemoved || localsRemoved || handlersRemoved
+ }
+
+ private def removeUnreachableCodeImpl(method: MethodNode, ownerClassName: String): Boolean = {
+ val initialSize = method.instructions.size
+ if (initialSize == 0) return false
+
+ // The data flow analysis requires the maxLocals / maxStack fields of the method to be computed.
+ computeMaxLocalsMaxStack(method)
+ val a = new Analyzer[BasicValue](new BasicInterpreter)
+ a.analyze(ownerClassName, method)
+ val frames = a.getFrames
+
+ var i = 0
+ val itr = method.instructions.iterator()
+ while (itr.hasNext) {
+ val ins = itr.next()
+ // Don't remove label nodes: they might be referenced for example in a LocalVariableNode
+ if (frames(i) == null && !ins.isInstanceOf[LabelNode]) {
+ // Instruction iterators allow removing during iteration.
+ // Removing is O(1): instructions are doubly linked list elements.
+ itr.remove()
+ }
+ i += 1
+ }
+
+ method.instructions.size != initialSize
+ }
+
+ /**
+ * Remove exception handlers that cover empty code blocks from all methods of `clazz`.
+ * Returns `true` if any exception handler was eliminated.
+ */
+ def removeEmptyExceptionHandlers(clazz: ClassNode): Boolean = {
+ clazz.methods.asScala.foldLeft(false) {
+ case (changed, method) => removeEmptyExceptionHandlers(method) || changed
+ }
+ }
+
+ /**
+ * Remove exception handlers that cover empty code blocks. A block is considered empty if it
+ * consist only of labels, frames, line numbers, nops and gotos.
+ *
+ * Note that no instructions are eliminated.
+ *
+ * @return `true` if some exception handler was eliminated.
+ */
+ def removeEmptyExceptionHandlers(method: MethodNode): Boolean = {
+ /** True if there exists code between start and end. */
+ def containsExecutableCode(start: AbstractInsnNode, end: LabelNode): Boolean = {
+ start != end && (start.getOpcode match {
+ // FrameNode, LabelNode and LineNumberNode have opcode == -1.
+ case -1 | Opcodes.NOP | Opcodes.GOTO => containsExecutableCode(start.getNext, end)
+ case _ => true
+ })
+ }
+
+ val initialNumberHandlers = method.tryCatchBlocks.size
+ val handlersIter = method.tryCatchBlocks.iterator()
+ while(handlersIter.hasNext) {
+ val handler = handlersIter.next()
+ if (!containsExecutableCode(handler.start, handler.end)) handlersIter.remove()
+ }
+ method.tryCatchBlocks.size != initialNumberHandlers
+ }
+
+ /**
+ * Remove all non-parameter entries from the local variable table which denote variables that are
+ * not actually read or written.
+ *
+ * Note that each entry in the local variable table has a start, end and index. Two entries with
+ * the same index, but distinct start / end ranges are different variables, they may have not the
+ * same type or name.
+ *
+ * TODO: also re-allocate locals to occupy fewer slots after eliminating unused ones
+ */
+ def removeUnusedLocalVariableNodes(method: MethodNode): Boolean = {
+ def variableIsUsed(start: AbstractInsnNode, end: LabelNode, varIndex: Int): Boolean = {
+ start != end && (start match {
+ case v: VarInsnNode => v.`var` == varIndex
+ case _ => variableIsUsed(start.getNext, end, varIndex)
+ })
+ }
+
+ val initialNumVars = method.localVariables.size
+ val localsIter = method.localVariables.iterator()
+ // The parameters and `this` (for instance methods) have the lowest indices in the local variables
+ // table. Note that double / long fields occupy two slots, so we sum up the sizes. Since getSize
+ // returns 0 for void, we have to add `max 1`.
+ val paramsSize = scala.tools.asm.Type.getArgumentTypes(method.desc).map(_.getSize max 1).sum
+ val thisSize = if ((method.access & Opcodes.ACC_STATIC) == 0) 1 else 0
+ val endParamIndex = paramsSize + thisSize
+ while (localsIter.hasNext) {
+ val local = localsIter.next()
+ // parameters and `this` have the lowest indices, starting at 0
+ val used = local.index < endParamIndex || variableIsUsed(local.start, local.end, local.index)
+ if (!used)
+ localsIter.remove()
+ }
+ method.localVariables.size == initialNumVars
+ }
+
+
+ /**
+ * In order to run an Analyzer, the maxLocals / maxStack fields need to be available. The ASM
+ * framework only computes these values during bytecode generation.
+ *
+ * Sicne there's currently no better way, we run a bytecode generator on the method and extract
+ * the computed values. This required changes to the ASM codebase:
+ * - the [[MethodWriter]] class was made public
+ * - accessors for maxLocals / maxStack were added to the MethodWriter class
+ *
+ * We could probably make this faster (and allocate less memory) by hacking the ASM framework
+ * more: create a subclass of MethodWriter with a /dev/null byteVector. Another option would be
+ * to create a separate visitor for computing those values, duplicating the functionality from the
+ * MethodWriter.
+ */
+ private def computeMaxLocalsMaxStack(method: MethodNode) {
+ val cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
+ val excs = method.exceptions.asScala.toArray
+ val mw = cw.visitMethod(method.access, method.name, method.desc, method.signature, excs).asInstanceOf[MethodWriter]
+ method.accept(mw)
+ method.maxLocals = mw.getMaxLocals
+ method.maxStack = mw.getMaxStack
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
index 8df3969c49..351eb23c4c 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
@@ -283,7 +283,7 @@ abstract class Inliners extends SubComponent {
}
val tfa = new analysis.MTFAGrowable()
- tfa.stat = global.settings.Ystatistics.value
+ tfa.stat = global.settings.YstatisticsEnabled
val staleOut = new mutable.ListBuffer[BasicBlock]
val splicedBlocks = mutable.Set.empty[BasicBlock]
val staleIn = mutable.Set.empty[BasicBlock]
diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
index bddcf6567c..ac86dfd665 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala
@@ -861,7 +861,7 @@ trait JavaScanners extends ast.parser.ScannersCommon {
in = new JavaCharArrayReader(unit.source.content, !settings.nouescape.value, syntaxError)
init()
def error (pos: Int, msg: String) = reporter.error(pos, msg)
- def incompleteInputError(pos: Int, msg: String) = currentRun.reporting.incompleteInputError(pos, msg)
+ def incompleteInputError(pos: Int, msg: String) = currentRun.parsing.incompleteInputError(pos, msg)
def deprecationWarning(pos: Int, msg: String) = currentRun.reporting.deprecationWarning(pos, msg)
implicit def g2p(pos: Int): Position = Position.offset(unit.source, pos)
}
diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
index 6c592ead0d..5e4914fa83 100644
--- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala
@@ -30,6 +30,7 @@ abstract class AbstractReporter extends Reporter {
private def isVerbose = settings.verbose.value
private def noWarnings = settings.nowarnings.value
private def isPromptSet = settings.prompt.value
+ private def isDebug = settings.debug
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean) {
if (severity == INFO) {
@@ -46,7 +47,7 @@ abstract class AbstractReporter extends Reporter {
severity.count += 1
display(pos, msg, severity)
}
- else if (settings.debug) {
+ else if (isDebug) {
severity.count += 1
display(pos, "[ suppressed ] " + msg, severity)
}
@@ -57,6 +58,7 @@ abstract class AbstractReporter extends Reporter {
}
}
+
/** Logs a position and returns true if it was already logged.
* @note Two positions are considered identical for logging if they have the same point.
*/
diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
index 5b576a547d..3d688efae1 100644
--- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
@@ -13,8 +13,8 @@ import scala.reflect.internal.util._
* This describes the internal interface for issuing information, warnings and errors.
* The only abstract method in this class must be info0.
*
- * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter
- * This interface should be considered private to the compiler.
+ * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter,
+ * and remove this class.
*/
abstract class Reporter extends scala.reflect.internal.Reporter {
/** Informational messages. If `!force`, they may be suppressed. */
diff --git a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala
index d0b8fd70ed..6b339b2a6d 100644
--- a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala
@@ -7,20 +7,24 @@ package scala
package tools.nsc
package settings
+import scala.language.higherKinds
+
trait AbsScalaSettings {
self: AbsSettings =>
+ type MultiChoiceEnumeration <: Enumeration
+
type Setting <: AbsSetting
- type BooleanSetting <: Setting { type T = Boolean }
- type ChoiceSetting <: Setting { type T = String }
- type IntSetting <: Setting { type T = Int }
- type MultiStringSetting <: Setting { type T = List[String] }
- type MultiChoiceSetting <: Setting { type T = List[String] }
- type PathSetting <: Setting { type T = String }
- type PhasesSetting <: Setting { type T = List[String] }
- type StringSetting <: Setting { type T = String }
- type PrefixSetting <: Setting { type T = List[String] }
+ type BooleanSetting <: Setting { type T = Boolean }
+ type ChoiceSetting <: Setting { type T = String }
+ type IntSetting <: Setting { type T = Int }
+ type MultiStringSetting <: Setting { type T = List[String] }
+ type MultiChoiceSetting[E <: MultiChoiceEnumeration] <: Setting { type T <: E#ValueSet }
+ type PathSetting <: Setting { type T = String }
+ type PhasesSetting <: Setting { type T = List[String] }
+ type StringSetting <: Setting { type T = String }
+ type PrefixSetting <: Setting { type T = List[String] }
type OutputDirs
type OutputSetting <: Setting
@@ -29,7 +33,7 @@ trait AbsScalaSettings {
def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String): ChoiceSetting
def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]): IntSetting
def MultiStringSetting(name: String, helpArg: String, descr: String): MultiStringSetting
- def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: Option[() => Unit])(helper: MultiChoiceSetting => String): MultiChoiceSetting
+ def MultiChoiceSetting[E <: MultiChoiceEnumeration](name: String, helpArg: String, descr: String, domain: E, default: Option[List[String]]): MultiChoiceSetting[E]
def OutputSetting(outputDirs: OutputDirs, default: String): OutputSetting
def PathSetting(name: String, descr: String, default: String): PathSetting
def PhasesSetting(name: String, descr: String, default: String): PhasesSetting
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index f26192f88a..bbe21477cb 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -209,12 +209,11 @@ class MutableSettings(val errorFn: String => Unit)
def BooleanSetting(name: String, descr: String) = add(new BooleanSetting(name, descr))
def ChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: String) =
add(new ChoiceSetting(name, helpArg, descr, choices, default))
- def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]) = add(new IntSetting(name, descr, default, range, parser))
+ def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]) =
+ add(new IntSetting(name, descr, default, range, parser))
def MultiStringSetting(name: String, arg: String, descr: String) = add(new MultiStringSetting(name, arg, descr))
- def MultiChoiceSetting(name: String, helpArg: String, descr: String, choices: List[String], default: Option[() => Unit] = None)(
- helper: MultiChoiceSetting => String = _ => choices.mkString(f"$descr:%n", f"%n ", f"%n")
- ) =
- add(new MultiChoiceSetting(name, helpArg, descr, choices, default, helper))
+ def MultiChoiceSetting[E <: MultiChoiceEnumeration](name: String, helpArg: String, descr: String, domain: E, default: Option[List[String]] = None) =
+ add(new MultiChoiceSetting[E](name, helpArg, descr, domain, default))
def OutputSetting(outputDirs: OutputDirs, default: String) = add(new OutputSetting(outputDirs, default))
def PhasesSetting(name: String, descr: String, default: String = "") = add(new PhasesSetting(name, descr, default))
def StringSetting(name: String, arg: String, descr: String, default: String) = add(new StringSetting(name, arg, descr, default))
@@ -367,7 +366,7 @@ class MutableSettings(val errorFn: String => Unit)
def withDeprecationMessage(msg: String): this.type = { _deprecationMessage = Some(msg) ; this }
}
- /** A setting represented by an integer */
+ /** A setting represented by an integer. */
class IntSetting private[nsc](
name: String,
descr: String,
@@ -553,57 +552,193 @@ class MutableSettings(val errorFn: String => Unit)
}
}
- /** A setting that receives any combination of enumerated values,
- * including "_" to mean all values and "help" for verbose info.
- * In non-colonated mode, stops consuming args at the first
- * non-value, instead of at the next option, as for a multi-string.
+ /**
+ * Each [[MultiChoiceSetting]] takes a MultiChoiceEnumeration as domain. The enumeration may
+ * use the Choice class to define values, or simply use the default `Value` constructor:
+ *
+ * object SettingDomain extends MultiChoiceEnumeration { val arg1, arg2 = Value }
+ *
+ * Or
+ *
+ * object SettingDomain extends MultiChoiceEnumeration {
+ * val arg1 = Choice("arg1", "help")
+ * val arg2 = Choice("arg2", "help")
+ * }
+ *
+ * Choices with a non-empty `expandsTo` enable other options. Note that expanding choices are
+ * not present in the multiChoiceSetting.value set, only their expansion.
+ */
+ abstract class MultiChoiceEnumeration extends Enumeration {
+ case class Choice(name: String, help: String = "", expandsTo: List[Choice] = Nil) extends Val(name)
+ }
+
+ /**
+ * A Setting that collects string-valued settings from an enumerated domain.
+ * - These choices can be turned on or off: "-option:on,-off"
+ * - If an option is set both on and off, then the option is on
+ * - The choice "_" enables all choices that have not been explicitly disabled
+ *
+ * Arguments can be provided in colonated or non-colonated mode, i.e. "-option a b" or
+ * "-option:a,b". Note that arguments starting with a "-" can only be provided in colonated mode,
+ * otherwise they are interpreted as a new option.
+ *
+ * In non-colonated mode, the setting stops consuming arguments at the first non-choice,
+ * i.e. "-option a b c" only consumes "a" and "b" if "c" is not a valid choice.
+ *
+ * @param name command-line setting name, eg "-Xlint"
+ * @param helpArg help description for the kind of arguments it takes, eg "warning"
+ * @param descr description of the setting
+ * @param domain enumeration of choices implementing MultiChoice, or the string value is
+ * taken for the name
+ * @param default If Some(args), the default options if none are provided. If None, an
+ * error is printed if there are no arguments.
*/
- class MultiChoiceSetting private[nsc](
+ class MultiChoiceSetting[E <: MultiChoiceEnumeration] private[nsc](
name: String,
- arg: String,
+ helpArg: String,
descr: String,
- override val choices: List[String],
- val default: Option[() => Unit],
- helper: MultiChoiceSetting => String
- ) extends MultiStringSetting(name, s"_,$arg,-$arg", s"$descr: `_' for all, `$name:help' to list") {
+ val domain: E,
+ val default: Option[List[String]]
+ ) extends Setting(name, s"$descr: `_' for all, `$name:help' to list") with Clearable {
- private def badChoice(s: String, n: String) = errorFn(s"'$s' is not a valid choice for '$name'")
- private def choosing = choices.nonEmpty
- private def isChoice(s: String) = (s == "_") || (choices contains (s stripPrefix "-"))
+ withHelpSyntax(s"$name:<_,$helpArg,-$helpArg>")
- private var sawHelp = false
- private var sawAll = false
- private val adderAll = () => sawAll = true
- private val noargs = () => errorFn(s"'$name' requires an option. See '$name:help'.")
+ object ChoiceOrVal {
+ def unapply(a: domain.Value): Option[(String, String, List[domain.Choice])] = a match {
+ case c: domain.Choice => Some((c.name, c.help, c.expandsTo))
+ case v: domain.Value => Some((v.toString, "", Nil))
+ }
+ }
+
+ type T = domain.ValueSet
+ protected var v: T = domain.ValueSet.empty
+
+ // Explicitly enabled or disabled. Yeas may contain expanding options, nays may not.
+ private var yeas = domain.ValueSet.empty
+ private var nays = domain.ValueSet.empty
+
+ // Asked for help
+ private var sawHelp = false
+ // Wildcard _ encountered
+ private var sawAll = false
+
+ private def badChoice(s: String) = errorFn(s"'$s' is not a valid choice for '$name'")
+ private def isChoice(s: String) = (s == "_") || (choices contains pos(s))
+
+ private def pos(s: String) = s stripPrefix "-"
+ private def isPos(s: String) = !(s startsWith "-")
+
+ override val choices: List[String] = domain.values.toList map {
+ case ChoiceOrVal(name, _, _) => name
+ }
+
+ def descriptions: List[String] = domain.values.toList map {
+ case ChoiceOrVal(_, "", x :: xs) => "Enables the options "+ (x :: xs).map(_.name).mkString(", ")
+ case ChoiceOrVal(_, descr, _) => descr
+ case _ => ""
+ }
- override protected def tts(args: List[String], halting: Boolean) = {
+ /** (Re)compute from current yeas, nays, wildcard status. */
+ def compute() = {
+ def simple(v: domain.Value) = v match {
+ case ChoiceOrVal(_, _, Nil) => true
+ case _ => false
+ }
+
+ /**
+ * Expand an expanding option, if necessary recursively. Expanding options are not included in
+ * the result (consistent with "_", which is not in `value` either).
+ *
+ * Note: by precondition, options in nays are not expanding, they can only be leaves.
+ */
+ def expand(vs: domain.ValueSet): domain.ValueSet = vs flatMap {
+ case c @ ChoiceOrVal(_, _, Nil) => domain.ValueSet(c)
+ case ChoiceOrVal(_, _, others) => expand(domain.ValueSet(others: _*))
+ }
+
+ // yeas from _ or expansions are weak: an explicit nay will disable them
+ val weakYeas = if (sawAll) domain.values filter simple else expand(yeas filterNot simple)
+ value = (yeas filter simple) | (weakYeas &~ nays)
+ }
+
+ /** Add a named choice to the multichoice value. */
+ def add(arg: String) = arg match {
+ case _ if !isChoice(arg) =>
+ badChoice(arg)
+ case "_" =>
+ sawAll = true
+ compute()
+ case _ if isPos(arg) =>
+ yeas += domain withName arg
+ compute()
+ case _ =>
+ val choice = domain withName pos(arg)
+ choice match {
+ case ChoiceOrVal(_, _, _ :: _) => errorFn(s"'${pos(arg)}' cannot be negated, it enables other arguments")
+ case _ =>
+ }
+ nays += choice
+ compute()
+ }
+
+ def tryToSet(args: List[String]) = tryToSetArgs(args, halting = true)
+ override def tryToSetColon(args: List[String]) = tryToSetArgs(args, halting = false)
+ override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) // used from ide
+
+ /** Try to set args, handling "help" and default.
+ * The "halting" parameter means args were "-option a b c -else" so halt
+ * on "-else" or other non-choice. Otherwise, args were "-option:a,b,c,d",
+ * so process all and report non-choices as errors.
+ * @param args args to process
+ * @param halting stop on non-arg
+ */
+ private def tryToSetArgs(args: List[String], halting: Boolean) = {
val added = collection.mutable.ListBuffer.empty[String]
+
def tryArg(arg: String) = arg match {
- case "_" if choosing => addAll()
- case "help" if choosing => sawHelp = true
- case s if !choosing || isChoice(s) => added += s
- case s => badChoice(s, name)
+ case "help" => sawHelp = true
+ case s if isChoice(s) => added += s // this case also adds "_"
+ case s => badChoice(s)
}
- def stoppingAt(arg: String) = (arg startsWith "-") || (choosing && !isChoice(arg))
def loop(args: List[String]): List[String] = args match {
- case arg :: _ if halting && stoppingAt(arg) => args
- case arg :: rest => tryArg(arg) ; loop(rest)
- case Nil => Nil
+ case arg :: _ if halting && (!isPos(arg) || !isChoice(arg)) => args
+ case arg :: rest => tryArg(arg) ; loop(rest)
+ case Nil => Nil
}
val rest = loop(args)
- if (rest.size == args.size)
- (default getOrElse noargs)() // if no arg consumed, trigger default action or error
- else
- value ++= added.toList // update all new settings at once
+
+ // if no arg consumed, use defaults or error; otherwise, add what they added
+ if (rest.size == args.size) default match {
+ case Some(defaults) => defaults foreach add
+ case None => errorFn(s"'$name' requires an option. See '$name:help'.")
+ } else {
+ added foreach add
+ }
+
Some(rest)
}
+ def contains(choice: domain.Value): Boolean = value contains choice
+
def isHelping: Boolean = sawHelp
- def help: String = helper(this)
- def addAll(): Unit = (default getOrElse adderAll)()
- // the semantics is: s is enabled, i.e., either s or (_ but not -s)
- override def contains(s: String) = isChoice(s) && (value contains s) || (sawAll && !(value contains s"-$s"))
+ def help: String = {
+ val choiceLength = choices.map(_.length).max + 1
+ val formatStr = s" %-${choiceLength}s %s"
+ choices.zipAll(descriptions, "", "").map {
+ case (arg, descr) => formatStr.format(arg, descr)
+ } mkString (f"$descr%n", f"%n", "")
+ }
+
+ def clear(): Unit = {
+ v = domain.ValueSet.empty
+ yeas = domain.ValueSet.empty
+ nays = domain.ValueSet.empty
+ sawAll = false
+ sawHelp = false
+ }
+ def unparse: List[String] = value.toList map (s => s"$name:$s")
+ def contains(s: String) = domain.values.find(_.toString == s).exists(value.contains)
}
/** A setting that accumulates all strings supplied to it,
@@ -619,15 +754,15 @@ class MutableSettings(val errorFn: String => Unit)
def appendToValue(str: String) = value ++= List(str)
// try to set. halting means halt at first non-arg
- protected def tts(args: List[String], halting: Boolean) = {
+ protected def tryToSetArgs(args: List[String], halting: Boolean) = {
def loop(args: List[String]): List[String] = args match {
case arg :: rest => if (halting && (arg startsWith "-")) args else { appendToValue(arg) ; loop(rest) }
case Nil => Nil
}
Some(loop(args))
}
- def tryToSet(args: List[String]) = tts(args, halting = true)
- override def tryToSetColon(args: List[String]) = tts(args, halting = false)
+ def tryToSet(args: List[String]) = tryToSetArgs(args, halting = true)
+ override def tryToSetColon(args: List[String]) = tryToSetArgs(args, halting = false)
override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) // used from ide
def clear(): Unit = (v = Nil)
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 8e69598614..466e397dd7 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -45,7 +45,7 @@ trait ScalaSettings extends AbsScalaSettings
def infoSettings = List[Setting](version, help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph)
/** Any -multichoice:help? Nicer if any option could report that it had help to offer. */
- private def multihelp = allSettings exists { case s: MultiChoiceSetting => s.isHelping case _ => false }
+ private def multihelp = allSettings exists { case s: MultiChoiceSetting[_] => s.isHelping case _ => false }
/** Is an info setting set? */
def isInfo = (infoSettings exists (_.isSetByUser)) || multihelp
@@ -69,28 +69,23 @@ trait ScalaSettings extends AbsScalaSettings
// Would be nice to build this dynamically from scala.languageFeature.
// The two requirements: delay error checking until you have symbols, and let compiler command build option-specific help.
+ object languageFeatures extends MultiChoiceEnumeration {
+ val dynamics = Choice("dynamics", "Allow direct or indirect subclasses of scala.Dynamic")
+ val postfixOps = Choice("postfixOps", "Allow postfix operator notation, such as `1 to 10 toList'")
+ val reflectiveCalls = Choice("reflectiveCalls", "Allow reflective access to members of structural types")
+ val implicitConversions = Choice("implicitConversions", "Allow definition of implicit functions called views")
+ val higherKinds = Choice("higherKinds", "Allow higher-kinded types")
+ val existentials = Choice("existentials", "Existential types (besides wildcard types) can be written and inferred")
+ val macros = Choice("experimental.macros", "Allow macro defintion (besides implementation and application)")
+ }
val language = {
- val features = List(
- "dynamics" -> "Allow direct or indirect subclasses of scala.Dynamic",
- "postfixOps" -> "Allow postfix operator notation, such as `1 to 10 toList'",
- "reflectiveCalls" -> "Allow reflective access to members of structural types",
- "implicitConversions" -> "Allow definition of implicit functions called views",
- "higherKinds" -> "Allow higher-kinded types", // "Ask Adriaan, but if you have to ask..."
- "existentials" -> "Existential types (besides wildcard types) can be written and inferred",
- "experimental.macros" -> "Allow macro defintion (besides implementation and application)"
- )
val description = "Enable or disable language features"
MultiChoiceSetting(
name = "-language",
helpArg = "feature",
descr = description,
- choices = features map (_._1)
- ) { s =>
- val helpline: ((String, String)) => String = {
- case (name, text) => f" $name%-25s $text%n"
- }
- features map helpline mkString (f"$description:%n", "", f"%n")
- }
+ domain = languageFeatures
+ )
}
/*
@@ -193,7 +188,6 @@ trait ScalaSettings extends AbsScalaSettings
val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "")
val Ygenasmp = StringSetting ("-Ygen-asmp", "dir", "Generate a parallel output directory of .asmp files (ie ASM Textifier output).", "")
val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "")
- val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (scala.reflect.internal.util.Statistics.enabled = _)
val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat
val stopBefore = PhasesSetting ("-Ystop-before", "Stop before")
val Yrangepos = BooleanSetting ("-Yrangepos", "Use range positions for syntax trees.")
@@ -215,8 +209,48 @@ trait ScalaSettings extends AbsScalaSettings
// the current standard is "inline" but we are moving towards "method"
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "inline")
+ object YoptChoices extends MultiChoiceEnumeration {
+ val unreachableCode = Choice("unreachable-code", "Eliminate unreachable code")
+
+ val lNone = Choice("l:none", "Don't enable any optimizations")
+
+ private val defaultChoices = List(unreachableCode)
+ val lDefault = Choice("l:default", "Enable default optimizations: "+ defaultChoices.mkString(","), expandsTo = defaultChoices)
+
+ private val methodChoices = List(lDefault)
+ val lMethod = Choice("l:method", "Intra-method optimizations: "+ methodChoices.mkString(","), expandsTo = methodChoices)
+
+ private val projectChoices = List(lMethod)
+ val lProject = Choice("l:project", "Cross-method optimizations within the current project: "+ projectChoices.mkString(","), expandsTo = projectChoices)
+
+ private val classpathChoices = List(lProject)
+ val lClasspath = Choice("l:classpath", "Cross-method optmizations across the entire classpath: "+ classpathChoices.mkString(","), expandsTo = classpathChoices)
+ }
+
+ val Yopt = MultiChoiceSetting(
+ name = "-Yopt",
+ helpArg = "optimization",
+ descr = "Enable optimizations",
+ domain = YoptChoices)
+
+ def YoptUnreachableCode: Boolean = !Yopt.isSetByUser || Yopt.contains(YoptChoices.unreachableCode)
+
private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug."
+ object YstatisticsPhases extends MultiChoiceEnumeration { val parser, typer, patmat, erasure, cleanup, jvm = Value }
+ val Ystatistics = {
+ val description = "Print compiler statistics for specific phases"
+ MultiChoiceSetting(
+ name = "-Ystatistics",
+ helpArg = "phase",
+ descr = description,
+ domain = YstatisticsPhases,
+ default = Some(List("_"))
+ ) withPostSetHook { _ => scala.reflect.internal.util.Statistics.enabled = true }
+ }
+
+ def YstatisticsEnabled = Ystatistics.value.nonEmpty
+
/** Area-specific debug output.
*/
val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.")
diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala
index bec068b56a..c400e8c29c 100644
--- a/src/compiler/scala/tools/nsc/settings/Warnings.scala
+++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala
@@ -17,142 +17,94 @@ trait Warnings {
// Warning semantics.
val fatalWarnings = BooleanSetting("-Xfatal-warnings", "Fail the compilation if there are any warnings.")
- // These additional warnings are all so noisy as to be useless in their
- // present form, but have the potential to offer useful info.
- protected def allWarnings = lintWarnings ++ List(
- warnDeadCode,
- warnValueDiscard,
- warnNumericWiden,
- warnUnused, // SI-7712, SI-7707 warnUnused not quite ready for prime-time
- warnUnusedImport, // currently considered too noisy for general use
- warnValueOverrides // currently turned off as experimental
- )
- // These warnings should be pretty quiet unless you're doing
- // something inadvisable.
- protected def lintWarnings = List(
- warnInaccessible,
- warnNullaryOverride,
- warnNullaryUnit,
- warnAdaptedArgs,
- warnInferAny,
- warnMissingInterpolator,
- warnDocDetached,
- warnPrivateShadow,
- warnPolyImplicitOverload,
- warnOptionImplicit,
- warnDelayedInit,
- warnByNameRightAssociative,
- warnPackageObjectClasses,
- warnUnsoundMatch
- )
-
- // Individual warnings. They can be set with -Ywarn.
- private def nonlintflag(name: String, text: String): BooleanSetting = BooleanSetting(name, text)
-
- val warnDeadCode = nonlintflag("-Ywarn-dead-code",
- "Warn when dead code is identified.")
- val warnValueDiscard = nonlintflag("-Ywarn-value-discard",
- "Warn when non-Unit expression results are unused.")
- val warnNumericWiden = nonlintflag("-Ywarn-numeric-widen",
- "Warn when numerics are widened.")
- val warnUnused = nonlintflag("-Ywarn-unused",
- "Warn when local and private vals, vars, defs, and types are are unused")
- val warnUnusedImport = nonlintflag("-Ywarn-unused-import",
- "Warn when imports are unused")
-
- // Lint warnings that have no -Y avatar, created with new instead of the autoregistering factory method.
- // They evaluate true if set to true or else are unset but -Xlint is true
- private def lintflag(name: String, text: String): BooleanSetting =
- new BooleanSetting(name, text) {
- override def value = if (isSetByUser) super.value else xlint
- }
-
- val warnAdaptedArgs = lintflag("adapted-args",
- "Warn if an argument list is modified to match the receiver.")
- val warnNullaryUnit = lintflag("nullary-unit",
- "Warn when nullary methods return Unit.")
- val warnInaccessible = lintflag("inaccessible",
- "Warn about inaccessible types in method signatures.")
- val warnNullaryOverride = lintflag("nullary-override",
- "Warn when non-nullary `def f()' overrides nullary `def f'.")
- val warnInferAny = lintflag("infer-any",
- "Warn when a type argument is inferred to be `Any`.")
- val warnMissingInterpolator = lintflag("missing-interpolator",
- "A string literal appears to be missing an interpolator id.")
- val warnDocDetached = lintflag("doc-detached",
- "A ScalaDoc comment appears to be detached from its element.")
- val warnPrivateShadow = lintflag("private-shadow",
- "A private field (or class parameter) shadows a superclass field.")
- val warnPolyImplicitOverload = lintflag("poly-implicit-overload",
- "Parameterized overloaded implicit methods are not visible as view bounds")
- val warnOptionImplicit = lintflag("option-implicit",
- "Option.apply used implicit view.")
- val warnDelayedInit = lintflag("delayedinit-select",
- "Selecting member of DelayedInit")
- val warnByNameRightAssociative = lintflag("by-name-right-associative",
- "By-name parameter of right associative operator")
- val warnPackageObjectClasses = lintflag("package-object-classes",
- "Class or object defined in package object")
- val warnUnsoundMatch = lintflag("unsound-match",
- "Pattern match may not be typesafe")
+ // Non-lint warnings
+
+ val warnDeadCode = BooleanSetting("-Ywarn-dead-code", "Warn when dead code is identified.")
+ val warnValueDiscard = BooleanSetting("-Ywarn-value-discard", "Warn when non-Unit expression results are unused.")
+ val warnNumericWiden = BooleanSetting("-Ywarn-numeric-widen", "Warn when numerics are widened.")
+ // SI-7712, SI-7707 warnUnused not quite ready for prime-time
+ val warnUnused = BooleanSetting("-Ywarn-unused", "Warn when local and private vals, vars, defs, and types are are unused.")
+ // currently considered too noisy for general use
+ val warnUnusedImport = BooleanSetting("-Ywarn-unused-import", "Warn when imports are unused.")
// Experimental lint warnings that are turned off, but which could be turned on programmatically.
// These warnings are said to blind those who dare enable them.
// They are not activated by -Xlint and can't be enabled on the command line.
- val warnValueOverrides = {
- val flag = lintflag("value-overrides", "Generated value class method overrides an implementation")
+ val warnValueOverrides = { // currently turned off as experimental. creaded using constructor (new BS), so not available on the command line.
+ val flag = new BooleanSetting("value-overrides", "Generated value class method overrides an implementation")
flag.value = false
flag
}
- // The Xlint warning group.
- private val xlint = new BooleanSetting("-Zunused", "True if -Xlint or -Xlint:_")
- // On -Xlint or -Xlint:_, set xlint, otherwise set the lint warning unless already set true
- val lint = {
- val description = "Enable or disable specific warnings"
- val choices = (lintWarnings map (_.name)).sorted
- MultiChoiceSetting(
- name = "-Xlint",
- helpArg = "warning",
- descr = description,
- choices = choices,
- default = Some(() => xlint.value = true)
- ) { s =>
- def helpline(n: String) = lintWarnings.find(_.name == n).map(w => f" ${w.name}%-25s ${w.helpDescription}%n")
- choices flatMap (helpline(_)) mkString (f"$description:%n", "", f"%n")
- } withPostSetHook { x =>
- val Neg = "-"
- def setPolitely(b: BooleanSetting, v: Boolean) = if (!b.isSetByUser || !b) b.value = v
- def set(w: String, v: Boolean) = lintWarnings find (_.name == w) foreach (setPolitely(_, v))
- def propagate(ss: List[String]): Unit = ss match {
- case w :: rest => if (w startsWith Neg) set(w stripPrefix Neg, false) else set(w, true) ; propagate(rest)
- case Nil => ()
- }
- propagate(x.value)
- }
+ // Lint warnings
+
+ object LintWarnings extends MultiChoiceEnumeration {
+ class LintWarning(name: String, help: String, val yAliased: Boolean) extends Choice(name, help)
+ def LintWarning(name: String, help: String, yAliased: Boolean = false) = new LintWarning(name, help, yAliased)
+
+ val AdaptedArgs = LintWarning("adapted-args", "Warn if an argument list is modified to match the receiver.", true)
+ val NullaryUnit = LintWarning("nullary-unit", "Warn when nullary methods return Unit.", true)
+ val Inaccessible = LintWarning("inaccessible", "Warn about inaccessible types in method signatures.", true)
+ val NullaryOverride = LintWarning("nullary-override", "Warn when non-nullary `def f()' overrides nullary `def f'.", true)
+ val InferAny = LintWarning("infer-any", "Warn when a type argument is inferred to be `Any`.", true)
+ val MissingInterpolator = LintWarning("missing-interpolator", "A string literal appears to be missing an interpolator id.")
+ val DocDetached = LintWarning("doc-detached", "A ScalaDoc comment appears to be detached from its element.")
+ val PrivateShadow = LintWarning("private-shadow", "A private field (or class parameter) shadows a superclass field.")
+ val TypeParameterShadow = LintWarning("type-parameter-shadow", "A local type parameter shadows a type already in scope.")
+ val PolyImplicitOverload = LintWarning("poly-implicit-overload", "Parameterized overloaded implicit methods are not visible as view bounds.")
+ val OptionImplicit = LintWarning("option-implicit", "Option.apply used implicit view.")
+ val DelayedInitSelect = LintWarning("delayedinit-select", "Selecting member of DelayedInit")
+ val ByNameRightAssociative = LintWarning("by-name-right-associative", "By-name parameter of right associative operator.")
+ val PackageObjectClasses = LintWarning("package-object-classes", "Class or object defined in package object.")
+ val UnsoundMatch = LintWarning("unsound-match", "Pattern match may not be typesafe.")
+
+ def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]]
}
+ import LintWarnings._
+
+ def warnAdaptedArgs = lint contains AdaptedArgs
+ def warnNullaryUnit = lint contains NullaryUnit
+ def warnInaccessible = lint contains Inaccessible
+ def warnNullaryOverride = lint contains NullaryOverride
+ def warnInferAny = lint contains InferAny
+ def warnMissingInterpolator = lint contains MissingInterpolator
+ def warnDocDetached = lint contains DocDetached
+ def warnPrivateShadow = lint contains PrivateShadow
+ def warnTypeParameterShadow = lint contains TypeParameterShadow
+ def warnPolyImplicitOverload = lint contains PolyImplicitOverload
+ def warnOptionImplicit = lint contains OptionImplicit
+ def warnDelayedInit = lint contains DelayedInitSelect
+ def warnByNameRightAssociative = lint contains ByNameRightAssociative
+ def warnPackageObjectClasses = lint contains PackageObjectClasses
+ def warnUnsoundMatch = lint contains UnsoundMatch
// Lint warnings that are currently -Y, but deprecated in that usage
@deprecated("Use warnAdaptedArgs", since="2.11.2")
- val YwarnAdaptedArgs = BooleanSetting("-Ywarn-adapted-args",
- "Warn if an argument list is modified to match the receiver.") enabling List(warnAdaptedArgs)
- //withDeprecationMessage "Enable -Xlint:adapted-args"
+ def YwarnAdaptedArgs = warnAdaptedArgs
@deprecated("Use warnNullaryUnit", since="2.11.2")
- val YwarnNullaryUnit = BooleanSetting("-Ywarn-nullary-unit",
- "Warn when nullary methods return Unit.") enabling List(warnNullaryUnit)
- //withDeprecationMessage "Enable -Xlint:nullary-unit"
+ def YwarnNullaryUnit = warnNullaryUnit
@deprecated("Use warnInaccessible", since="2.11.2")
- val YwarnInaccessible = BooleanSetting("-Ywarn-inaccessible",
- "Warn about inaccessible types in method signatures.") enabling List(warnInaccessible)
- //withDeprecationMessage "Enable -Xlint:inaccessible"
+ def YwarnInaccessible = warnInaccessible
@deprecated("Use warnNullaryOverride", since="2.11.2")
- val YwarnNullaryOverride = BooleanSetting("-Ywarn-nullary-override",
- "Warn when non-nullary `def f()' overrides nullary `def f'.") enabling List(warnNullaryOverride)
- //withDeprecationMessage "Enable -Xlint:nullary-override"
+ def YwarnNullaryOverride = warnNullaryOverride
@deprecated("Use warnInferAny", since="2.11.2")
- val YwarnInferAny = BooleanSetting("-Ywarn-infer-any",
- "Warn when a type argument is inferred to be `Any`.") enabling List(warnInferAny)
- //withDeprecationMessage "Enable -Xlint:infer-any"
+ def YwarnInferAny = warnInferAny
+
+ // The Xlint warning group.
+ val lint = MultiChoiceSetting(
+ name = "-Xlint",
+ helpArg = "warning",
+ descr = "Enable or disable specific warnings",
+ domain = LintWarnings,
+ default = Some(List("_")))
+
+ allLintWarnings foreach {
+ case w if w.yAliased =>
+ BooleanSetting(s"-Ywarn-${w.name}", {w.help}) withPostSetHook { s =>
+ lint.add(if (s) w.name else s"-${w.name}")
+ } // withDeprecationMessage s"Enable -Xlint:${c._1}"
+ case _ =>
+ }
private lazy val warnSelectNullable = BooleanSetting("-Xcheck-null", "This option is obsolete and does nothing.")
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index 1f8ab7e887..12e7b23f48 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -37,6 +37,15 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
/** the following two members override abstract members in Transform */
val phaseName: String = "delambdafy"
+ override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = {
+ if (settings.Ydelambdafy.value == "method") new Phase(prev)
+ else new SkipPhase(prev)
+ }
+
+ class SkipPhase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) {
+ def apply(unit: global.CompilationUnit): Unit = ()
+ }
+
protected def newTransformer(unit: CompilationUnit): Transformer =
new DelambdafyTransformer(unit)
@@ -236,55 +245,59 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// - make `anonClass.isAnonymousClass` true.
// - use `newAnonymousClassSymbol` or push the required variations into a similar factory method
// - reinstate the assertion in `Erasure.resolveAnonymousBridgeClash`
- val suffix = "$lambda$" + (
+ val suffix = nme.DELAMBDAFY_LAMBDA_CLASS_NAME + "$" + (
if (funOwner.isPrimaryConstructor) ""
else "$" + funOwner.name + "$"
)
- val name = unit.freshTypeName(s"${oldClass.name.decode}$suffix")
+ val oldClassPart = oldClass.name.decode
+ // make sure the class name doesn't contain $anon, otherwsie isAnonymousClass/Function may be true
+ val name = unit.freshTypeName(s"$oldClassPart$suffix".replace("$anon", "$nestedInAnon"))
- val anonClass = pkg newClassSymbol(name, originalFunction.pos, FINAL | SYNTHETIC) addAnnotation SerialVersionUIDAnnotation
- anonClass setInfo ClassInfoType(parents, newScope, anonClass)
+ val lambdaClass = pkg newClassSymbol(name, originalFunction.pos, FINAL | SYNTHETIC) addAnnotation SerialVersionUIDAnnotation
+ lambdaClass setInfo ClassInfoType(parents, newScope, lambdaClass)
+ assert(!lambdaClass.isAnonymousClass && !lambdaClass.isAnonymousFunction, "anonymous class name: "+ lambdaClass.name)
+ assert(lambdaClass.isDelambdafyFunction, "not lambda class name: " + lambdaClass.name)
val captureProxies2 = new LinkedHashMap[Symbol, TermSymbol]
captures foreach {capture =>
- val sym = anonClass.newVariable(capture.name.toTermName, capture.pos, SYNTHETIC)
+ val sym = lambdaClass.newVariable(capture.name.toTermName, capture.pos, SYNTHETIC)
sym setInfo capture.info
captureProxies2 += ((capture, sym))
}
- // the Optional proxy that will hold a reference to the 'this'
- // object used by the lambda, if any. NoSymbol if there is no this proxy
- val thisProxy = {
- val target = targetMethod(originalFunction)
- if (thisReferringMethods contains target) {
- val sym = anonClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC)
- sym.info = oldClass.tpe
- sym
- } else NoSymbol
- }
+ // the Optional proxy that will hold a reference to the 'this'
+ // object used by the lambda, if any. NoSymbol if there is no this proxy
+ val thisProxy = {
+ val target = targetMethod(originalFunction)
+ if (thisReferringMethods contains target) {
+ val sym = lambdaClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC)
+ sym.info = oldClass.tpe
+ sym
+ } else NoSymbol
+ }
- val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, anonClass, originalFunction.symbol.pos, thisProxy)
+ val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, lambdaClass, originalFunction.symbol.pos, thisProxy)
- val accessorMethod = createAccessorMethod(thisProxy, originalFunction)
+ val accessorMethod = createAccessorMethod(thisProxy, originalFunction)
- val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function]
+ val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function]
- val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member =>
- anonClass.info.decls enter member
- ValDef(member, gen.mkZero(member.tpe)) setPos decapturedFunction.pos
- }
+ val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member =>
+ lambdaClass.info.decls enter member
+ ValDef(member, gen.mkZero(member.tpe)) setPos decapturedFunction.pos
+ }
- // constructor
- val constr = createConstructor(anonClass, members)
+ // constructor
+ val constr = createConstructor(lambdaClass, members)
- // apply method with same arguments and return type as original lambda.
- val applyMethodDef = createApplyMethod(anonClass, decapturedFunction, accessorMethod, thisProxy)
+ // apply method with same arguments and return type as original lambda.
+ val applyMethodDef = createApplyMethod(lambdaClass, decapturedFunction, accessorMethod, thisProxy)
- val bridgeMethod = createBridgeMethod(anonClass, originalFunction, applyMethodDef)
+ val bridgeMethod = createBridgeMethod(lambdaClass, originalFunction, applyMethodDef)
- def fulldef(sym: Symbol) =
- if (sym == NoSymbol) sym.toString
- else s"$sym: ${sym.tpe} in ${sym.owner}"
+ def fulldef(sym: Symbol) =
+ if (sym == NoSymbol) sym.toString
+ else s"$sym: ${sym.tpe} in ${sym.owner}"
bridgeMethod foreach (bm =>
// TODO SI-6260 maybe just create the apply method with the signature (Object => Object) in all cases
@@ -296,7 +309,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val body = members ++ List(constr, applyMethodDef) ++ bridgeMethod
// TODO if member fields are private this complains that they're not accessible
- (localTyper.typedPos(decapturedFunction.pos)(ClassDef(anonClass, body)).asInstanceOf[ClassDef], thisProxy, accessorMethod)
+ (localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef], thisProxy, accessorMethod)
}
val (anonymousClassDef, thisProxy, accessorMethod) = makeAnonymousClass
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index ec4deb6be0..3d8b2f02f3 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -468,8 +468,12 @@ abstract class Erasure extends AddInterfaces
if (!bridgeNeeded)
return
- val newFlags = (member.flags | BRIDGE | ARTIFACT) & ~(ACCESSOR | DEFERRED | LAZY | lateDEFERRED)
- val bridge = other.cloneSymbolImpl(root, newFlags) setPos root.pos
+ var newFlags = (member.flags | BRIDGE | ARTIFACT) & ~(ACCESSOR | DEFERRED | LAZY | lateDEFERRED)
+ // If `member` is a ModuleSymbol, the bridge should not also be a ModuleSymbol. Otherwise we
+ // end up with two module symbols with the same name in the same scope, which is surprising
+ // when implementing later phases.
+ if (member.isModule) newFlags = (newFlags | METHOD) & ~(MODULE | lateMETHOD | STABLE)
+ val bridge = other.cloneSymbolImpl(root, newFlags) setPos root.pos
debuglog("generating bridge from %s (%s): %s to %s: %s".format(
other, flagsToString(newFlags),
@@ -1133,7 +1137,7 @@ abstract class Erasure extends AddInterfaces
val tree2 = mixinTransformer.transform(tree1)
// debuglog("tree after addinterfaces: \n" + tree2)
- newTyper(rootContext(unit, tree, erasedTypes = true)).typed(tree2)
+ newTyper(rootContextPostTyper(unit, tree)).typed(tree2)
}
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index f85d8222f0..d69c9d9a65 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -449,6 +449,8 @@ abstract class LambdaLift extends InfoTransform {
if (sym.isClass) sym.owner = sym.owner.toInterface
if (sym.isMethod) sym setFlag LIFTED
liftedDefs(sym.owner) ::= tree
+ // TODO: this modifies the ClassInfotype of the enclosing class, which is associated with another phase (explicitouter).
+ // This breaks type history: in a phase travel to before lambda lift, the ClassInfoType will contain lifted classes.
sym.owner.info.decls enterUnique sym
debuglog("lifted: " + sym + " from " + oldOwner + " to " + sym.owner)
EmptyTree
diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
index 3feadcd9b2..dc3313e2e4 100644
--- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
+++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
@@ -17,9 +17,9 @@ trait TypingTransformers {
abstract class TypingTransformer(unit: CompilationUnit) extends Transformer {
var localTyper: analyzer.Typer =
if (phase.erasedTypes)
- erasure.newTyper(erasure.rootContext(unit, EmptyTree, erasedTypes = true)).asInstanceOf[analyzer.Typer]
- else
- analyzer.newTyper(analyzer.rootContext(unit, EmptyTree, true))
+ erasure.newTyper(erasure.rootContextPostTyper(unit, EmptyTree)).asInstanceOf[analyzer.Typer]
+ else // TODO: AM: should some phases use a regular rootContext instead of a post-typer one??
+ analyzer.newTyper(analyzer.rootContextPostTyper(unit, EmptyTree))
protected var curTree: Tree = _
override final def atOwner[A](owner: Symbol)(trans: => A): A = atOwner(curTree, owner)(trans)
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 9715fdaf00..20e462bbce 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -27,6 +27,16 @@ trait ContextErrors {
override def toString() = "[Type error at:" + errPos + "] " + errMsg
}
+ abstract class AbsAmbiguousTypeError extends AbsTypeError
+
+ case class AmbiguousTypeError(errPos: Position, errMsg: String)
+ extends AbsAmbiguousTypeError
+
+ case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String)
+ extends AbsAmbiguousTypeError {
+ def errPos = underlyingTree.pos
+ }
+
sealed abstract class TreeTypeError extends AbsTypeError {
def underlyingTree: Tree
def errPos = underlyingTree.pos
@@ -38,9 +48,6 @@ trait ContextErrors {
case class AccessTypeError(underlyingTree: Tree, errMsg: String)
extends TreeTypeError
- case class AmbiguousTypeError(errPos: Position, errMsg: String)
- extends AbsTypeError
-
case class SymbolTypeError(underlyingSym: Symbol, errMsg: String)
extends AbsTypeError {
@@ -75,8 +82,6 @@ trait ContextErrors {
s"diverging implicit expansion for type ${pt}\nstarting with ${sym.fullLocationString}"
}
- case class AmbiguousImplicitTypeError(underlyingTree: Tree, errMsg: String)
- extends TreeTypeError
case class PosAndMsgTypeError(errPos: Position, errMsg: String)
extends AbsTypeError
@@ -90,10 +95,6 @@ trait ContextErrors {
issueTypeError(SymbolTypeError(sym, msg))
}
- def issueAmbiguousTypeError(pre: Type, sym1: Symbol, sym2: Symbol, err: AmbiguousTypeError)(implicit context: Context) {
- context.issueAmbiguousError(pre, sym1, sym2, err)
- }
-
def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) }
def typeErrorMsg(found: Type, req: Type) = "type mismatch" + foundReqMsg(found, req)
@@ -123,6 +124,36 @@ trait ContextErrors {
import ErrorUtils._
+ private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = {
+ def debugDiagnostic = s"(internal diagnostic: $internalMessage)"
+ val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage
+ // TODO: clean this up! (This is a more explicit version of what the code use to do, to reveal the issue.)
+ throw new TypeError(analyzer.lastTreeToTyper.pos, message)
+ }
+
+ def MacroCantExpand210xMacrosError(internalMessage: String) =
+ MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage)
+
+ def MacroCantExpandIncompatibleMacrosError(internalMessage: String) =
+ MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage)
+
+ def NoImplicitFoundError(tree: Tree, param: Symbol)(implicit context: Context): Unit = {
+ def errMsg = {
+ val paramName = param.name
+ val paramTp = param.tpe
+ def evOrParam = (
+ if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX)
+ "evidence parameter of type"
+ else
+ s"parameter $paramName:")
+ paramTp.typeSymbolDirect match {
+ case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp)
+ case _ => s"could not find implicit value for $evOrParam $paramTp"
+ }
+ }
+ issueNormalTypeError(tree, errMsg)
+ }
+
trait TyperContextErrors {
self: Typer =>
@@ -141,24 +172,6 @@ trait ContextErrors {
setError(tree)
}
- def NoImplicitFoundError(tree: Tree, param: Symbol) = {
- def errMsg = {
- val paramName = param.name
- val paramTp = param.tpe
- def evOrParam = (
- if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX)
- "evidence parameter of type"
- else
- s"parameter $paramName:"
- )
- paramTp.typeSymbolDirect match {
- case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp)
- case _ => s"could not find implicit value for $evOrParam $paramTp"
- }
- }
- issueNormalTypeError(tree, errMsg)
- }
-
def AdaptTypeError(tree: Tree, found: Type, req: Type) = {
// SI-3971 unwrapping to the outermost Apply helps prevent confusion with the
// error message point.
@@ -733,17 +746,6 @@ trait ContextErrors {
NormalTypeError(expandee, "too many argument lists for " + fun)
}
- private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = {
- def debugDiagnostic = s"(internal diagnostic: $internalMessage)"
- val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage
- issueNormalTypeError(lastTreeToTyper, message)
- }
-
- def MacroCantExpand210xMacrosError(internalMessage: String) =
- MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage)
-
- def MacroCantExpandIncompatibleMacrosError(internalMessage: String) =
- MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage)
case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable
@@ -883,19 +885,21 @@ trait ContextErrors {
val WrongNumber, NoParams, ArgsDoNotConform = Value
}
- private def ambiguousErrorMsgPos(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) =
- if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) {
- val methodName = nme.defaultGetterToMethod(sym1.name)
- (sym1.enclClass.pos,
- "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName +
- " define default arguments")
- } else {
- (pos,
- ("ambiguous reference to overloaded definition,\n" +
- "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) +
- "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) +
- "\nmatch " + rest)
- )
+ private def issueAmbiguousTypeErrorUnlessErroneous(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String): Unit =
+ if (!(pre.isErroneous || sym1.isErroneous || sym2.isErroneous)) {
+ if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) {
+ val methodName = nme.defaultGetterToMethod(sym1.name)
+ context.issueAmbiguousError(AmbiguousTypeError(sym1.enclClass.pos,
+ "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName +
+ " define default arguments"))
+ } else {
+ context.issueAmbiguousError(AmbiguousTypeError(pos,
+ ("ambiguous reference to overloaded definition,\n" +
+ "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) +
+ "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) +
+ "\nmatch " + rest)
+ ))
+ }
}
def AccessError(tree: Tree, sym: Symbol, ctx: Context, explanation: String): AbsTypeError =
@@ -952,8 +956,7 @@ trait ContextErrors {
val msg0 =
"argument types " + argtpes.mkString("(", ",", ")") +
(if (pt == WildcardType) "" else " and expected result type " + pt)
- val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0)
- issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg))
+ issueAmbiguousTypeErrorUnlessErroneous(tree.pos, pre, best, firstCompeting, msg0)
setErrorOnLastTry(lastTry, tree)
} else setError(tree) // do not even try further attempts because they should all fail
// even if this is not the last attempt (because of the SO's possibility on the horizon)
@@ -966,8 +969,7 @@ trait ContextErrors {
}
def AmbiguousExprAlternativeError(tree: Tree, pre: Type, best: Symbol, firstCompeting: Symbol, pt: Type, lastTry: Boolean) = {
- val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, "expected type " + pt)
- issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(pos, msg))
+ issueAmbiguousTypeErrorUnlessErroneous(tree.pos, pre, best, firstCompeting, "expected type " + pt)
setErrorOnLastTry(lastTry, tree)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 72ca9b879a..a79f162140 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -9,6 +9,7 @@ package typechecker
import scala.collection.{ immutable, mutable }
import scala.annotation.tailrec
import scala.reflect.internal.util.shortClassOfInstance
+import scala.tools.nsc.reporters.Reporter
/**
* @author Martin Odersky
@@ -98,7 +99,7 @@ trait Contexts { self: Analyzer =>
}
- def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, erasedTypes: Boolean = false): Context = {
+ def rootContext(unit: CompilationUnit, tree: Tree = EmptyTree, throwing: Boolean = false, checking: Boolean = false): Context = {
val rootImportsContext = (startContext /: rootImports(unit))((c, sym) => c.make(gen.mkWildcardImport(sym)))
// there must be a scala.xml package when xml literals were parsed in this unit
@@ -113,18 +114,21 @@ trait Contexts { self: Analyzer =>
else rootImportsContext.make(gen.mkImport(ScalaXmlPackage, nme.TopScope, nme.dollarScope))
val c = contextWithXML.make(tree, unit = unit)
- if (erasedTypes) c.setThrowErrors() else c.setReportErrors()
- c(EnrichmentEnabled | ImplicitsEnabled) = !erasedTypes
+
+ c.initRootContext(throwing, checking)
c
}
+ def rootContextPostTyper(unit: CompilationUnit, tree: Tree = EmptyTree): Context =
+ rootContext(unit, tree, throwing = true)
+
def resetContexts() {
startContext.enclosingContextChain foreach { context =>
context.tree match {
case Import(qual, _) => qual setType singleType(qual.symbol.owner.thisType, qual.symbol)
case _ =>
}
- context.reportBuffer.clearAll()
+ context.reporter.clearAll()
}
}
@@ -178,7 +182,8 @@ trait Contexts { self: Analyzer =>
* @param _outer The next outer context.
*/
class Context private[typechecker](val tree: Tree, val owner: Symbol, val scope: Scope,
- val unit: CompilationUnit, _outer: Context) {
+ val unit: CompilationUnit, _outer: Context,
+ private[this] var _reporter: ContextReporter = new ThrowingReporter) {
private def outerIsNoContext = _outer eq null
final def outer: Context = if (outerIsNoContext) NoContext else _outer
@@ -254,8 +259,6 @@ trait Contexts { self: Analyzer =>
def macrosEnabled = this(MacrosEnabled)
def enrichmentEnabled_=(value: Boolean) = this(EnrichmentEnabled) = value
def enrichmentEnabled = this(EnrichmentEnabled)
- def checking_=(value: Boolean) = this(Checking) = value
- def checking = this(Checking)
def retyping_=(value: Boolean) = this(ReTyping) = value
def retyping = this(ReTyping)
def inSecondTry = this(SecondTry)
@@ -265,8 +268,9 @@ trait Contexts { self: Analyzer =>
def defaultModeForTyped: Mode = if (inTypeConstructorAllowed) Mode.NOmode else Mode.EXPRmode
- /** These messages are printed when issuing an error */
- var diagnostic: List[String] = Nil
+ /** To enrich error messages involving default arguments.
+ When extending the notion, group diagnostics in an object. */
+ var diagUsedDefaults: Boolean = false
/** Saved type bounds for type parameters which are narrowed in a GADT. */
var savedTypeBounds: List[(Symbol, Type)] = List()
@@ -310,7 +314,7 @@ trait Contexts { self: Analyzer =>
*/
def savingUndeterminedTypeParams[A](reportAmbiguous: Boolean = ambiguousErrors)(body: => A): A = {
withMode() {
- this(AmbiguousErrors) = reportAmbiguous
+ setAmbiguousErrors(reportAmbiguous)
val saved = extractUndetparams()
try body
finally undetparams = saved
@@ -321,54 +325,59 @@ trait Contexts { self: Analyzer =>
// Error reporting policies and buffer.
//
- private var _reportBuffer: ReportBuffer = new ReportBuffer
- /** A buffer for errors and warnings, used with `this.bufferErrors == true` */
- def reportBuffer = _reportBuffer
- /** Discard the current report buffer, and replace with an empty one */
- def useFreshReportBuffer() = _reportBuffer = new ReportBuffer
- /** Discard the current report buffer, and replace with `other` */
- def restoreReportBuffer(other: ReportBuffer) = _reportBuffer = other
-
- /** The first error, if any, in the report buffer */
- def firstError: Option[AbsTypeError] = reportBuffer.firstError
- def errors: Seq[AbsTypeError] = reportBuffer.errors
- /** Does the report buffer contain any errors? */
- def hasErrors = reportBuffer.hasErrors
-
- def reportErrors = this(ReportErrors)
- def bufferErrors = this(BufferErrors)
+ // the reporter for this context
+ def reporter: ContextReporter = _reporter
+
+ // if set, errors will not be reporter/thrown
+ def bufferErrors = reporter.isBuffering
+ def reportErrors = !bufferErrors
+
+ // whether to *report* (which is separate from buffering/throwing) ambiguity errors
def ambiguousErrors = this(AmbiguousErrors)
- def throwErrors = contextMode.inNone(ReportErrors | BufferErrors)
-
- def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors)
- def setBufferErrors(): Unit = set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors)
- def setThrowErrors(): Unit = this(ReportErrors | AmbiguousErrors | BufferErrors) = false
- def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report
-
- /** Append the given errors to the report buffer */
- def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors
- /** Clear all errors from the report buffer */
- def flushBuffer() { reportBuffer.clearAllErrors() }
- /** Return and clear all errors from the report buffer */
- def flushAndReturnBuffer(): immutable.Seq[AbsTypeError] = {
- val current = reportBuffer.errors
- reportBuffer.clearAllErrors()
- current
- }
- /** Issue and clear all warnings from the report buffer */
- def flushAndIssueWarnings() {
- reportBuffer.warnings foreach {
- case (pos, msg) => reporter.warning(pos, msg)
+ private def setAmbiguousErrors(report: Boolean): Unit = this(AmbiguousErrors) = report
+
+ /**
+ * Try inference twice: once without views and once with views,
+ * unless views are already disabled.
+ */
+ abstract class TryTwice {
+ def tryOnce(isLastTry: Boolean): Unit
+
+ final def apply(): Unit = {
+ val doLastTry =
+ // do first try if implicits are enabled
+ if (implicitsEnabled) {
+ // We create a new BufferingReporter to
+ // distinguish errors that occurred before entering tryTwice
+ // and our first attempt in 'withImplicitsDisabled'. If the
+ // first attempt fails, we try with implicits on
+ // and the original reporter.
+ // immediate reporting of ambiguous errors is suppressed, so that they are buffered
+ inSilentMode {
+ try {
+ set(disable = ImplicitsEnabled | EnrichmentEnabled) // restored by inSilentMode
+ tryOnce(false)
+ reporter.hasErrors
+ } catch {
+ case ex: CyclicReference => throw ex
+ case ex: TypeError => true // recoverable cyclic references?
+ }
+ }
+ } else true
+
+ // do last try if try with implicits enabled failed
+ // (or if it was not attempted because they were disabled)
+ if (doLastTry)
+ tryOnce(true)
}
- reportBuffer.clearAllWarnings()
}
//
// Temporary mode adjustment
//
- @inline def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = {
+ @inline final def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = {
val saved = contextMode
set(enabled, disabled)
try op
@@ -402,12 +411,18 @@ trait Contexts { self: Analyzer =>
// See comment on FormerNonStickyModes.
@inline final def withOnlyStickyModes[T](op: => T): T = withMode(disabled = FormerNonStickyModes)(op)
- /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */
+ // inliner note: this has to be a simple method for inlining to work -- moved the `&& !reporter.hasErrors` out
@inline final def inSilentMode(expr: => Boolean): Boolean = {
- withMode() { // withMode with no arguments to restore the mode mutated by `setBufferErrors`.
- setBufferErrors()
- try expr && !hasErrors
- finally reportBuffer.clearAll()
+ val savedContextMode = contextMode
+ val savedReporter = reporter
+
+ setAmbiguousErrors(false)
+ _reporter = new BufferingReporter
+
+ try expr
+ finally {
+ contextMode = savedContextMode
+ _reporter = savedReporter
}
}
@@ -423,7 +438,8 @@ trait Contexts { self: Analyzer =>
* `Context#imports`.
*/
def make(tree: Tree = tree, owner: Symbol = owner,
- scope: Scope = scope, unit: CompilationUnit = unit): Context = {
+ scope: Scope = scope, unit: CompilationUnit = unit,
+ reporter: ContextReporter = this.reporter): Context = {
val isTemplateOrPackage = tree match {
case _: Template | _: PackageDef => true
case _ => false
@@ -446,16 +462,15 @@ trait Contexts { self: Analyzer =>
// The blank canvas
val c = if (isImport)
- new Context(tree, owner, scope, unit, this) with ImportContext
+ new Context(tree, owner, scope, unit, this, reporter) with ImportContext
else
- new Context(tree, owner, scope, unit, this)
+ new Context(tree, owner, scope, unit, this, reporter)
// Fields that are directly propagated
c.variance = variance
- c.diagnostic = diagnostic
+ c.diagUsedDefaults = diagUsedDefaults
c.openImplicits = openImplicits
c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below.
- c._reportBuffer = reportBuffer
// Fields that may take on a different value in the child
c.prefix = prefixInChild
@@ -470,22 +485,38 @@ trait Contexts { self: Analyzer =>
c
}
+ /** Use reporter (possibly buffered) for errors/warnings and enable implicit conversion **/
+ def initRootContext(throwing: Boolean = false, checking: Boolean = false): Unit = {
+ _reporter =
+ if (checking) new CheckingReporter
+ else if (throwing) new ThrowingReporter
+ else new ImmediateReporter
+
+ setAmbiguousErrors(!throwing)
+ this(EnrichmentEnabled | ImplicitsEnabled) = !throwing
+ }
+
def make(tree: Tree, owner: Symbol, scope: Scope): Context =
// TODO SI-7345 Moving this optimization into the main overload of `make` causes all tests to fail.
- // even if it is extened to check that `unit == this.unit`. Why is this?
+ // even if it is extended to check that `unit == this.unit`. Why is this?
if (tree == this.tree && owner == this.owner && scope == this.scope) this
else make(tree, owner, scope, unit)
/** Make a child context that represents a new nested scope */
- def makeNewScope(tree: Tree, owner: Symbol): Context =
- make(tree, owner, newNestedScope(scope))
+ def makeNewScope(tree: Tree, owner: Symbol, reporter: ContextReporter = this.reporter): Context =
+ make(tree, owner, newNestedScope(scope), reporter = reporter)
/** Make a child context that buffers errors and warnings into a fresh report buffer. */
def makeSilent(reportAmbiguousErrors: Boolean = ambiguousErrors, newtree: Tree = tree): Context = {
- val c = make(newtree)
- c.setBufferErrors()
+ // A fresh buffer so as not to leak errors/warnings into `this`.
+ val c = make(newtree, reporter = new BufferingReporter)
c.setAmbiguousErrors(reportAmbiguousErrors)
- c._reportBuffer = new ReportBuffer // A fresh buffer so as not to leak errors/warnings into `this`.
+ c
+ }
+
+ def makeNonSilent(newtree: Tree): Context = {
+ val c = make(newtree, reporter = reporter.makeImmediate)
+ c.setAmbiguousErrors(true)
c
}
@@ -508,7 +539,9 @@ trait Contexts { self: Analyzer =>
*/
def makeConstructorContext = {
val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template])
- val argContext = baseContext.makeNewScope(tree, owner)
+ // must propagate reporter!
+ // (caught by neg/t3649 when refactoring reporting to be specified only by this.reporter and not also by this.contextMode)
+ val argContext = baseContext.makeNewScope(tree, owner, reporter = this.reporter)
argContext.contextMode = contextMode
argContext.inSelfSuperCall = true
def enterElems(c: Context) {
@@ -533,65 +566,16 @@ trait Contexts { self: Analyzer =>
// Error and warning issuance
//
- private def addDiagString(msg: String) = {
- val ds =
- if (diagnostic.isEmpty) ""
- else diagnostic.mkString("\n","\n", "")
- if (msg endsWith ds) msg else msg + ds
- }
-
- private def unitError(pos: Position, msg: String): Unit =
- if (checking) onTreeCheckerError(pos, msg) else reporter.error(pos, msg)
-
- @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) {
- // TODO: are errors allowed to have pos == NoPosition??
- // if not, Jason suggests doing: val pos = err.errPos.orElse( { devWarning("Que?"); context.tree.pos })
- if (settings.Yissuedebug) {
- log("issue error: " + err.errMsg)
- (new Exception).printStackTrace()
- }
- if (pf isDefinedAt err) pf(err)
- else if (bufferErrors) { reportBuffer += err }
- else throw new TypeError(err.errPos, err.errMsg)
- }
-
/** Issue/buffer/throw the given type error according to the current mode for error reporting. */
- def issue(err: AbsTypeError) {
- issueCommon(err) { case _ if reportErrors =>
- unitError(err.errPos, addDiagString(err.errMsg))
- }
- }
-
- /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */
- def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) {
- issueCommon(err) { case _ if ambiguousErrors =>
- if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous)
- unitError(err.errPos, err.errMsg)
- }
- }
-
+ private[typechecker] def issue(err: AbsTypeError) = reporter.issue(err)(this)
/** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */
- def issueAmbiguousError(err: AbsTypeError) {
- issueCommon(err) { case _ if ambiguousErrors => unitError(err.errPos, addDiagString(err.errMsg)) }
- }
-
- /** Issue/throw the given `err` according to the current mode for error reporting. */
- def error(pos: Position, err: Throwable) =
- if (reportErrors) unitError(pos, addDiagString(err.getMessage()))
- else throw err
-
+ private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reporter.issueAmbiguousError(err)(this)
/** Issue/throw the given error message according to the current mode for error reporting. */
- def error(pos: Position, msg: String) = {
- val msg1 = addDiagString(msg)
- if (reportErrors) unitError(pos, msg1)
- else throw new TypeError(pos, msg1)
- }
-
+ def error(pos: Position, msg: String) = reporter.error(pos, msg)
/** Issue/throw the given error message according to the current mode for error reporting. */
- def warning(pos: Position, msg: String, force: Boolean = false) {
- if (reportErrors || force) reporter.warning(pos, msg)
- else if (bufferErrors) reportBuffer += (pos -> msg)
- }
+ def warning(pos: Position, msg: String) = reporter.warning(pos, msg)
+ def echo(pos: Position, msg: String) = reporter.echo(pos, msg)
+
def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit =
currentRun.reporting.deprecationWarning(pos, sym, msg)
@@ -601,7 +585,6 @@ trait Contexts { self: Analyzer =>
def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit =
currentRun.reporting.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required)
- def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg)
// nextOuter determines which context is searched next for implicits
// (after `this`, which contributes `newImplicits` below.) In
@@ -1238,61 +1221,176 @@ trait Contexts { self: Analyzer =>
override final def toString = super.toString + " with " + s"ImportContext { $impInfo; outer.owner = ${outer.owner} }"
}
- /** A buffer for warnings and errors that are accumulated during speculative type checking. */
- final class ReportBuffer {
+ /** A reporter for use during type checking. It has multiple modes for handling errors.
+ *
+ * The default (immediate mode) is to send the error to the global reporter.
+ * When switched into buffering mode via makeBuffering, errors and warnings are buffered and not be reported
+ * (there's a special case for ambiguity errors for some reason: those are force to the reporter when context.ambiguousErrors,
+ * or else they are buffered -- TODO: can we simplify this?)
+ *
+ * When using the type checker after typers, an error results in a TypeError being thrown. TODO: get rid of this mode.
+ *
+ * To handle nested contexts, reporters share buffers. TODO: only buffer in BufferingReporter, emit immediately in ImmediateReporter
+ */
+ abstract class ContextReporter(private[this] var _errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, private[this] var _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends Reporter {
type Error = AbsTypeError
type Warning = (Position, String)
- private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results.
+ def issue(err: AbsTypeError)(implicit context: Context): Unit = handleError(err.errPos, addDiagString(err.errMsg))
- // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This
- // is replicated here out of conservatism.
- private var _errorBuffer: mutable.LinkedHashSet[Error] = _
- private def errorBuffer = {if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer}
- def errors: immutable.Seq[Error] = errorBuffer.toVector
+ protected def handleError(pos: Position, msg: String): Unit
+ protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = ()
+ protected def handleWarning(pos: Position, msg: String): Unit = reporter.warning(pos, msg)
- private var _warningBuffer: mutable.LinkedHashSet[Warning] = _
- private def warningBuffer = {if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer}
- def warnings: immutable.Seq[Warning] = warningBuffer.toVector
+ def makeImmediate: ContextReporter = this
+ def makeBuffering: ContextReporter = this
+ def isBuffering: Boolean = false
- def +=(error: AbsTypeError): this.type = {
- errorBuffer += error
- this
- }
- def ++=(errors: Traversable[AbsTypeError]): this.type = {
- errorBuffer ++= errors
- this
- }
- def +=(warning: Warning): this.type = {
- warningBuffer += warning
- this
+ /** Emit an ambiguous error according to context.ambiguousErrors
+ *
+ * - when true, use global.reporter regardless of whether we're buffering (TODO: can we change this?)
+ * - else, let this context reporter decide
+ */
+ final def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit =
+ if (context.ambiguousErrors) reporter.error(err.errPos, addDiagString(err.errMsg)) // force reporting... see TODO above
+ else handleSuppressedAmbiguous(err)
+
+ @inline final def withFreshErrorBuffer[T](expr: => T): T = {
+ val previousBuffer = _errorBuffer
+ _errorBuffer = newBuffer
+ val res = expr // expr will read _errorBuffer
+ _errorBuffer = previousBuffer
+ res
}
- def clearAll(): this.type = {
- clearAllErrors(); clearAllWarnings();
+ @inline final def propagatingErrorsTo[T](target: ContextReporter)(expr: => T): T = {
+ val res = expr // TODO: make sure we're okay skipping the try/finally overhead
+ if ((this ne target) && hasErrors) { // `this eq target` in e.g., test/files/neg/divergent-implicit.scala
+ // assert(target.errorBuffer ne _errorBuffer)
+ target ++= errors
+ // TODO: is clearAllErrors necessary? (no tests failed when dropping it)
+ // NOTE: even though `this ne target`, it may still be that `target.errorBuffer eq _errorBuffer`,
+ // so don't clear the buffer, but null out the reference so that a new one will be created when necessary (should be never??)
+ // (we should refactor error buffering to avoid mutation on shared buffers)
+ clearAllErrors()
+ }
+ res
}
- def clearAllErrors(): this.type = {
- errorBuffer.clear()
- this
- }
- def clearErrors(removeF: PartialFunction[AbsTypeError, Boolean]): this.type = {
- errorBuffer.retain(!PartialFunction.cond(_)(removeF))
- this
+ protected final def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit =
+ severity match {
+ case ERROR => handleError(pos, msg)
+ case WARNING => handleWarning(pos, msg)
+ case INFO => reporter.echo(pos, msg)
+ }
+
+ final override def hasErrors = super.hasErrors || errorBuffer.nonEmpty
+
+ // TODO: everything below should be pushed down to BufferingReporter (related to buffering)
+ // Implicit relies on this most heavily, but there you know reporter.isInstanceOf[BufferingReporter]
+ // can we encode this statically?
+
+ // have to pass in context because multiple contexts may share the same ReportBuffer
+ def reportFirstDivergentError(fun: Tree, param: Symbol, paramTp: Type)(implicit context: Context): Unit =
+ errors.collectFirst {
+ case dte: DivergentImplicitTypeError => dte
+ } match {
+ case Some(divergent) =>
+ // DivergentImplicit error has higher priority than "no implicit found"
+ // no need to issue the problem again if we are still in silent mode
+ if (context.reportErrors) {
+ context.issue(divergent.withPt(paramTp))
+ errorBuffer.retain {
+ case dte: DivergentImplicitTypeError => false
+ case _ => true
+ }
+ }
+ case _ =>
+ NoImplicitFoundError(fun, param)(context)
+ }
+
+ def retainDivergentErrorsExcept(saved: DivergentImplicitTypeError) =
+ errorBuffer.retain {
+ case err: DivergentImplicitTypeError => err ne saved
+ case _ => false
+ }
+
+ def propagateImplicitTypeErrorsTo(target: ContextReporter) = {
+ errors foreach {
+ case err@(_: DivergentImplicitTypeError | _: AmbiguousImplicitTypeError) =>
+ target.errorBuffer += err
+ case _ =>
+ }
+ // debuglog("propagateImplicitTypeErrorsTo: " + errors)
}
- def retainErrors(leaveF: PartialFunction[AbsTypeError, Boolean]): this.type = {
- errorBuffer.retain(PartialFunction.cond(_)(leaveF))
- this
+
+ protected def addDiagString(msg: String)(implicit context: Context): String = {
+ val diagUsedDefaultsMsg = "Error occurred in an application involving default arguments."
+ if (context.diagUsedDefaults && !(msg endsWith diagUsedDefaultsMsg)) msg + "\n" + diagUsedDefaultsMsg
+ else msg
}
- def clearAllWarnings(): this.type = {
- warningBuffer.clear()
- this
+
+ final def emitWarnings() = if (_warningBuffer != null) {
+ _warningBuffer foreach {
+ case (pos, msg) => reporter.warning(pos, msg)
+ }
+ _warningBuffer = null
}
- def hasErrors = errorBuffer.nonEmpty
- def firstError = errorBuffer.headOption
+ // [JZ] Contexts, pre- the SI-7345 refactor, avoided allocating the buffers until needed. This
+ // is replicated here out of conservatism.
+ private def newBuffer[A] = mutable.LinkedHashSet.empty[A] // Important to use LinkedHS for stable results.
+ final protected def errorBuffer = { if (_errorBuffer == null) _errorBuffer = newBuffer; _errorBuffer }
+ final protected def warningBuffer = { if (_warningBuffer == null) _warningBuffer = newBuffer; _warningBuffer }
+
+ final def errors: immutable.Seq[Error] = errorBuffer.toVector
+ final def warnings: immutable.Seq[Warning] = warningBuffer.toVector
+ final def firstError: Option[AbsTypeError] = errorBuffer.headOption
+
+ // TODO: remove ++= and clearAll* entirely in favor of more high-level combinators like withFreshErrorBuffer
+ final private[typechecker] def ++=(errors: Traversable[AbsTypeError]): Unit = errorBuffer ++= errors
+
+ // null references to buffers instead of clearing them,
+ // as the buffers may be shared between different reporters
+ final def clearAll(): Unit = { _errorBuffer = null; _warningBuffer = null }
+ final def clearAllErrors(): Unit = { _errorBuffer = null }
+ }
+
+ private[typechecker] class ImmediateReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) {
+ override def makeBuffering: ContextReporter = new BufferingReporter(errorBuffer, warningBuffer)
+ protected def handleError(pos: Position, msg: String): Unit = reporter.error(pos, msg)
+ }
+
+
+ private[typechecker] class BufferingReporter(_errorBuffer: mutable.LinkedHashSet[AbsTypeError] = null, _warningBuffer: mutable.LinkedHashSet[(Position, String)] = null) extends ContextReporter(_errorBuffer, _warningBuffer) {
+ override def isBuffering = true
+
+ override def issue(err: AbsTypeError)(implicit context: Context): Unit = errorBuffer += err
+
+ // this used to throw new TypeError(pos, msg) -- buffering lets us report more errors (test/files/neg/macro-basic-mamdmi)
+ // the old throwing behavior was relied on by diagnostics in manifestOfType
+ protected def handleError(pos: Position, msg: String): Unit = errorBuffer += TypeErrorWrapper(new TypeError(pos, msg))
+ override protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = errorBuffer += err
+ override protected def handleWarning(pos: Position, msg: String): Unit = warningBuffer += ((pos, msg))
+
+ // TODO: emit all buffered errors, warnings
+ override def makeImmediate: ContextReporter = new ImmediateReporter(errorBuffer, warningBuffer)
}
+ /** Used after typer (specialization relies on TypeError being thrown, among other post-typer phases).
+ *
+ * TODO: get rid of it, use ImmediateReporter and a check for reporter.hasErrors where necessary
+ */
+ private[typechecker] class ThrowingReporter extends ContextReporter {
+ protected def handleError(pos: Position, msg: String): Unit = throw new TypeError(pos, msg)
+ }
+
+ /** Used during a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */
+ private[typechecker] class CheckingReporter extends ContextReporter {
+ protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg)
+ }
+
+
class ImportInfo(val tree: Import, val depth: Int) {
def pos = tree.pos
def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos
@@ -1385,8 +1483,6 @@ object ContextMode {
def apply(bits: Int): ContextMode = new ContextMode(bits)
final val NOmode: ContextMode = 0
- final val ReportErrors: ContextMode = 1 << 0
- final val BufferErrors: ContextMode = 1 << 1
final val AmbiguousErrors: ContextMode = 1 << 2
/** Are we in a secondary constructor after the this constructor call? */
@@ -1409,8 +1505,6 @@ object ContextMode {
/** To selectively allow enrichment in patterns, where other kinds of implicit conversions are not allowed */
final val EnrichmentEnabled: ContextMode = 1 << 8
- /** Are we in a run of [[scala.tools.nsc.typechecker.TreeCheckers]]? */
- final val Checking: ContextMode = 1 << 9
/** Are we retypechecking arguments independently from the function applied to them? See `Typer.tryTypedApply`
* TODO - iron out distinction/overlap with SecondTry.
@@ -1447,17 +1541,14 @@ object ContextMode {
PatternAlternative | StarPatterns | SuperInit | SecondTry | ReturnExpr | TypeConstructorAllowed
)
- final val DefaultMode: ContextMode = MacrosEnabled
+ final val DefaultMode: ContextMode = MacrosEnabled
private val contextModeNameMap = Map(
- ReportErrors -> "ReportErrors",
- BufferErrors -> "BufferErrors",
AmbiguousErrors -> "AmbiguousErrors",
ConstructorSuffix -> "ConstructorSuffix",
SelfSuperCall -> "SelfSuperCall",
ImplicitsEnabled -> "ImplicitsEnabled",
MacrosEnabled -> "MacrosEnabled",
- Checking -> "Checking",
ReTyping -> "ReTyping",
PatternAlternative -> "PatternAlternative",
StarPatterns -> "StarPatterns",
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 73c3e6f016..b85c8e6d42 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -71,13 +71,10 @@ trait Implicits {
typingStack.printTyping(tree, "typing implicit: %s %s".format(tree, context.undetparamsString))
val implicitSearchContext = context.makeImplicit(reportAmbiguous)
val result = new ImplicitSearch(tree, pt, isView, implicitSearchContext, pos).bestImplicit
- if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.hasErrors) {
- context.updateBuffer(implicitSearchContext.reportBuffer.errors.collect {
- case dte: DivergentImplicitTypeError => dte
- case ate: AmbiguousImplicitTypeError => ate
- })
- debuglog("update buffer: " + implicitSearchContext.reportBuffer.errors)
- }
+
+ if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.reporter.hasErrors)
+ implicitSearchContext.reporter.propagateImplicitTypeErrorsTo(context.reporter)
+
// SI-7944 undetermined type parameters that result from inference within typedImplicit land in
// `implicitSearchContext.undetparams`, *not* in `context.undetparams`
// Here, we copy them up to parent context (analogously to the way the errors are copied above),
@@ -99,7 +96,7 @@ trait Implicits {
def wrapper(inference: => SearchResult) = wrapper1(inference)
val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos))
if (result.isFailure && !silent) {
- val err = context.firstError
+ val err = context.reporter.firstError
val errPos = err.map(_.errPos).getOrElse(pos)
val errMsg = err.map(_.errMsg).getOrElse("implicit search has failed. to find out the reason, turn on -Xlog-implicits")
onError(errPos, errMsg)
@@ -635,7 +632,7 @@ trait Implicits {
}
case _ => fallback
}
- context.firstError match { // using match rather than foreach to avoid non local return.
+ context.reporter.firstError match { // using match rather than foreach to avoid non local return.
case Some(err) =>
log("implicit adapt failed: " + err.errMsg)
return fail(err.errMsg)
@@ -658,8 +655,8 @@ trait Implicits {
}
}
- if (context.hasErrors)
- fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg)
+ if (context.reporter.hasErrors)
+ fail("hasMatchingSymbol reported error: " + context.reporter.firstError.get.errMsg)
else if (itree3.isErroneous)
fail("error typechecking implicit candidate")
else if (isLocalToCallsite && !hasMatchingSymbol(itree2))
@@ -677,7 +674,7 @@ trait Implicits {
// #2421: check that we correctly instantiated type parameters outside of the implicit tree:
checkBounds(itree3, NoPrefix, NoSymbol, undetParams, targs, "inferred ")
- context.firstError match {
+ context.reporter.firstError match {
case Some(err) =>
return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg)
case None =>
@@ -716,7 +713,7 @@ trait Implicits {
case t => t
}
- context.firstError match {
+ context.reporter.firstError match {
case Some(err) =>
fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg)
case None =>
@@ -857,13 +854,11 @@ trait Implicits {
SearchFailure
} else {
if (search.isFailure) {
- // We don't want errors that occur during checking implicit info
+ // Discard the divergentError we saved (if any), as well as all errors that are not of type DivergentImplicitTypeError
+ // We don't want errors that occur while checking the implicit info
// to influence the check of further infos, but we should retain divergent implicit errors
// (except for the one we already squirreled away)
- val saved = divergentError.getOrElse(null)
- context.reportBuffer.retainErrors {
- case err: DivergentImplicitTypeError => err ne saved
- }
+ context.reporter.retainDivergentErrorsExcept(divergentError.getOrElse(null))
}
search
}
@@ -909,7 +904,7 @@ trait Implicits {
// the first `DivergentImplicitTypeError` that is being propagated
// from a nested implicit search; this one will be
// re-issued if this level of the search fails.
- DivergentImplicitRecovery(typedFirstPending, firstPending, context.errors) match {
+ DivergentImplicitRecovery(typedFirstPending, firstPending, context.reporter.errors) match {
case sr if sr.isDivergent => Nil
case sr if sr.isFailure => rankImplicits(otherPending, acc)
case newBest =>
@@ -1146,7 +1141,7 @@ trait Implicits {
try {
val tree1 = typedPos(pos.focus)(arg)
- context.firstError match {
+ context.reporter.firstError match {
case Some(err) => processMacroExpansionError(err.errPos, err.errMsg)
case None => new SearchResult(tree1, EmptyTreeTypeSubstituter, Nil)
}
@@ -1278,19 +1273,20 @@ trait Implicits {
if (tagInScope.isEmpty) mot(tp, Nil, Nil)
else {
if (ReflectRuntimeUniverse == NoSymbol) {
- // todo. write a test for this
- context.error(pos,
+ // TODO: write a test for this (the next error message is already checked by neg/interop_typetags_without_classtags_arenot_manifests.scala)
+ // TODO: this was using context.error, and implicit search always runs in silent mode, thus it was actually throwing a TypeError
+ // with the new strategy-based reporting, a BufferingReporter buffers instead of throwing
+ // it would be good to rework this logic to fit into the regular context.error mechanism
+ throw new TypeError(pos,
sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope.
|however typetag -> manifest conversion requires Scala reflection, which is not present on the classpath.
|to proceed put scala-reflect.jar on your compilation classpath and recompile.""")
- return SearchFailure
}
if (resolveClassTag(pos, tp, allowMaterialization = true) == EmptyTree) {
- context.error(pos,
+ throw new TypeError(pos,
sm"""to create a manifest here, it is necessary to interoperate with the type tag `$tagInScope` in scope.
|however typetag -> manifest conversion requires a class tag for the corresponding type to be present.
|to proceed add a class tag to the type `$tp` (e.g. by introducing a context bound) and recompile.""")
- return SearchFailure
}
val cm = typed(Ident(ReflectRuntimeCurrentMirror))
val internal = gen.mkAttributedSelect(gen.mkAttributedRef(ReflectRuntimeUniverse), UniverseInternal)
@@ -1346,52 +1342,66 @@ trait Implicits {
* If all fails return SearchFailure
*/
def bestImplicit: SearchResult = {
- val failstart = if (Statistics.canEnable) Statistics.startTimer(inscopeFailNanos) else null
- val succstart = if (Statistics.canEnable) Statistics.startTimer(inscopeSucceedNanos) else null
+ val stats = Statistics.canEnable
+ val failstart = if (stats) Statistics.startTimer(inscopeFailNanos) else null
+ val succstart = if (stats) Statistics.startTimer(inscopeSucceedNanos) else null
var result = searchImplicit(context.implicitss, isLocalToCallsite = true)
- if (result.isFailure) {
- if (Statistics.canEnable) Statistics.stopTimer(inscopeFailNanos, failstart)
- } else {
- if (Statistics.canEnable) Statistics.stopTimer(inscopeSucceedNanos, succstart)
- if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits)
+ if (stats) {
+ if (result.isFailure) Statistics.stopTimer(inscopeFailNanos, failstart)
+ else {
+ Statistics.stopTimer(inscopeSucceedNanos, succstart)
+ Statistics.incCounter(inscopeImplicitHits)
+ }
}
+
if (result.isFailure) {
- val previousErrs = context.flushAndReturnBuffer()
- val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null
- val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null
+ val failstart = if (stats) Statistics.startTimer(oftypeFailNanos) else null
+ val succstart = if (stats) Statistics.startTimer(oftypeSucceedNanos) else null
+
+ // SI-6667, never search companions after an ambiguous error in in-scope implicits
+ val wasAmbigious = result.isAmbiguousFailure
+
+ // TODO: encapsulate
+ val previousErrs = context.reporter.errors
+ context.reporter.clearAllErrors()
- val wasAmbigious = result.isAmbiguousFailure // SI-6667, never search companions after an ambiguous error in in-scope implicits
result = materializeImplicit(pt)
+
// `materializeImplicit` does some preprocessing for `pt`
// is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`?
if (result.isFailure && !wasAmbigious)
result = searchImplicit(implicitsOfExpectedType, isLocalToCallsite = false)
- if (result.isFailure) {
- context.updateBuffer(previousErrs)
- if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart)
- } else {
- if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart)
- if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits)
+ if (result.isFailure)
+ context.reporter ++= previousErrs
+
+ if (stats) {
+ if (result.isFailure) Statistics.stopTimer(oftypeFailNanos, failstart)
+ else {
+ Statistics.stopTimer(oftypeSucceedNanos, succstart)
+ Statistics.incCounter(oftypeImplicitHits)
+ }
}
}
if (result.isSuccess && isView) {
def maybeInvalidConversionError(msg: String) {
// We have to check context.ambiguousErrors even though we are calling "issueAmbiguousError"
// which ostensibly does exactly that before issuing the error. Why? I have no idea. Test is pos/t7690.
+ // AM: I would guess it's because ambiguous errors will be buffered in silent mode if they are not reported
if (context.ambiguousErrors)
context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg))
}
pt match {
case Function1(_, out) =>
- def prohibit(sym: Symbol) = if (sym.tpe <:< out) {
- maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}")
- result = SearchFailure
+ // must inline to avoid capturing result
+ def prohibit(sym: Symbol) = (sym.tpe <:< out) && {
+ maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}")
+ true
}
- prohibit(AnyRefClass)
- if (settings.isScala211) prohibit(AnyValClass)
+ if (prohibit(AnyRefClass) || (settings.isScala211 && prohibit(AnyValClass)))
+ result = SearchFailure
case _ => false
}
if (settings.isScala211 && isInvalidConversionSource(pt)) {
@@ -1399,8 +1409,9 @@ trait Implicits {
result = SearchFailure
}
}
- if (result.isFailure)
- debuglog("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType)
+
+ if (result.isFailure && settings.debug) // debuglog is not inlined for some reason
+ log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType)
result
}
@@ -1422,20 +1433,19 @@ trait Implicits {
val eligible = new ImplicitComputation(iss, isLocalToCallsite).eligible
eligible.toList.flatMap {
(ii: ImplicitInfo) =>
- // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit)
- // thus, start each type var off with a fresh for every typedImplicit
- resetTVars()
- // any previous errors should not affect us now
- context.flushBuffer()
-
- val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite)
- if (res.tree ne EmptyTree) List((res, tvars map (_.constr)))
- else Nil
+ // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit)
+ // thus, start each type var off with a fresh for every typedImplicit
+ resetTVars()
+ // any previous errors should not affect us now
+ context.reporter.clearAllErrors()
+ val res = typedImplicit(ii, ptChecked = false, isLocalToCallsite)
+ if (res.tree ne EmptyTree) List((res, tvars map (_.constr)))
+ else Nil
+ }
}
- }
eligibleInfos(context.implicitss, isLocalToCallsite = true) ++
eligibleInfos(implicitsOfExpectedType, isLocalToCallsite = false)
- }
+ }
}
object ImplicitNotFoundMsg {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index c793632de4..ee2775ee26 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -199,8 +199,6 @@ trait Infer extends Checkable {
def getContext = context
- def issue(err: AbsTypeError): Unit = context.issue(err)
-
def explainTypes(tp1: Type, tp2: Type) = {
if (context.reportErrors)
withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2))
@@ -788,7 +786,7 @@ trait Infer extends Checkable {
def applicableExpectingPt(pt: Type): Boolean = {
val silent = context.makeSilent(reportAmbiguousErrors = false)
val result = newTyper(silent).infer.isApplicable(undetparams, ftpe, argtpes0, pt)
- if (silent.hasErrors && !pt.isWildcard)
+ if (silent.reporter.hasErrors && !pt.isWildcard)
applicableExpectingPt(WildcardType) // second try
else
result
@@ -1273,33 +1271,36 @@ trait Infer extends Checkable {
* If no alternative matches `pt`, take the parameterless one anyway.
*/
def inferExprAlternative(tree: Tree, pt: Type): Tree = {
- def tryOurBests(pre: Type, alts: List[Symbol], isSecondTry: Boolean): Unit = {
- val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt))
- val alts1 = if (alts0.isEmpty) alts else alts0
- val bests = bestAlternatives(alts1) { (sym1, sym2) =>
- val tp1 = pre memberType sym1
- val tp2 = pre memberType sym2
-
- ( (tp2 eq ErrorType)
- || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt)
- || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)
- )
- }
- // todo: missing test case for bests.isEmpty
- bests match {
- case best :: Nil => tree setSymbol best setType (pre memberType best)
- case best :: competing :: _ if alts0.nonEmpty =>
- // SI-6912 Don't give up and leave an OverloadedType on the tree.
- // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try
- // unless an error is issued. We're not issuing an error, in the assumption that it would be
- // spurious in light of the erroneous expected type
- if (pt.isErroneous) setError(tree)
- else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry)
- case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry)
+ val c = context
+ class InferTwice(pre: Type, alts: List[Symbol]) extends c.TryTwice {
+ def tryOnce(isSecondTry: Boolean): Unit = {
+ val alts0 = alts filter (alt => isWeaklyCompatible(pre memberType alt, pt))
+ val alts1 = if (alts0.isEmpty) alts else alts0
+ val bests = bestAlternatives(alts1) { (sym1, sym2) =>
+ val tp1 = pre memberType sym1
+ val tp2 = pre memberType sym2
+
+ ( (tp2 eq ErrorType)
+ || isWeaklyCompatible(tp1, pt) && !isWeaklyCompatible(tp2, pt)
+ || isStrictlyMoreSpecific(tp1, tp2, sym1, sym2)
+ )
+ }
+ // todo: missing test case for bests.isEmpty
+ bests match {
+ case best :: Nil => tree setSymbol best setType (pre memberType best)
+ case best :: competing :: _ if alts0.nonEmpty =>
+ // SI-6912 Don't give up and leave an OverloadedType on the tree.
+ // Originally I wrote this as `if (secondTry) ... `, but `tryTwice` won't attempt the second try
+ // unless an error is issued. We're not issuing an error, in the assumption that it would be
+ // spurious in light of the erroneous expected type
+ if (pt.isErroneous) setError(tree)
+ else AmbiguousExprAlternativeError(tree, pre, best, competing, pt, isSecondTry)
+ case _ => if (bests.isEmpty || alts0.isEmpty) NoBestExprAlternativeError(tree, pt, isSecondTry)
+ }
}
}
tree.tpe match {
- case OverloadedType(pre, alts) => tryTwice(tryOurBests(pre, alts, _)) ; tree
+ case OverloadedType(pre, alts) => (new InferTwice(pre, alts)).apply() ; tree
case _ => tree
}
}
@@ -1377,70 +1378,41 @@ trait Infer extends Checkable {
* @pre tree.tpe is an OverloadedType.
*/
def inferMethodAlternative(tree: Tree, undetparams: List[Symbol], argtpes0: List[Type], pt0: Type): Unit = {
- val OverloadedType(pre, alts) = tree.tpe
- var varargsStar = false
- val argtpes = argtpes0 mapConserve {
- case RepeatedType(tp) => varargsStar = true ; tp
- case tp => tp
- }
- def followType(sym: Symbol) = followApply(pre memberType sym)
- def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = {
- val applicable0 = alts filter (alt => context inSilentMode isApplicable(undetparams, followType(alt), argtpes, pt))
- val applicable = overloadsToConsiderBySpecificity(applicable0, argtpes, varargsStar)
- val ranked = bestAlternatives(applicable)((sym1, sym2) =>
- isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2)
- )
- ranked match {
- case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous
- case best :: Nil => tree setSymbol best setType (pre memberType best) // success
- case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed
- case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType
- }
- }
- // This potentially makes up to four attempts: tryTwice may execute
+ // This potentially makes up to four attempts: tryOnce may execute
// with and without views enabled, and bestForExpectedType will try again
// with pt = WildcardType if it fails with pt != WildcardType.
- tryTwice { isLastTry =>
- val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0
- debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt")
- bestForExpectedType(pt, isLastTry)
- }
- }
+ val c = context
+ class InferMethodAlternativeTwice extends c.TryTwice {
+ private[this] val OverloadedType(pre, alts) = tree.tpe
+ private[this] var varargsStar = false
+ private[this] val argtpes = argtpes0 mapConserve {
+ case RepeatedType(tp) => varargsStar = true ; tp
+ case tp => tp
+ }
- /** Try inference twice, once without views and once with views,
- * unless views are already disabled.
- */
- def tryTwice(infer: Boolean => Unit): Unit = {
- if (context.implicitsEnabled) {
- val savedContextMode = context.contextMode
- var fallback = false
- context.setBufferErrors()
- // We cache the current buffer because it is impossible to
- // distinguish errors that occurred before entering tryTwice
- // and our first attempt in 'withImplicitsDisabled'. If the
- // first attempt fails we try with implicits on *and* clean
- // buffer but that would also flush any pre-tryTwice valid
- // errors, hence some manual buffer tweaking is necessary.
- val errorsToRestore = context.flushAndReturnBuffer()
- try {
- context.withImplicitsDisabled(infer(false))
- if (context.hasErrors) {
- fallback = true
- context.contextMode = savedContextMode
- context.flushBuffer()
- infer(true)
+ private def followType(sym: Symbol) = followApply(pre memberType sym)
+ // separate method to help the inliner
+ private def isAltApplicable(pt: Type)(alt: Symbol) = context inSilentMode { isApplicable(undetparams, followType(alt), argtpes, pt) && !context.reporter.hasErrors }
+ private def rankAlternatives(sym1: Symbol, sym2: Symbol) = isStrictlyMoreSpecific(followType(sym1), followType(sym2), sym1, sym2)
+ private def bestForExpectedType(pt: Type, isLastTry: Boolean): Unit = {
+ val applicable = overloadsToConsiderBySpecificity(alts filter isAltApplicable(pt), argtpes, varargsStar)
+ val ranked = bestAlternatives(applicable)(rankAlternatives)
+ ranked match {
+ case best :: competing :: _ => AmbiguousMethodAlternativeError(tree, pre, best, competing, argtpes, pt, isLastTry) // ambiguous
+ case best :: Nil => tree setSymbol best setType (pre memberType best) // success
+ case Nil if pt.isWildcard => NoBestMethodAlternativeError(tree, argtpes, pt, isLastTry) // failed
+ case Nil => bestForExpectedType(WildcardType, isLastTry) // failed, but retry with WildcardType
}
- } catch {
- case ex: CyclicReference => throw ex
- case ex: TypeError => // recoverable cyclic references
- context.contextMode = savedContextMode
- if (!fallback) infer(true) else ()
- } finally {
- context.contextMode = savedContextMode
- context.updateBuffer(errorsToRestore)
+ }
+
+ private[this] val pt = if (pt0.typeSymbol == UnitClass) WildcardType else pt0
+ def tryOnce(isLastTry: Boolean): Unit = {
+ debuglog(s"infer method alt ${tree.symbol} with alternatives ${alts map pre.memberType} argtpes=$argtpes pt=$pt")
+ bestForExpectedType(pt, isLastTry)
}
}
- else infer(true)
+
+ (new InferMethodAlternativeTwice).apply()
}
/** Assign `tree` the type of all polymorphic alternatives
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 33d3432ae2..da7b8b09aa 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -226,7 +226,8 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
val Apply(_, pickledPayload) = wrapped
val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap
- import typer.TyperErrorGen._
+ // TODO: refactor error handling: fail always throws a TypeError,
+ // and uses global state (analyzer.lastTreeToTyper) to determine the position for the error
def fail(msg: String) = MacroCantExpandIncompatibleMacrosError(msg)
def unpickle[T](field: String, clazz: Class[T]): T = {
def failField(msg: String) = fail(s"$field $msg")
@@ -624,7 +625,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
// `macroExpandApply` is called from `adapt`, where implicit conversions are disabled
// therefore we need to re-enable the conversions back temporarily
val result = typer.context.withImplicitsEnabled(typer.typed(tree, mode, pt))
- if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reportBuffer.errors}")
+ if (result.isErrorTyped && macroDebugVerbose) println(s"$label has failed: ${typer.context.reporter.errors}")
result
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 7bbd81118a..fdff2f3076 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -1494,8 +1494,7 @@ trait Namers extends MethodSynthesis {
case defn: MemberDef =>
val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann =>
val ctx = typer.context
- val annCtx = ctx.make(ann)
- annCtx.setReportErrors()
+ val annCtx = ctx.makeNonSilent(ann)
// need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892.
AnnotationInfo lazily {
enteringTyper(newTyper(annCtx) typedAnnotation ann)
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
index cf3f265f0c..da0e67a2a5 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala
@@ -261,7 +261,7 @@ trait PatternTypers {
def doTypedUnapply(tree: Tree, fun0: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = {
def duplErrTree = setError(treeCopy.Apply(tree, fun0, args))
- def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree }
+ def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree }
if (args.length > MaxTupleArity)
return duplErrorTree(TooManyArgsPatternError(fun))
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 47465875e9..af4e9e8927 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1287,6 +1287,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
private def checkUndesiredProperties(sym: Symbol, pos: Position) {
// If symbol is deprecated, and the point of reference is not enclosed
// in either a deprecated member or a scala bridge method, issue a warning.
+ // TODO: x.hasBridgeAnnotation doesn't seem to be needed here...
if (sym.isDeprecated && !currentOwner.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation))
currentRun.reporting.deprecationWarning(pos, sym)
@@ -1305,7 +1306,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
reporter.warning(pos, s"${sym.fullLocationString} has changed semantics in version ${sym.migrationVersion.get}:\n${sym.migrationMessage.get}")
}
// See an explanation of compileTimeOnly in its scaladoc at scala.annotation.compileTimeOnly.
- if (sym.isCompileTimeOnly) {
+ if (sym.isCompileTimeOnly && !currentOwner.ownerChain.exists(x => x.isCompileTimeOnly)) {
def defaultMsg =
sm"""Reference to ${sym.fullLocationString} should not have survived past type checking,
|it should have been processed and eliminated during expansion of an enclosing macro."""
@@ -1633,7 +1634,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
if (settings.warnNullaryUnit)
checkNullaryMethodReturnType(sym)
if (settings.warnInaccessible) {
- if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic)
+ if (!sym.isConstructor && !sym.isEffectivelyFinalOrNotOverridden && !sym.isSynthetic)
checkAccessibilityOfReferencedTypes(tree)
}
tree match {
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index 38b00a015b..db81eecdf5 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -82,11 +82,11 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
val buf = accDefs.getOrElse(clazz, sys.error("no acc def buf for "+clazz))
buf += typers(clazz) typed tree
}
- private def ensureAccessor(sel: Select) = {
+ private def ensureAccessor(sel: Select, mixName: TermName = nme.EMPTY) = {
val Select(qual, name) = sel
val sym = sel.symbol
val clazz = qual.symbol
- val supername = nme.superName(name)
+ val supername = nme.superName(name, mixName)
val superAcc = clazz.info.decl(supername).suchThat(_.alias == sym) orElse {
debuglog(s"add super acc ${sym.fullLocationString} to $clazz")
val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE | ARTIFACT) setAlias sym
@@ -150,8 +150,20 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
}
}
- if (name.isTermName && mix == tpnme.EMPTY && (clazz.isTrait || clazz != currentClass || !validCurrentOwner))
- ensureAccessor(sel)
+ def mixIsTrait = sup.tpe match {
+ case SuperType(thisTpe, superTpe) => superTpe.typeSymbol.isTrait
+ }
+
+ val needAccessor = name.isTermName && {
+ mix.isEmpty && (clazz.isTrait || clazz != currentClass || !validCurrentOwner) ||
+ // SI-8803. If we access super[A] from an inner class (!= currentClass) or closure (validCurrentOwner),
+ // where A is the superclass we need an accessor. If A is a parent trait we don't: in this case mixin
+ // will re-route the super call directly to the impl class (it's statically known).
+ !mix.isEmpty && (clazz != currentClass || !validCurrentOwner) && !mixIsTrait
+ }
+
+ if (needAccessor)
+ ensureAccessor(sel, mix.toTermName)
else sel
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index d0237fb468..bedca88974 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -95,7 +95,7 @@ trait SyntheticMethods extends ast.TreeDSL {
// which they shouldn't.
val accessorLub = (
if (settings.Xexperimental) {
- global.weakLub(accessors map (_.tpe.finalResultType)) match {
+ global.lub(accessors map (_.tpe.finalResultType)) match {
case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents)
case tp => tp
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
index 399a4ca8d5..743bbe53bd 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
@@ -208,8 +208,7 @@ abstract class TreeCheckers extends Analyzer {
}
def check(unit: CompilationUnit) {
informProgress("checking "+unit)
- val context = rootContext(unit)
- context.checking = true
+ val context = rootContext(unit, checking = true)
tpeOfTree.clear()
SymbolTracker.check(phase, unit)
val checker = new TreeChecker(context)
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index 7440f69e93..1dac27639c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -532,7 +532,7 @@ trait TypeDiagnostics {
if (sym.isDefaultGetter) "default argument"
else if (sym.isConstructor) "constructor"
else if (sym.isVar || sym.isGetter && sym.accessed.isVar) "var"
- else if (sym.isVal || sym.isGetter && sym.accessed.isVal) "val"
+ else if (sym.isVal || sym.isGetter && sym.accessed.isVal || sym.isLazy) "val"
else if (sym.isSetter) "setter"
else if (sym.isMethod) "method"
else if (sym.isModule) "object"
@@ -572,11 +572,11 @@ trait TypeDiagnostics {
} else f
}
def apply(tree: Tree): Tree = {
- // Error suppression will squash some of these warnings unless we circumvent it.
+ // Error suppression (in context.warning) would squash some of these warnings.
// It is presumed if you are using a -Y option you would really like to hear
- // the warnings you've requested.
+ // the warnings you've requested; thus, use reporter.warning.
if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && exprOK)
- context.warning(tree.pos, "dead code following this construct", force = true)
+ reporter.warning(tree.pos, "dead code following this construct")
tree
}
@@ -600,6 +600,23 @@ trait TypeDiagnostics {
)
}
+ // warn about class/method/type-members' type parameters that shadow types already in scope
+ def warnTypeParameterShadow(tparams: List[TypeDef], sym: Symbol): Unit =
+ if (settings.warnTypeParameterShadow && !isPastTyper && !sym.isSynthetic) {
+ def enclClassOrMethodOrTypeMember(c: Context): Context =
+ if (!c.owner.exists || c.owner.isClass || c.owner.isMethod || (c.owner.isType && !c.owner.isParameter)) c
+ else enclClassOrMethodOrTypeMember(c.outer)
+
+ val tt = tparams.filter(_.name != typeNames.WILDCARD).foreach { tp =>
+ // we don't care about type params shadowing other type params in the same declaration
+ enclClassOrMethodOrTypeMember(context).outer.lookupSymbol(tp.name, s => s != tp.symbol && s.hasRawInfo && reallyExists(s)) match {
+ case LookupSucceeded(_, sym2) => context.warning(tp.pos,
+ s"type parameter ${tp.name} defined in $sym shadows $sym2 defined in ${sym2.owner}. You may want to rename your type parameter, or possibly remove it.")
+ case _ =>
+ }
+ }
+ }
+
/** Report a type error.
*
* @param pos The position where to report the error
@@ -629,7 +646,7 @@ trait TypeDiagnostics {
throw new FatalError("cannot redefine root "+sym)
}
case _ =>
- context0.error(ex.pos, ex)
+ context0.error(ex.pos, ex.msg)
}
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 70f44c4fc6..422b940cd3 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -155,21 +155,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
} else {
mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args
if (!param.hasDefault && !paramFailed) {
- context.reportBuffer.errors.collectFirst {
- case dte: DivergentImplicitTypeError => dte
- } match {
- case Some(divergent) =>
- // DivergentImplicit error has higher priority than "no implicit found"
- // no need to issue the problem again if we are still in silent mode
- if (context.reportErrors) {
- context.issue(divergent.withPt(paramTp))
- context.reportBuffer.clearErrors {
- case dte: DivergentImplicitTypeError => true
- }
- }
- case _ =>
- NoImplicitFoundError(fun, param)
- }
+ context.reporter.reportFirstDivergentError(fun, param, paramTp)(context)
paramFailed = true
}
/* else {
@@ -478,20 +464,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (cond) typerWithLocalContext(c)(f) else f(this)
@inline
- final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = {
- val res = f(newTyper(c))
- if (c.hasErrors)
- context.updateBuffer(c.flushAndReturnBuffer())
- res
- }
-
- @inline
- final def withSavedContext[T](c: Context)(f: => T) = {
- val savedErrors = c.flushAndReturnBuffer()
- val res = f
- c.updateBuffer(savedErrors)
- res
- }
+ final def typerWithLocalContext[T](c: Context)(f: Typer => T): T =
+ c.reporter.propagatingErrorsTo(context.reporter)(f(newTyper(c)))
/** The typer for a label definition. If this is part of a template we
* first have to enter the label definition.
@@ -684,6 +658,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (Statistics.canEnable) Statistics.stopCounter(subtypeFailed, subtypeStart)
if (Statistics.canEnable) Statistics.stopTimer(failedSilentNanos, failedSilentStart)
}
+ @inline def wrapResult(reporter: ContextReporter, result: T) =
+ if (reporter.hasErrors) {
+ stopStats()
+ SilentTypeError(reporter.errors: _*)
+ } else SilentResultValue(result)
+
try {
if (context.reportErrors ||
reportAmbiguousErrors != context.ambiguousErrors ||
@@ -697,20 +677,17 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
context.undetparams = context1.undetparams
context.savedTypeBounds = context1.savedTypeBounds
context.namedApplyBlockInfo = context1.namedApplyBlockInfo
- if (context1.hasErrors) {
- stopStats()
- SilentTypeError(context1.errors: _*)
- } else {
- // If we have a successful result, emit any warnings it created.
- context1.flushAndIssueWarnings()
- SilentResultValue(result)
- }
+
+ // If we have a successful result, emit any warnings it created.
+ if (!context1.reporter.hasErrors)
+ context1.reporter.emitWarnings()
+
+ wrapResult(context1.reporter, result)
} else {
assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer")
- withSavedContext(context){
- val res = op(this)
- val errorsToReport = context.flushAndReturnBuffer()
- if (errorsToReport.isEmpty) SilentResultValue(res) else SilentTypeError(errorsToReport.head)
+
+ context.reporter.withFreshErrorBuffer {
+ wrapResult(context.reporter, op(this))
}
}
} catch {
@@ -816,14 +793,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
// avoid throwing spurious DivergentImplicit errors
- if (context.hasErrors)
+ if (context.reporter.hasErrors)
setError(tree)
else
withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree))(typer1 =>
if (original != EmptyTree && pt != WildcardType) (
typer1 silent { tpr =>
val withImplicitArgs = tpr.applyImplicitArgs(tree)
- if (tpr.context.hasErrors) tree // silent will wrap it in SilentTypeError anyway
+ if (tpr.context.reporter.hasErrors) tree // silent will wrap it in SilentTypeError anyway
else tpr.typed(withImplicitArgs, mode, pt)
}
orElse { _ =>
@@ -1057,7 +1034,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val silentContext = context.makeImplicit(context.ambiguousErrors)
val res = newTyper(silentContext).typed(
new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt)
- silentContext.firstError match {
+ silentContext.reporter.firstError match {
case Some(err) => context.issue(err)
case None => return res
}
@@ -1749,6 +1726,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
|you want, you must write the annotation class in Java.""".stripMargin)
}
+ warnTypeParameterShadow(tparams1, clazz)
+
if (!isPastTyper) {
for (ann <- clazz.getAnnotation(DeprecatedAttr)) {
val m = companionSymbolOf(clazz, context)
@@ -2151,6 +2130,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val tparams1 = ddef.tparams mapConserve typedTypeDef
val vparamss1 = ddef.vparamss mapConserve (_ mapConserve typedValDef)
+ warnTypeParameterShadow(tparams1, meth)
+
meth.annotations.map(_.completeInfo())
for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1)
@@ -2227,6 +2208,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val typedMods = typedModifiers(tdef.mods)
tdef.symbol.annotations.map(_.completeInfo())
+ warnTypeParameterShadow(tparams1, tdef.symbol)
+
// @specialized should not be pickled when compiling with -no-specialize
if (settings.nospecialization && currentRun.compiles(tdef.symbol)) {
tdef.symbol.removeAnnotation(definitions.SpecializedClass)
@@ -2990,7 +2973,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
ConstructorsOrderError(stat)
}
- if (treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos,
+ if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos,
"a pure expression does nothing in statement position; " +
"you may be omitting necessary parentheses"
)
@@ -3149,7 +3132,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = {
// TODO_NMT: check the assumption that args nonEmpty
def duplErrTree = setError(treeCopy.Apply(tree, fun0, args))
- def duplErrorTree(err: AbsTypeError) = { issue(err); duplErrTree }
+ def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree }
def preSelectOverloaded(fun: Tree): Tree = {
if (fun.hasSymbolField && fun.symbol.isOverloaded) {
@@ -3229,7 +3212,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
(arg1, arg1.tpe.deconst)
}.unzip
}
- if (context.hasErrors)
+ if (context.reporter.hasErrors)
setError(tree)
else {
inferMethodAlternative(fun, undetparams, argTpes, pt)
@@ -3357,8 +3340,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
duplErrTree
} else if (lencmp2 == 0) {
// useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]()
- val note = "Error occurred in an application involving default arguments."
- if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic
+ context.diagUsedDefaults = true
doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)
} else {
rollbackNamesDefaultsOwnerChanges()
@@ -4331,7 +4313,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
c.retyping = true
try {
val res = newTyper(c).typedArgs(args, mode)
- if (c.hasErrors) None else Some(res)
+ if (c.reporter.hasErrors) None else Some(res)
} catch {
case ex: CyclicReference =>
throw ex
@@ -4395,7 +4377,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ => ()
}
}
- typeErrors foreach issue
+ typeErrors foreach context.issue
setError(treeCopy.Apply(tree, fun, args))
}
@@ -4449,7 +4431,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
doTypedApply(tree, fun2, args, mode, pt)
case err: SilentTypeError =>
onError({
- err.reportableErrors foreach issue
+ err.reportableErrors foreach context.issue
args foreach (arg => typed(arg, mode, ErrorType))
setError(tree)
})
@@ -4686,7 +4668,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
else
// before failing due to access, try a dynamic call.
asDynamicCall getOrElse {
- issue(accessibleError.get)
+ context.issue(accessibleError.get)
setError(tree)
}
case _ =>
diff --git a/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala b/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala
index 225f6ca68e..be245347a8 100644
--- a/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala
+++ b/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala
@@ -14,12 +14,10 @@ abstract class StatisticsInfo {
import global._
import scala.reflect.internal.TreesStats.nodeByType
- val phasesShown = List("parser", "typer", "patmat", "erasure", "cleanup")
-
val retainedCount = Statistics.newCounter("#retained tree nodes")
val retainedByType = Statistics.newByClass("#retained tree nodes by type")(Statistics.newCounter(""))
- def print(phase: Phase) = if (phasesShown contains phase.name) {
+ def print(phase: Phase) = if (settings.Ystatistics contains phase.name) {
inform("*** Cumulative statistics at phase " + phase)
retainedCount.value = 0
for (c <- retainedByType.keys)
diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala
index 64cf3d0847..8fed53c89f 100644
--- a/src/compiler/scala/tools/reflect/FastTrack.scala
+++ b/src/compiler/scala/tools/reflect/FastTrack.scala
@@ -5,7 +5,7 @@ import scala.reflect.reify.Taggers
import scala.tools.nsc.typechecker.{ Analyzer, Macros }
import scala.reflect.runtime.Macros.currentMirror
import scala.reflect.api.Universe
-import scala.tools.reflect.quasiquotes.{ Quasiquotes => QuasiquoteImpls }
+import scala.reflect.quasiquotes.{ Quasiquotes => QuasiquoteImpls }
/** Optimizes system macro expansions by hardwiring them directly to their implementations
* bypassing standard reflective load and invoke to avoid the overhead of Java/Scala reflection.
diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
index 923297bafb..1643e0061f 100644
--- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
+++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala
@@ -142,7 +142,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf =>
run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works
phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled
globalPhase = run.typerPhase // amazing... looks like phase and globalPhase are different things, so we need to set them separately
- currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions
+ currentTyper.context.initRootContext() // need to manually set context mode, otherwise typer.silent will throw exceptions
reporter.reset()
val expr3 = withContext(transform(currentTyper, expr2))
diff --git a/src/eclipse/repl/.classpath b/src/eclipse/repl/.classpath
index 601a231aeb..8ff9aabfbf 100644
--- a/src/eclipse/repl/.classpath
+++ b/src/eclipse/repl/.classpath
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="src" path="repl"/>
- <classpathentry combineaccessrules="false" kind="src" path="/asm"/>
- <classpathentry kind="var" path="M2_REPO/jline/jline/2.11/jline-2.11.jar"/>
- <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/>
- <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_COMPILER_CONTAINER"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.11.jar"/>
- <classpathentry kind="output" path="build-quick-repl"/>
+ <classpathentry kind="src" path="repl"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/asm"/>
+ <classpathentry kind="var" path="M2_REPO/jline/jline/2.12/jline-2.12.jar"/>
+ <!-- <classpathentry kind="var" path="SCALA_BASEDIR/build/deps/repl/jline-2.12.jar"/> -->
+ <classpathentry combineaccessrules="false" kind="src" path="/scala-compiler"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="output" path="build-quick-repl"/>
</classpath>
diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala
index 95027a26b1..174254d523 100644
--- a/src/interactive/scala/tools/nsc/interactive/Global.scala
+++ b/src/interactive/scala/tools/nsc/interactive/Global.scala
@@ -142,8 +142,6 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
// don't keep the original owner in presentation compiler runs
// (the map will grow indefinitely, and the only use case is the backend)
override protected def saveOriginalOwner(sym: Symbol) { }
- override protected def originalEnclosingMethod(sym: Symbol) =
- abort("originalOwner is not kept in presentation compiler runs.")
override def forInteractive = true
override protected def synchronizeNames = true
diff --git a/src/library/scala/Option.scala b/src/library/scala/Option.scala
index 905e925f57..66900e7258 100644
--- a/src/library/scala/Option.scala
+++ b/src/library/scala/Option.scala
@@ -211,6 +211,17 @@ sealed abstract class Option[+A] extends Product with Serializable {
/** Tests whether the option contains a given value as an element.
*
+ * @example {{{
+ * // Returns true because Some instance contains string "something" which equals "something".
+ * Some("something") contains "something"
+ *
+ * // Returns false because "something" != "anything".
+ * Some("something") contains "anything"
+ *
+ * // Returns false when method called on None.
+ * None contains "anything"
+ * }}}
+ *
* @param elem the element to test.
* @return `true` if the option has an element that is equal (as
* determined by `==`) to `elem`, `false` otherwise.
@@ -251,6 +262,17 @@ sealed abstract class Option[+A] extends Product with Serializable {
* nonempty '''and''' `pf` is defined for that value.
* Returns $none otherwise.
*
+ * @example {{{
+ * // Returns Some(HTTP) because the partial function covers the case.
+ * Some("http") collect {case "http" => "HTTP"}
+ *
+ * // Returns None because the partial function doesn't cover the case.
+ * Some("ftp") collect {case "http" => "HTTP"}
+ *
+ * // Returns None because None is passed to the collect method.
+ * None collect {case value => value}
+ * }}}
+ *
* @param pf the partial function.
* @return the result of applying `pf` to this $option's
* value (if possible), or $none.
diff --git a/src/library/scala/PartialFunction.scala b/src/library/scala/PartialFunction.scala
index 7f4a9dc45d..fba759eb32 100644
--- a/src/library/scala/PartialFunction.scala
+++ b/src/library/scala/PartialFunction.scala
@@ -20,6 +20,11 @@ package scala
* {{{
* val f: PartialFunction[Int, Any] = { case _ => 1/0 }
* }}}
+ *
+ * It is the responsibility of the caller to call `isDefinedAt` before
+ * calling `apply`, because if `isDefinedAt` is false, it is not guaranteed
+ * `apply` will throw an exception to indicate an error condition. If an
+ * exception is not thrown, evaluation may result in an arbitrary value.
*
* The main distinction between `PartialFunction` and [[scala.Function1]] is
* that the user of a `PartialFunction` may choose to do something different
diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala
index faeb1dcbe2..7f717aa6e4 100644
--- a/src/library/scala/Predef.scala
+++ b/src/library/scala/Predef.scala
@@ -303,7 +303,7 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef {
@inline implicit def augmentString(x: String): StringOps = new StringOps(x)
@inline implicit def unaugmentString(x: StringOps): String = x.repr
- // printing and reading -----------------------------------------------
+ // printing -----------------------------------------------------------
def print(x: Any) = Console.print(x)
def println() = Console.println()
diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala
index 20a328ec8f..2632994a34 100644
--- a/src/library/scala/StringContext.scala
+++ b/src/library/scala/StringContext.scala
@@ -38,7 +38,7 @@ import scala.annotation.tailrec
* To provide your own string interpolator, create an implicit class
* which adds a method to `StringContext`. Here's an example:
* {{{
- * implicit class JsonHelper(val sc: StringContext) extends AnyVal {
+ * implicit class JsonHelper(private val sc: StringContext) extends AnyVal {
* def json(args: Any*): JSONObject = ...
* }
* val x: JSONObject = json"{ a: $a }"
diff --git a/src/library/scala/collection/GenMapLike.scala b/src/library/scala/collection/GenMapLike.scala
index 4e7d359251..bce9740522 100644
--- a/src/library/scala/collection/GenMapLike.scala
+++ b/src/library/scala/collection/GenMapLike.scala
@@ -102,7 +102,7 @@ trait GenMapLike[A, +B, +Repr] extends GenIterableLike[(A, B), Repr] with Equals
*/
def mapValues[C](f: B => C): GenMap[A, C]
- /** Compares two maps structurally; i.e. checks if all mappings
+ /** Compares two maps structurally; i.e., checks if all mappings
* contained in this map are also contained in the other map,
* and vice versa.
*
diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala
index f6f46e158f..660cc5a42a 100644
--- a/src/library/scala/collection/Iterator.scala
+++ b/src/library/scala/collection/Iterator.scala
@@ -1088,6 +1088,9 @@ trait Iterator[+A] extends TraversableOnce[A] {
}
/** Returns this iterator with patched values.
+ * Patching at negative indices is the same as patching starting at 0.
+ * Patching at indices at or larger than the length of the original iterator appends the patch to the end.
+ * If more values are replaced than actually exist, the excess is ignored.
*
* @param from The start index from which to patch
* @param patchElems The iterator of patch values
@@ -1096,18 +1099,33 @@ trait Iterator[+A] extends TraversableOnce[A] {
*/
def patch[B >: A](from: Int, patchElems: Iterator[B], replaced: Int): Iterator[B] = new AbstractIterator[B] {
private var origElems = self
- private var i = 0
- def hasNext: Boolean =
- if (i < from) origElems.hasNext
- else patchElems.hasNext || origElems.hasNext
+ private var i = (if (from > 0) from else 0) // Counts down, switch to patch on 0, -1 means use patch first
+ def hasNext: Boolean = {
+ if (i == 0) {
+ origElems = origElems drop replaced
+ i = -1
+ }
+ origElems.hasNext || patchElems.hasNext
+ }
def next(): B = {
- // We have to do this *first* just in case from = 0.
- if (i == from) origElems = origElems drop replaced
- val result: B =
- if (i < from || !patchElems.hasNext) origElems.next()
- else patchElems.next()
- i += 1
- result
+ if (i == 0) {
+ origElems = origElems drop replaced
+ i = -1
+ }
+ if (i < 0) {
+ if (patchElems.hasNext) patchElems.next()
+ else origElems.next()
+ }
+ else {
+ if (origElems.hasNext) {
+ i -= 1
+ origElems.next()
+ }
+ else {
+ i = -1
+ patchElems.next()
+ }
+ }
}
}
diff --git a/src/library/scala/collection/SeqViewLike.scala b/src/library/scala/collection/SeqViewLike.scala
index 5e31ac4a53..e719f19c78 100644
--- a/src/library/scala/collection/SeqViewLike.scala
+++ b/src/library/scala/collection/SeqViewLike.scala
@@ -154,17 +154,27 @@ trait SeqViewLike[+A,
}
}
+ // Note--for this to work, must ensure 0 <= from and 0 <= replaced
+ // Must also take care to allow patching inside an infinite stream
+ // (patching in an infinite stream is not okay)
trait Patched[B >: A] extends Transformed[B] {
protected[this] val from: Int
protected[this] val patch: GenSeq[B]
protected[this] val replaced: Int
private lazy val plen = patch.length
override def iterator: Iterator[B] = self.iterator patch (from, patch.iterator, replaced)
- def length: Int = self.length + plen - replaced
- def apply(idx: Int): B =
- if (idx < from) self.apply(idx)
- else if (idx < from + plen) patch.apply(idx - from)
+ def length: Int = {
+ val len = self.length
+ val pre = math.min(from, len)
+ val post = math.max(0, len - pre - replaced)
+ pre + plen + post
+ }
+ def apply(idx: Int): B = {
+ val actualFrom = if (self.lengthCompare(from) < 0) self.length else from
+ if (idx < actualFrom) self.apply(idx)
+ else if (idx < actualFrom + plen) patch.apply(idx - actualFrom)
else self.apply(idx - plen + replaced)
+ }
final override protected[this] def viewIdentifier = "P"
}
@@ -210,7 +220,10 @@ trait SeqViewLike[+A,
override def reverse: This = newReversed.asInstanceOf[This]
override def patch[B >: A, That](from: Int, patch: GenSeq[B], replaced: Int)(implicit bf: CanBuildFrom[This, B, That]): That = {
- newPatched(from, patch, replaced).asInstanceOf[That]
+ // Be careful to not evaluate the entire sequence! Patch should work (slowly, perhaps) on infinite streams.
+ val nonNegFrom = math.max(0,from)
+ val nonNegRep = math.max(0,replaced)
+ newPatched(nonNegFrom, patch, nonNegRep).asInstanceOf[That]
// was: val b = bf(repr)
// if (b.isInstanceOf[NoBuilder[_]]) newPatched(from, patch, replaced).asInstanceOf[That]
// else super.patch[B, That](from, patch, replaced)(bf)
diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala
index d3a7db6968..a8731a51b1 100644
--- a/src/library/scala/collection/TraversableLike.scala
+++ b/src/library/scala/collection/TraversableLike.scala
@@ -253,7 +253,7 @@ trait TraversableLike[+A, +Repr] extends Any
b.result
}
- private def filterImpl(p: A => Boolean, isFlipped: Boolean): Repr = {
+ private[scala] def filterImpl(p: A => Boolean, isFlipped: Boolean): Repr = {
val b = newBuilder
for (x <- this)
if (p(x) != isFlipped) b += x
diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala
index a8c4e047ab..13cd99d910 100644
--- a/src/library/scala/collection/TraversableOnce.scala
+++ b/src/library/scala/collection/TraversableOnce.scala
@@ -75,7 +75,7 @@ trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] {
// at least indirectly. Currently, these are `ArrayOps` and `StringOps`.
// It is also implemented in `TraversableOnce[A]`.
/** A version of this collection with all
- * of the operations implemented sequentially (i.e. in a single-threaded manner).
+ * of the operations implemented sequentially (i.e., in a single-threaded manner).
*
* This method returns a reference to this collection. In parallel collections,
* it is redefined to return a sequential implementation of this collection. In
diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala
index d3ff5e8abf..1f97c4c769 100644
--- a/src/library/scala/collection/immutable/Stream.scala
+++ b/src/library/scala/collection/immutable/Stream.scala
@@ -468,8 +468,18 @@ self =>
)
else super.flatMap(f)(bf)
+ override private[scala] def filterImpl(p: A => Boolean, isFlipped: Boolean): Stream[A] = {
+ // optimization: drop leading prefix of elems for which f returns false
+ // var rest = this dropWhile (!p(_)) - forget DRY principle - GC can't collect otherwise
+ var rest = this
+ while (!rest.isEmpty && p(rest.head) == isFlipped) rest = rest.tail
+ // private utility func to avoid `this` on stack (would be needed for the lazy arg)
+ if (rest.nonEmpty) Stream.filteredTail(rest, p, isFlipped)
+ else Stream.Empty
+ }
+
/** Returns all the elements of this `Stream` that satisfy the predicate `p`
- * in a new `Stream` - i.e. it is still a lazy data structure. The order of
+ * in a new `Stream` - i.e., it is still a lazy data structure. The order of
* the elements is preserved
*
* @param p the predicate used to filter the stream.
@@ -481,15 +491,7 @@ self =>
* // produces
* }}}
*/
- override def filter(p: A => Boolean): Stream[A] = {
- // optimization: drop leading prefix of elems for which f returns false
- // var rest = this dropWhile (!p(_)) - forget DRY principle - GC can't collect otherwise
- var rest = this
- while (!rest.isEmpty && !p(rest.head)) rest = rest.tail
- // private utility func to avoid `this` on stack (would be needed for the lazy arg)
- if (rest.nonEmpty) Stream.filteredTail(rest, p)
- else Stream.Empty
- }
+ override def filter(p: A => Boolean): Stream[A] = filterImpl(p, isFlipped = false) // This override is only left in 2.11 because of binary compatibility, see PR #3925
override final def withFilter(p: A => Boolean): StreamWithFilter = new StreamWithFilter(p)
@@ -1187,8 +1189,8 @@ object Stream extends SeqFactory[Stream] {
else cons(start, range(start + step, end, step))
}
- private[immutable] def filteredTail[A](stream: Stream[A], p: A => Boolean) = {
- cons(stream.head, stream.tail filter p)
+ private[immutable] def filteredTail[A](stream: Stream[A], p: A => Boolean, isFlipped: Boolean) = {
+ cons(stream.head, stream.tail.filterImpl(p, isFlipped))
}
private[immutable] def collectedTail[A, B, That](head: B, stream: Stream[A], pf: PartialFunction[A, B], bf: CanBuildFrom[Stream[A], B, That]) = {
diff --git a/src/library/scala/collection/immutable/StringLike.scala b/src/library/scala/collection/immutable/StringLike.scala
index 8e1d950d00..738b294ce6 100644
--- a/src/library/scala/collection/immutable/StringLike.scala
+++ b/src/library/scala/collection/immutable/StringLike.scala
@@ -121,14 +121,14 @@ self =>
}
/** Return all lines in this string in an iterator, excluding trailing line
- * end characters, i.e. apply `.stripLineEnd` to all lines
+ * end characters, i.e., apply `.stripLineEnd` to all lines
* returned by `linesWithSeparators`.
*/
def lines: Iterator[String] =
linesWithSeparators map (line => new WrappedString(line).stripLineEnd)
/** Return all lines in this string in an iterator, excluding trailing line
- * end characters, i.e. apply `.stripLineEnd` to all lines
+ * end characters, i.e., apply `.stripLineEnd` to all lines
* returned by `linesWithSeparators`.
*/
@deprecated("Use `lines` instead.","2.11.0")
diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala
index 4ed0687334..e93a3284dc 100644
--- a/src/library/scala/concurrent/Future.scala
+++ b/src/library/scala/concurrent/Future.scala
@@ -102,7 +102,7 @@ trait Future[+T] extends Awaitable[T] {
/* Callbacks */
- /** When this future is completed successfully (i.e. with a value),
+ /** When this future is completed successfully (i.e., with a value),
* apply the provided partial function to the value if the partial function
* is defined at that value.
*
@@ -118,7 +118,7 @@ trait Future[+T] extends Awaitable[T] {
case _ =>
}
- /** When this future is completed with a failure (i.e. with a throwable),
+ /** When this future is completed with a failure (i.e., with a throwable),
* apply the provided callback to the throwable.
*
* $caughtThrowables
diff --git a/src/library/scala/concurrent/duration/Deadline.scala b/src/library/scala/concurrent/duration/Deadline.scala
index 61cbe47530..a25a478602 100644
--- a/src/library/scala/concurrent/duration/Deadline.scala
+++ b/src/library/scala/concurrent/duration/Deadline.scala
@@ -25,15 +25,15 @@ package scala.concurrent.duration
*/
case class Deadline private (time: FiniteDuration) extends Ordered[Deadline] {
/**
- * Return a deadline advanced (i.e. moved into the future) by the given duration.
+ * Return a deadline advanced (i.e., moved into the future) by the given duration.
*/
def +(other: FiniteDuration): Deadline = copy(time = time + other)
/**
- * Return a deadline moved backwards (i.e. towards the past) by the given duration.
+ * Return a deadline moved backwards (i.e., towards the past) by the given duration.
*/
def -(other: FiniteDuration): Deadline = copy(time = time - other)
/**
- * Calculate time difference between this and the other deadline, where the result is directed (i.e. may be negative).
+ * Calculate time difference between this and the other deadline, where the result is directed (i.e., may be negative).
*/
def -(other: Deadline): FiniteDuration = time - other.time
/**
diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala
index bcbed645a7..5a81710986 100644
--- a/src/library/scala/math/BigDecimal.scala
+++ b/src/library/scala/math/BigDecimal.scala
@@ -617,10 +617,10 @@ extends ScalaNumber with ScalaNumericConversions with Serializable {
*/
def abs: BigDecimal = if (signum < 0) unary_- else this
- /** Returns the sign of this BigDecimal, i.e.
+ /** Returns the sign of this BigDecimal;
* -1 if it is less than 0,
- * +1 if it is greater than 0
- * 0 if it is equal to 0
+ * +1 if it is greater than 0,
+ * 0 if it is equal to 0.
*/
def signum: Int = this.bigDecimal.signum()
diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala
index 689fc0c3e1..abc7371d9f 100644
--- a/src/library/scala/math/BigInt.scala
+++ b/src/library/scala/math/BigInt.scala
@@ -282,10 +282,10 @@ final class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNum
*/
def abs: BigInt = new BigInt(this.bigInteger.abs())
- /** Returns the sign of this BigInt, i.e.
+ /** Returns the sign of this BigInt;
* -1 if it is less than 0,
- * +1 if it is greater than 0
- * 0 if it is equal to 0
+ * +1 if it is greater than 0,
+ * 0 if it is equal to 0.
*/
def signum: Int = this.bigInteger.signum()
diff --git a/src/library/scala/math/Ordering.scala b/src/library/scala/math/Ordering.scala
index d1a4e7c35c..0d7ea8bce2 100644
--- a/src/library/scala/math/Ordering.scala
+++ b/src/library/scala/math/Ordering.scala
@@ -26,7 +26,7 @@ import scala.language.{implicitConversions, higherKinds}
* val pairs = Array(("a", 5, 2), ("c", 3, 1), ("b", 1, 3))
*
* // sort by 2nd element
- * Sorting.quickSort(pairs)(Ordering.by[(String, Int, Int), Int](_._2)
+ * Sorting.quickSort(pairs)(Ordering.by[(String, Int, Int), Int](_._2))
*
* // sort by the 3rd element, then 1st
* Sorting.quickSort(pairs)(Ordering[(Int, String)].on(x => (x._3, x._1)))
diff --git a/src/library/scala/math/PartialOrdering.scala b/src/library/scala/math/PartialOrdering.scala
index 9e35381528..8d7fc32535 100644
--- a/src/library/scala/math/PartialOrdering.scala
+++ b/src/library/scala/math/PartialOrdering.scala
@@ -15,17 +15,24 @@ package math
* latter.
*
* A [[http://en.wikipedia.org/wiki/Partial_order partial ordering]] is a
- * binary relation on a type `T` that is also an equivalence relation on
- * values of type `T`. This relation is exposed as the `lteq` method of
- * the `PartialOrdering` trait. This relation must be:
+ * binary relation on a type `T`, exposed as the `lteq` method of this trait.
+ * This relation must be:
*
* - reflexive: `lteq(x, x) == '''true'''`, for any `x` of type `T`.
- * - anti-symmetric: `lteq(x, y) == '''true'''` and `lteq(y, x) == true`
- * then `equiv(x, y)`, for any `x` and `y` of type `T`.
+ * - anti-symmetric: if `lteq(x, y) == '''true'''` and
+ * `lteq(y, x) == '''true'''`
+ * then `equiv(x, y) == '''true'''`, for any `x` and `y` of type `T`.
* - transitive: if `lteq(x, y) == '''true'''` and
* `lteq(y, z) == '''true'''` then `lteq(x, z) == '''true'''`,
* for any `x`, `y`, and `z` of type `T`.
*
+ * Additionally, a partial ordering induces an
+ * [[http://en.wikipedia.org/wiki/Equivalence_relation equivalence relation]]
+ * on a type `T`: `x` and `y` of type `T` are equivalent if and only if
+ * `lteq(x, y) && lteq(y, x) == '''true'''`. This equivalence relation is
+ * exposed as the `equiv` method, inherited from the
+ * [[scala.math.Equiv Equiv]] trait.
+ *
* @author Geoffrey Washburn
* @version 1.0, 2008-04-0-3
* @since 2.7
diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala
index 5fb24f2a36..f50059ce54 100644
--- a/src/library/scala/runtime/ScalaRunTime.scala
+++ b/src/library/scala/runtime/ScalaRunTime.scala
@@ -62,7 +62,7 @@ object ScalaRunTime {
}
/** Return the class object representing an unboxed value type,
- * e.g. classOf[int], not classOf[java.lang.Integer]. The compiler
+ * e.g., classOf[int], not classOf[java.lang.Integer]. The compiler
* rewrites expressions like 5.getClass to come here.
*/
def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] =
diff --git a/src/library/scala/runtime/SeqCharSequence.scala b/src/library/scala/runtime/SeqCharSequence.scala
index ce7d7afc9e..74e67bb9e7 100644
--- a/src/library/scala/runtime/SeqCharSequence.scala
+++ b/src/library/scala/runtime/SeqCharSequence.scala
@@ -44,5 +44,10 @@ final class ArrayCharSequence(val xs: Array[Char], start: Int, end: Int) extends
new ArrayCharSequence(xs, start1, start1 + newlen)
}
}
- override def toString = xs drop start take length mkString ""
+ override def toString = {
+ val start = math.max(this.start, 0)
+ val end = math.min(xs.length, start + length)
+
+ if (start >= end) "" else new String(xs, start, end - start)
+ }
}
diff --git a/src/library/scala/sys/Prop.scala b/src/library/scala/sys/Prop.scala
index 04c7b5108c..17ae8cb69c 100644
--- a/src/library/scala/sys/Prop.scala
+++ b/src/library/scala/sys/Prop.scala
@@ -20,7 +20,7 @@ package sys
* @since 2.9
*/
trait Prop[+T] {
- /** The full name of the property, e.g. "java.awt.headless".
+ /** The full name of the property, e.g., "java.awt.headless".
*/
def key: String
diff --git a/src/library/scala/sys/package.scala b/src/library/scala/sys/package.scala
index 386bd84113..e493603bc2 100644
--- a/src/library/scala/sys/package.scala
+++ b/src/library/scala/sys/package.scala
@@ -61,16 +61,15 @@ package object sys {
def env: immutable.Map[String, String] = immutable.Map(System.getenv().asScala.toSeq: _*)
/** Register a shutdown hook to be run when the VM exits.
- * The newly created thread is marked as a daemon so it will not
- * interfere with VM shutdown. The hook is automatically registered:
- * the returned value can be ignored, but is available in case the
- * Thread requires further modification. It can also be unregistered
- * by calling ShutdownHookThread#remove().
+ * The hook is automatically registered: the returned value can be ignored,
+ * but is available in case the Thread requires further modification.
+ * It can also be unregistered by calling ShutdownHookThread#remove().
*
* Note that shutdown hooks are NOT guaranteed to be run.
*
* @param body the body of code to run at shutdown
* @return the Thread which will run the shutdown hook.
+ * @see [[scala.sys.ShutdownHookThread]]
*/
def addShutdownHook(body: => Unit): ShutdownHookThread = ShutdownHookThread(body)
diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala
index 2daa4de9a6..8835730d95 100644
--- a/src/library/scala/util/Properties.scala
+++ b/src/library/scala/util/Properties.scala
@@ -107,7 +107,7 @@ private[scala] trait PropertiesTrait {
val versionString = "version " + scalaPropOrElse("version.number", "(unknown)")
val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL")
- /** This is the encoding to use reading in source files, overridden with -encoding
+ /** This is the encoding to use reading in source files, overridden with -encoding.
* Note that it uses "prop" i.e. looks in the scala jar, not the system properties.
*/
def sourceEncoding = scalaPropOrElse("file.encoding", "UTF-8")
diff --git a/src/partest-extras/scala/tools/partest/ASMConverters.scala b/src/partest-extras/scala/tools/partest/ASMConverters.scala
index d618e086f4..67a4e8ae01 100644
--- a/src/partest-extras/scala/tools/partest/ASMConverters.scala
+++ b/src/partest-extras/scala/tools/partest/ASMConverters.scala
@@ -2,70 +2,216 @@ package scala.tools.partest
import scala.collection.JavaConverters._
import scala.tools.asm
-import asm.tree.{ClassNode, MethodNode, InsnList}
+import asm.{tree => t}
/** Makes using ASM from ByteCodeTests more convenient.
*
* Wraps ASM instructions in case classes so that equals and toString work
* for the purpose of bytecode diffing and pretty printing.
*/
-trait ASMConverters {
- // wrap ASM's instructions so we get case class-style `equals` and `toString`
- object instructions {
- def fromMethod(meth: MethodNode): List[Instruction] = {
- val insns = meth.instructions
- val asmToScala = new AsmToScala{ def labelIndex(l: asm.tree.AbstractInsnNode) = insns.indexOf(l) }
-
- asmToScala.mapOver(insns.iterator.asScala.toList).asInstanceOf[List[Instruction]]
+object ASMConverters {
+
+ /**
+ * Transform the instructions of an ASM Method into a list of [[Instruction]]s.
+ */
+ def instructionsFromMethod(meth: t.MethodNode): List[Instruction] = new AsmToScala(meth).instructions
+
+ def convertMethod(meth: t.MethodNode): Method = new AsmToScala(meth).method
+
+ implicit class RichInstructionLists(val self: List[Instruction]) extends AnyVal {
+ def === (other: List[Instruction]) = equivalentBytecode(self, other)
+
+ def dropLinesFrames = self.filterNot(i => i.isInstanceOf[LineNumber] || i.isInstanceOf[FrameEntry])
+
+ private def referencedLabels(instruction: Instruction): Set[Instruction] = instruction match {
+ case Jump(op, label) => Set(label)
+ case LookupSwitch(op, dflt, keys, labels) => (dflt :: labels).toSet
+ case TableSwitch(op, min, max, dflt, labels) => (dflt :: labels).toSet
+ case LineNumber(line, start) => Set(start)
+ case _ => Set.empty
}
- sealed abstract class Instruction { def opcode: String }
- case class Field (opcode: String, desc: String, name: String, owner: String) extends Instruction
- case class Incr (opcode: String, incr: Int, `var`: Int) extends Instruction
- case class Op (opcode: String) extends Instruction
- case class IntOp (opcode: String, operand: Int) extends Instruction
- case class Jump (opcode: String, label: Label) extends Instruction
- case class Ldc (opcode: String, cst: Any) extends Instruction
- case class LookupSwitch (opcode: String, dflt: Label, keys: List[Integer], labels: List[Label]) extends Instruction
- case class TableSwitch (opcode: String, dflt: Label, max: Int, min: Int, labels: List[Label]) extends Instruction
- case class Method (opcode: String, desc: String, name: String, owner: String) extends Instruction
- case class NewArray (opcode: String, desc: String, dims: Int) extends Instruction
- case class TypeOp (opcode: String, desc: String) extends Instruction
- case class VarOp (opcode: String, `var`: Int) extends Instruction
- case class Label (offset: Int) extends Instruction { def opcode: String = "" }
- case class FrameEntry (local: List[Any], stack: List[Any]) extends Instruction { def opcode: String = "" }
- case class LineNumber (line: Int, start: Label) extends Instruction { def opcode: String = "" }
+ def dropStaleLabels = {
+ val definedLabels: Set[Instruction] = self.filter(_.isInstanceOf[Label]).toSet
+ val usedLabels: Set[Instruction] = self.flatMap(referencedLabels)(collection.breakOut)
+ self.filterNot(definedLabels diff usedLabels)
+ }
+
+ def dropNonOp = dropLinesFrames.dropStaleLabels
+ }
+
+ sealed abstract class Instruction extends Product {
+ def opcode: Int
+
+ // toString such that the first field, "opcode: Int", is printed textually.
+ final override def toString() = {
+ import scala.tools.asm.util.Printer.OPCODES
+ def opString(op: Int) = if (OPCODES.isDefinedAt(op)) OPCODES(op) else "?"
+ val printOpcode = opcode != -1
+
+ productPrefix + (
+ if (printOpcode) Iterator(opString(opcode)) ++ productIterator.drop(1)
+ else productIterator
+ ).mkString("(", ", ", ")")
+ }
}
- abstract class AsmToScala {
- import instructions._
+ case class Method(instructions: List[Instruction], handlers: List[ExceptionHandler], localVars: List[LocalVariable])
+
+ case class Field (opcode: Int, owner: String, name: String, desc: String) extends Instruction
+ case class Incr (opcode: Int, `var`: Int, incr: Int) extends Instruction
+ case class Op (opcode: Int) extends Instruction
+ case class IntOp (opcode: Int, operand: Int) extends Instruction
+ case class Jump (opcode: Int, label: Label) extends Instruction
+ case class Ldc (opcode: Int, cst: Any) extends Instruction
+ case class LookupSwitch(opcode: Int, dflt: Label, keys: List[Int], labels: List[Label]) extends Instruction
+ case class TableSwitch (opcode: Int, min: Int, max: Int, dflt: Label, labels: List[Label]) extends Instruction
+ case class Invoke (opcode: Int, owner: String, name: String, desc: String, itf: Boolean) extends Instruction
+ case class NewArray (opcode: Int, desc: String, dims: Int) extends Instruction
+ case class TypeOp (opcode: Int, desc: String) extends Instruction
+ case class VarOp (opcode: Int, `var`: Int) extends Instruction
+ case class Label (offset: Int) extends Instruction { def opcode: Int = -1 }
+ case class FrameEntry (`type`: Int, local: List[Any], stack: List[Any]) extends Instruction { def opcode: Int = -1 }
+ case class LineNumber (line: Int, start: Label) extends Instruction { def opcode: Int = -1 }
+
+ case class ExceptionHandler(start: Label, end: Label, handler: Label, desc: Option[String])
+ case class LocalVariable(name: String, desc: String, signature: Option[String], start: Label, end: Label, index: Int)
+
+ class AsmToScala(asmMethod: t.MethodNode) {
+
+ def instructions: List[Instruction] = asmMethod.instructions.iterator.asScala.toList map apply
+
+ def method: Method = Method(instructions, convertHandlers(asmMethod), convertLocalVars(asmMethod))
- def labelIndex(l: asm.tree.AbstractInsnNode): Int
+ private def labelIndex(l: t.LabelNode): Int = asmMethod.instructions.indexOf(l)
+
+ private def op(i: t.AbstractInsnNode): Int = i.getOpcode
- def mapOver(is: List[Any]): List[Any] = is map {
- case i: asm.tree.AbstractInsnNode => apply(i)
+ private def lst[T](xs: java.util.List[T]): List[T] = if (xs == null) Nil else xs.asScala.toList
+
+ // Heterogenous List[Any] is used in FrameNode: type information about locals / stack values
+ // are stored in a List[Any] (Integer, String or LabelNode), see Javadoc of MethodNode#visitFrame.
+ // Opcodes (eg Opcodes.INTEGER) and Reference types (eg "java/lang/Object") are returned unchanged,
+ // LabelNodes are mapped to their LabelEntry.
+ private def mapOverFrameTypes(is: List[Any]): List[Any] = is map {
+ case i: t.LabelNode => applyLabel(i)
case x => x
}
- def op(i: asm.tree.AbstractInsnNode) = if (asm.util.Printer.OPCODES.isDefinedAt(i.getOpcode)) asm.util.Printer.OPCODES(i.getOpcode) else "?"
- def lst[T](xs: java.util.List[T]): List[T] = if (xs == null) Nil else xs.asScala.toList
- def apply(l: asm.tree.LabelNode): Label = this(l: asm.tree.AbstractInsnNode).asInstanceOf[Label]
- def apply(x: asm.tree.AbstractInsnNode): Instruction = x match {
- case i: asm.tree.FieldInsnNode => Field (op(i), i.desc: String, i.name: String, i.owner: String)
- case i: asm.tree.IincInsnNode => Incr (op(i), i.incr: Int, i.`var`: Int)
- case i: asm.tree.InsnNode => Op (op(i))
- case i: asm.tree.IntInsnNode => IntOp (op(i), i.operand: Int)
- case i: asm.tree.JumpInsnNode => Jump (op(i), this(i.label))
- case i: asm.tree.LdcInsnNode => Ldc (op(i), i.cst: Any)
- case i: asm.tree.LookupSwitchInsnNode => LookupSwitch (op(i), this(i.dflt), lst(i.keys), mapOver(lst(i.labels)).asInstanceOf[List[Label]])
- case i: asm.tree.TableSwitchInsnNode => TableSwitch (op(i), this(i.dflt), i.max: Int, i.min: Int, mapOver(lst(i.labels)).asInstanceOf[List[Label]])
- case i: asm.tree.MethodInsnNode => Method (op(i), i.desc: String, i.name: String, i.owner: String)
- case i: asm.tree.MultiANewArrayInsnNode => NewArray (op(i), i.desc: String, i.dims: Int)
- case i: asm.tree.TypeInsnNode => TypeOp (op(i), i.desc: String)
- case i: asm.tree.VarInsnNode => VarOp (op(i), i.`var`: Int)
- case i: asm.tree.LabelNode => Label (labelIndex(x))
- case i: asm.tree.FrameNode => FrameEntry (mapOver(lst(i.local)), mapOver(lst(i.stack)))
- case i: asm.tree.LineNumberNode => LineNumber (i.line: Int, this(i.start): Label)
+ // avoids some casts
+ private def applyLabel(l: t.LabelNode) = this(l: t.AbstractInsnNode).asInstanceOf[Label]
+
+ private def apply(x: t.AbstractInsnNode): Instruction = x match {
+ case i: t.FieldInsnNode => Field (op(i), i.owner, i.name, i.desc)
+ case i: t.IincInsnNode => Incr (op(i), i.`var`, i.incr)
+ case i: t.InsnNode => Op (op(i))
+ case i: t.IntInsnNode => IntOp (op(i), i.operand)
+ case i: t.JumpInsnNode => Jump (op(i), applyLabel(i.label))
+ case i: t.LdcInsnNode => Ldc (op(i), i.cst: Any)
+ case i: t.LookupSwitchInsnNode => LookupSwitch (op(i), applyLabel(i.dflt), lst(i.keys) map (x => x: Int), lst(i.labels) map applyLabel)
+ case i: t.TableSwitchInsnNode => TableSwitch (op(i), i.min, i.max, applyLabel(i.dflt), lst(i.labels) map applyLabel)
+ case i: t.MethodInsnNode => Invoke (op(i), i.owner, i.name, i.desc, i.itf)
+ case i: t.MultiANewArrayInsnNode => NewArray (op(i), i.desc, i.dims)
+ case i: t.TypeInsnNode => TypeOp (op(i), i.desc)
+ case i: t.VarInsnNode => VarOp (op(i), i.`var`)
+ case i: t.LabelNode => Label (labelIndex(i))
+ case i: t.FrameNode => FrameEntry (i.`type`, mapOverFrameTypes(lst(i.local)), mapOverFrameTypes(lst(i.stack)))
+ case i: t.LineNumberNode => LineNumber (i.line, applyLabel(i.start))
+ }
+
+ private def convertHandlers(method: t.MethodNode): List[ExceptionHandler] = {
+ method.tryCatchBlocks.asScala.map(h => ExceptionHandler(applyLabel(h.start), applyLabel(h.end), applyLabel(h.handler), Option(h.`type`)))(collection.breakOut)
+ }
+
+ private def convertLocalVars(method: t.MethodNode): List[LocalVariable] = {
+ method.localVariables.asScala.map(v => LocalVariable(v.name, v.desc, Option(v.signature), applyLabel(v.start), applyLabel(v.end), v.index))(collection.breakOut)
+ }
+ }
+
+ import collection.mutable.{Map => MMap}
+
+ /**
+ * Bytecode is equal modula local variable numbering and label numbering.
+ */
+ def equivalentBytecode(as: List[Instruction], bs: List[Instruction], varMap: MMap[Int, Int] = MMap(), labelMap: MMap[Int, Int] = MMap()): Boolean = {
+ def same(v1: Int, v2: Int, m: MMap[Int, Int]) = {
+ if (m contains v1) m(v1) == v2
+ else if (m.valuesIterator contains v2) false // v2 is already associated with some different value v1
+ else { m(v1) = v2; true }
+ }
+ def sameVar(v1: Int, v2: Int) = same(v1, v2, varMap)
+ def sameLabel(l1: Label, l2: Label) = same(l1.offset, l2.offset, labelMap)
+ def sameLabels(ls1: List[Label], ls2: List[Label]) = (ls1 corresponds ls2)(sameLabel)
+
+ def sameFrameTypes(ts1: List[Any], ts2: List[Any]) = (ts1 corresponds ts2) {
+ case (t1: Label, t2: Label) => sameLabel(t1, t2)
+ case (x, y) => x == y
+ }
+
+ if (as.isEmpty) bs.isEmpty
+ else if (bs.isEmpty) false
+ else ((as.head, bs.head) match {
+ case (VarOp(op1, v1), VarOp(op2, v2)) => op1 == op2 && sameVar(v1, v2)
+ case (Incr(op1, v1, inc1), Incr(op2, v2, inc2)) => op1 == op2 && sameVar(v1, v2) && inc1 == inc2
+
+ case (l1 @ Label(_), l2 @ Label(_)) => sameLabel(l1, l2)
+ case (Jump(op1, l1), Jump(op2, l2)) => op1 == op2 && sameLabel(l1, l2)
+ case (LookupSwitch(op1, l1, keys1, ls1), LookupSwitch(op2, l2, keys2, ls2)) => op1 == op2 && sameLabel(l1, l2) && keys1 == keys2 && sameLabels(ls1, ls2)
+ case (TableSwitch(op1, min1, max1, l1, ls1), TableSwitch(op2, min2, max2, l2, ls2)) => op1 == op2 && min1 == min2 && max1 == max2 && sameLabel(l1, l2) && sameLabels(ls1, ls2)
+ case (LineNumber(line1, l1), LineNumber(line2, l2)) => line1 == line2 && sameLabel(l1, l2)
+ case (FrameEntry(tp1, loc1, stk1), FrameEntry(tp2, loc2, stk2)) => tp1 == tp2 && sameFrameTypes(loc1, loc2) && sameFrameTypes(stk1, stk2)
+
+ // this needs to go after the above. For example, Label(1) may not equal Label(1), if before
+ // the left 1 was associated with another right index.
+ case (a, b) if a == b => true
+
+ case _ => false
+ }) && equivalentBytecode(as.tail, bs.tail, varMap, labelMap)
+ }
+
+ def applyToMethod(method: t.MethodNode, instructions: List[Instruction]): Unit = {
+ val asmLabel = createLabelNodes(instructions)
+ instructions.foreach(visitMethod(method, _, asmLabel))
+ }
+
+ /**
+ * Convert back a [[Method]] to ASM land. The code is emitted into the parameter `asmMethod`.
+ */
+ def applyToMethod(asmMethod: t.MethodNode, method: Method): Unit = {
+ val asmLabel = createLabelNodes(method.instructions)
+ method.instructions.foreach(visitMethod(asmMethod, _, asmLabel))
+ method.handlers.foreach(h => asmMethod.visitTryCatchBlock(asmLabel(h.start), asmLabel(h.end), asmLabel(h.handler), h.desc.orNull))
+ method.localVars.foreach(v => asmMethod.visitLocalVariable(v.name, v.desc, v.signature.orNull, asmLabel(v.start), asmLabel(v.end), v.index))
+ }
+
+ private def createLabelNodes(instructions: List[Instruction]): Map[Label, asm.Label] = {
+ val labels = instructions collect {
+ case l: Label => l
}
+ assert(labels.distinct == labels, s"Duplicate labels in: $labels")
+ labels.map(l => (l, new asm.Label())).toMap
+ }
+
+ private def frameTypesToAsm(l: List[Any], asmLabel: Map[Label, asm.Label]): List[Object] = l map {
+ case l: Label => asmLabel(l)
+ case x => x.asInstanceOf[Object]
+ }
+
+ private def visitMethod(method: t.MethodNode, instruction: Instruction, asmLabel: Map[Label, asm.Label]): Unit = instruction match {
+ case Field(op, owner, name, desc) => method.visitFieldInsn(op, owner, name, desc)
+ case Incr(op, vr, incr) => method.visitIincInsn(vr, incr)
+ case Op(op) => method.visitInsn(op)
+ case IntOp(op, operand) => method.visitIntInsn(op, operand)
+ case Jump(op, label) => method.visitJumpInsn(op, asmLabel(label))
+ case Ldc(op, cst) => method.visitLdcInsn(cst)
+ case LookupSwitch(op, dflt, keys, labels) => method.visitLookupSwitchInsn(asmLabel(dflt), keys.toArray, (labels map asmLabel).toArray)
+ case TableSwitch(op, min, max, dflt, labels) => method.visitTableSwitchInsn(min, max, asmLabel(dflt), (labels map asmLabel).toArray: _*)
+ case Invoke(op, owner, name, desc, itf) => method.visitMethodInsn(op, owner, name, desc, itf)
+ case NewArray(op, desc, dims) => method.visitMultiANewArrayInsn(desc, dims)
+ case TypeOp(op, desc) => method.visitTypeInsn(op, desc)
+ case VarOp(op, vr) => method.visitVarInsn(op, vr)
+ case l: Label => method.visitLabel(asmLabel(l))
+ case FrameEntry(tp, local, stack) => method.visitFrame(tp, local.length, frameTypesToAsm(local, asmLabel).toArray, stack.length, frameTypesToAsm(stack, asmLabel).toArray)
+ case LineNumber(line, start) => method.visitLineNumber(line, asmLabel(start))
}
-} \ No newline at end of file
+}
diff --git a/src/partest-extras/scala/tools/partest/BytecodeTest.scala b/src/partest-extras/scala/tools/partest/BytecodeTest.scala
index 1e4362fcde..3261cada37 100644
--- a/src/partest-extras/scala/tools/partest/BytecodeTest.scala
+++ b/src/partest-extras/scala/tools/partest/BytecodeTest.scala
@@ -3,7 +3,7 @@ package scala.tools.partest
import scala.tools.nsc.util.JavaClassPath
import scala.collection.JavaConverters._
import scala.tools.asm.{ClassWriter, ClassReader}
-import scala.tools.asm.tree.{ClassNode, MethodNode, InsnList}
+import scala.tools.asm.tree._
import java.io.{FileOutputStream, FileInputStream, File => JFile, InputStream}
import AsmNode._
@@ -28,8 +28,8 @@ import AsmNode._
* See test/files/jvm/bytecode-test-example for an example of bytecode test.
*
*/
-abstract class BytecodeTest extends ASMConverters {
- import instructions._
+abstract class BytecodeTest {
+ import ASMConverters._
/** produce the output to be compared against a checkfile */
protected def show(): Unit
@@ -38,8 +38,8 @@ abstract class BytecodeTest extends ASMConverters {
// asserts
def sameBytecode(methA: MethodNode, methB: MethodNode) = {
- val isa = instructions.fromMethod(methA)
- val isb = instructions.fromMethod(methB)
+ val isa = instructionsFromMethod(methA)
+ val isb = instructionsFromMethod(methB)
if (isa == isb) println("bytecode identical")
else diffInstructions(isa, isb)
}
@@ -81,18 +81,16 @@ abstract class BytecodeTest extends ASMConverters {
}
}
- // bytecode is equal modulo local variable numbering
- def equalsModuloVar(a: Instruction, b: Instruction) = (a, b) match {
- case _ if a == b => true
- case (VarOp(op1, _), VarOp(op2, _)) if op1 == op2 => true
- case _ => false
- }
-
- def similarBytecode(methA: MethodNode, methB: MethodNode, similar: (Instruction, Instruction) => Boolean) = {
- val isa = fromMethod(methA)
- val isb = fromMethod(methB)
+ /**
+ * Compare the bytecodes of two methods.
+ *
+ * For the `similar` function, you probably want to pass [[ASMConverters.equivalentBytecode]].
+ */
+ def similarBytecode(methA: MethodNode, methB: MethodNode, similar: (List[Instruction], List[Instruction]) => Boolean) = {
+ val isa = instructionsFromMethod(methA)
+ val isb = instructionsFromMethod(methB)
if (isa == isb) println("bytecode identical")
- else if ((isa, isb).zipped.forall { case (a, b) => similar(a, b) }) println("bytecode similar")
+ else if (similar(isa, isb)) println("bytecode similar")
else diffInstructions(isa, isb)
}
diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
index f814a746f5..fcef4dd6be 100644
--- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala
+++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
@@ -388,11 +388,11 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
case Literal(const) => LiteralAnnotArg(const)
case Apply(ArrayModule, args) => ArrayAnnotArg(args map encodeJavaArg toArray)
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => NestedAnnotArg(treeToAnnotation(arg))
- case _ => throw new Exception("unexpected java argument shape $arg: literals, arrays and nested annotations are supported")
+ case _ => throw new Exception(s"unexpected java argument shape $arg: literals, arrays and nested annotations are supported")
}
def encodeJavaArgs(args: List[Tree]): List[(Name, ClassfileAnnotArg)] = args match {
case AssignOrNamedArg(Ident(name), arg) :: rest => (name, encodeJavaArg(arg)) :: encodeJavaArgs(rest)
- case arg :: rest => throw new Exception("unexpected java argument shape $arg: only AssignOrNamedArg trees are supported")
+ case arg :: rest => throw new Exception(s"unexpected java argument shape $arg: only AssignOrNamedArg trees are supported")
case Nil => Nil
}
val atp = tpt.tpe
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index bf560a21e5..02578e2038 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -836,12 +836,18 @@ trait Definitions extends api.StandardDefinitions {
def typeOfMemberNamedHead(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.head)())
def typeOfMemberNamedApply(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.apply)(IntTpe))
def typeOfMemberNamedDrop(tp: Type) = typeArgOfBaseTypeOr(tp, SeqClass)(resultOfMatchingMethod(tp, nme.drop)(IntTpe))
- def typesOfSelectors(tp: Type) = getterMemberTypes(tp, productSelectors(tp))
+ def typesOfSelectors(tp: Type) =
+ if (isTupleType(tp)) tp.typeArgs
+ else getterMemberTypes(tp, productSelectors(tp))
+
// SI-8128 Still using the type argument of the base type at Seq/Option if this is an old-style (2.10 compatible)
// extractor to limit exposure to regressions like the reported problem with existentials.
// TODO fix the existential problem in the general case, see test/pending/pos/t8128.scala
private def typeArgOfBaseTypeOr(tp: Type, baseClass: Symbol)(or: => Type): Type = (tp baseType baseClass).typeArgs match {
- case x :: Nil => x
+ case x :: Nil =>
+ val x1 = x
+ val x2 = repackExistential(x1)
+ x2
case _ => or
}
diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala
index 2caa30d27e..759bd2e791 100644
--- a/src/reflect/scala/reflect/internal/ReificationSupport.scala
+++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala
@@ -866,7 +866,7 @@ trait ReificationSupport { self: SymbolTable =>
protected def mkCases(cases: List[Tree]): List[CaseDef] = cases.map {
case c: CaseDef => c
- case tree => throw new IllegalArgumentException("$tree is not valid representation of pattern match case")
+ case tree => throw new IllegalArgumentException(s"$tree is not valid representation of pattern match case")
}
object SyntacticPartialFunction extends SyntacticPartialFunctionExtractor {
diff --git a/src/reflect/scala/reflect/internal/Reporting.scala b/src/reflect/scala/reflect/internal/Reporting.scala
index 423127803e..f2de83bc5d 100644
--- a/src/reflect/scala/reflect/internal/Reporting.scala
+++ b/src/reflect/scala/reflect/internal/Reporting.scala
@@ -89,6 +89,9 @@ abstract class Reporter {
def count(severity: Severity): Int
def resetCount(severity: Severity): Unit
+ def errorCount: Int = count(ERROR)
+ def warningCount: Int = count(WARNING)
+
def hasErrors: Boolean = count(ERROR) > 0
def hasWarnings: Boolean = count(WARNING) > 0
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 6848c357c5..99ff6a10b4 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -99,17 +99,18 @@ trait StdNames {
val SINGLETON_SUFFIX: String = ".type"
- val ANON_CLASS_NAME: NameType = "$anon"
- val ANON_FUN_NAME: NameType = "$anonfun"
- val EMPTY: NameType = ""
- val EMPTY_PACKAGE_NAME: NameType = "<empty>"
- val IMPL_CLASS_SUFFIX = "$class"
- val IMPORT: NameType = "<import>"
- val MODULE_SUFFIX_NAME: NameType = MODULE_SUFFIX_STRING
- val MODULE_VAR_SUFFIX: NameType = "$module"
- val PACKAGE: NameType = "package"
- val ROOT: NameType = "<root>"
- val SPECIALIZED_SUFFIX: NameType = "$sp"
+ val ANON_CLASS_NAME: NameType = "$anon"
+ val DELAMBDAFY_LAMBDA_CLASS_NAME: NameType = "$lambda"
+ val ANON_FUN_NAME: NameType = "$anonfun"
+ val EMPTY: NameType = ""
+ val EMPTY_PACKAGE_NAME: NameType = "<empty>"
+ val IMPL_CLASS_SUFFIX = "$class"
+ val IMPORT: NameType = "<import>"
+ val MODULE_SUFFIX_NAME: NameType = MODULE_SUFFIX_STRING
+ val MODULE_VAR_SUFFIX: NameType = "$module"
+ val PACKAGE: NameType = "package"
+ val ROOT: NameType = "<root>"
+ val SPECIALIZED_SUFFIX: NameType = "$sp"
// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
@@ -473,7 +474,7 @@ trait StdNames {
)
def localDummyName(clazz: Symbol): TermName = newTermName(LOCALDUMMY_PREFIX + clazz.name + ">")
- def superName(name: Name): TermName = newTermName(SUPER_PREFIX_STRING + name)
+ def superName(name: Name, mix: Name = EMPTY): TermName = newTermName(SUPER_PREFIX_STRING + name + (if (mix.isEmpty) "" else "$" + mix))
/** The name of an accessor for protected symbols. */
def protName(name: Name): TermName = newTermName(PROTECTED_PREFIX + name)
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index b6cce4524b..44fce2c9ab 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -74,15 +74,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
}
}
- protected def originalEnclosingMethod(sym: Symbol): Symbol = {
- if (sym.isMethod || sym == NoSymbol) sym
- else {
- val owner = sym.originalOwner
- if (sym.isLocalDummy) owner.enclClass.primaryConstructor
- else originalEnclosingMethod(owner)
- }
- }
-
def symbolOf[T: WeakTypeTag]: TypeSymbol = weakTypeOf[T].typeSymbolDirect.asType
abstract class SymbolContextApiImpl extends SymbolApi {
@@ -795,9 +786,10 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
info.firstParent.typeSymbol == AnyValClass && !isPrimitiveValueClass
final def isMethodWithExtension =
- isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) && !isMacro
+ isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) && !isMacro && !isSpecialized
final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME)
+ final def isDelambdafyFunction = isSynthetic && (name containsName tpnme.DELAMBDAFY_LAMBDA_CLASS_NAME)
final def isDefinedInPackage = effectiveOwner.isPackageClass
final def needsFlatClasses = phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass
@@ -2122,16 +2114,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
* is not one. */
def enclosingPackage: Symbol = enclosingPackageClass.companionModule
- /** Return the original enclosing method of this symbol. It should return
- * the same thing as enclMethod when called before lambda lift,
- * but it preserves the original nesting when called afterwards.
- *
- * @note This method is NOT available in the presentation compiler run. The
- * originalOwner map is not populated for memory considerations (the symbol
- * may hang on to lazy types and in turn to whole (outdated) compilation units.
- */
- def originalEnclosingMethod: Symbol = Symbols.this.originalEnclosingMethod(this)
-
/** The method or class which logically encloses the current symbol.
* If the symbol is defined in the initialization part of a template
* this is the template's primary constructor, otherwise it is
@@ -3532,7 +3514,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
override def rawInfo: Type = NoType
override def accessBoundary(base: Symbol): Symbol = enclosingRootClass
def cloneSymbolImpl(owner: Symbol, newFlags: Long) = abort("NoSymbol.clone()")
- override def originalEnclosingMethod = this
}
protected def makeNoSymbol: NoSymbol = new NoSymbol
diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
index 6584d80de3..b7f229b6e5 100644
--- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala
+++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
@@ -807,31 +807,26 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
// field containing the cache for structural calls.
if (mods.isStatic) module.moduleClass.orElse(clazz) else clazz
- /** Methods which need to be treated with care
- * because they either are getSimpleName or call getSimpleName:
+ /**
+ * Certain method of the Java reflection api cannot be used on classfiles created by Scala.
+ * See the comment in test/files/jvm/javaReflection/Test.scala. The methods are
*
* public String getSimpleName()
* public boolean isAnonymousClass()
* public boolean isLocalClass()
* public String getCanonicalName()
- *
- * A typical manifestation:
- *
- * // java.lang.Error: sOwner(class Test$A$1) has failed
- * // Caused by: java.lang.InternalError: Malformed class name
- * // at java.lang.Class.getSimpleName(Class.java:1133)
- * // at java.lang.Class.isAnonymousClass(Class.java:1188)
- * // at java.lang.Class.isLocalClass(Class.java:1199)
- * // (see t5256c.scala for more details)
+ * public boolean isSynthetic()
*
* TODO - find all such calls and wrap them.
* TODO - create mechanism to avoid the recurrence of unwrapped calls.
*/
implicit class RichClass(jclazz: jClass[_]) {
- // `jclazz.isLocalClass` doesn't work because of problems with `getSimpleName`
- // hence we have to approximate by removing the `isAnonymousClass` check
-// def isLocalClass0: Boolean = jclazz.isLocalClass
- def isLocalClass0: Boolean = jclazz.getEnclosingMethod != null || jclazz.getEnclosingConstructor != null
+ // As explained in the javaReflection test, Class.isLocalClass is true for all non-member
+ // nested classes in Scala. This is fine per se, however the implementation may throw an
+ // InternalError. We therefore re-implement it here.
+ // TODO: this method should be renamed to `isLocalOrAnonymousClass`.
+ // due to bin compat that's only possible in 2.12, we cannot introduce a new alias in 2.11.
+ def isLocalClass0: Boolean = jclazz.getEnclosingClass != null && !jclazz.isMemberClass
}
/**
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala
index 8ea8759ee5..6e30b73e0e 100644
--- a/src/repl/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala
@@ -1121,7 +1121,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set
def apply(line: String): Result = debugging(s"""parse("$line")""") {
var isIncomplete = false
- currentRun.reporting.withIncompleteHandler((_, _) => isIncomplete = true) {
+ currentRun.parsing.withIncompleteHandler((_, _) => isIncomplete = true) {
reporter.reset()
val trees = newUnitParser(line).parseStats()
if (reporter.hasErrors) Error
diff --git a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala
index f4cbcb50fe..a37cdc2ec8 100644
--- a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala
+++ b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala
@@ -116,7 +116,7 @@ trait MemberHandlers {
else any2stringOf(path, maxStringElements)
val vidString =
- if (replProps.vids) s"""" + " @ " + "%%8x".format(System.identityHashCode($path)) + " """.trim
+ if (replProps.vids) s"""" + f"@$${System.identityHashCode($path)}%8x" + """"
else ""
""" + "%s%s: %s = " + %s""".format(string2code(prettyName), vidString, string2code(req typeOf name), resultString)
diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala
index ccf18b76de..cbf8ff22ba 100644
--- a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala
@@ -208,7 +208,7 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax
super.skipDocComment()
}
override def skipBlockComment(): Unit = {
- inDocComment = false
+ inDocComment = false // ??? this means docBuffer won't receive contents of this comment???
docBuffer = new StringBuilder("/*")
super.skipBlockComment()
}
@@ -217,9 +217,10 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax
def foundStarComment(start: Int, end: Int) = try {
val str = docBuffer.toString
val pos = Position.range(unit.source, start, start, end)
- unit.comment(pos, str)
- if (inDocComment)
+ if (inDocComment) {
+ signalParsedDocComment(str, pos)
lastDoc = DocComment(str, pos)
+ }
true
} finally {
docBuffer = null
diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala
index 2ea3a0eb7c..4b40d25c17 100644
--- a/src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocGlobal.scala
@@ -11,6 +11,7 @@ import reporters.Reporter
import typechecker.Analyzer
import scala.reflect.internal.util.{ BatchSourceFile, RangePosition }
+
trait ScaladocGlobalTrait extends Global {
outer =>
diff --git a/src/scaladoc/scala/tools/nsc/doc/Settings.scala b/src/scaladoc/scala/tools/nsc/doc/Settings.scala
index a8e1dee4a0..44683f1755 100644
--- a/src/scaladoc/scala/tools/nsc/doc/Settings.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/Settings.scala
@@ -66,7 +66,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_))
val docsourceurl = StringSetting (
"-doc-source-url",
"url",
- "A URL pattern used to build links to template sources; use variables, for example: ?{TPL_NAME} ('Seq'), ?{TPL_OWNER} ('scala.collection'), ?{FILE_PATH} ('scala/collection/Seq')",
+ s"A URL pattern used to link to the source file; the following variables are available: €{TPL_NAME}, €{TPL_OWNER} and respectively €{FILE_PATH}. For example, for `scala.collection.Seq`, the variables will be expanded to `Seq`, `scala.collection` and respectively `scala/collection/Seq` (without the backquotes). To obtain a relative path for €{FILE_PATH} instead of an absolute one, use the ${sourcepath.name} setting.",
""
)
diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
index 19cc27b40b..7cd8fa8e51 100755
--- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
@@ -131,18 +131,19 @@ trait CommentFactoryBase { this: MemberLookupBase =>
/** Javadoc tags that should be replaced by something useful, such as wiki
* syntax, or that should be dropped. */
private val JavadocTags =
- new Regex("""\{\@(code|docRoot|inheritDoc|link|linkplain|literal|value)([^}]*)\}""")
+ new Regex("""\{\@(code|docRoot|linkplain|link|literal|value)\p{Zs}*([^}]*)\}""")
/** Maps a javadoc tag to a useful wiki replacement, or an empty string if it cannot be salvaged. */
- private def javadocReplacement(mtch: Regex.Match): String = mtch.group(1) match {
- case "code" => "`" + mtch.group(2) + "`"
- case "docRoot" => ""
- case "inheritDoc" => ""
- case "link" => "`" + mtch.group(2) + "`"
- case "linkplain" => "`" + mtch.group(2) + "`"
- case "literal" => mtch.group(2)
- case "value" => "`" + mtch.group(2) + "`"
- case _ => ""
+ private def javadocReplacement(mtch: Regex.Match): String = {
+ mtch.group(1) match {
+ case "code" => "<code>" + mtch.group(2) + "</code>"
+ case "docRoot" => ""
+ case "link" => "`[[" + mtch.group(2) + "]]`"
+ case "linkplain" => "[[" + mtch.group(2) + "]]"
+ case "literal" => "`" + mtch.group(2) + "`"
+ case "value" => "`" + mtch.group(2) + "`"
+ case _ => ""
+ }
}
/** Safe HTML tags that can be kept. */
@@ -680,11 +681,10 @@ trait CommentFactoryBase { this: MemberLookupBase =>
jump("[[")
val parens = 2 + repeatJump('[')
val stop = "]" * parens
- //println("link with " + parens + " matching parens")
- val target = readUntil { check(stop) || check(" ") }
+ val target = readUntil { check(stop) || isWhitespaceOrNewLine(char) }
val title =
if (!check(stop)) Some({
- jump(" ")
+ jumpWhitespaceOrNewLine()
inline(check(stop))
})
else None
@@ -723,49 +723,15 @@ trait CommentFactoryBase { this: MemberLookupBase =>
*/
def normalizeIndentation(_code: String): String = {
- val code = _code.trim
- var maxSkip = Integer.MAX_VALUE
- var crtSkip = 0
- var wsArea = true
- var index = 0
- var firstLine = true
- var emptyLine = true
-
- while (index < code.length) {
- code(index) match {
- case ' ' =>
- if (wsArea)
- crtSkip += 1
- case c =>
- wsArea = (c == '\n')
- maxSkip = if (firstLine || emptyLine) maxSkip else if (maxSkip <= crtSkip) maxSkip else crtSkip
- crtSkip = if (c == '\n') 0 else crtSkip
- firstLine = if (c == '\n') false else firstLine
- emptyLine = if (c == '\n') true else false
- }
- index += 1
- }
+ val code = _code.replaceAll("\\s+$", "").dropWhile(_ == '\n') // right-trim + remove all leading '\n'
+ val lines = code.split("\n")
- if (maxSkip == 0)
- code
- else {
- index = 0
- val builder = new StringBuilder
- while (index < code.length) {
- builder.append(code(index))
- if (code(index) == '\n') {
- // we want to skip as many spaces are available, if there are less spaces (like on empty lines, do not
- // over-consume them)
- index += 1
- val limit = index + maxSkip
- while ((index < code.length) && (code(index) == ' ') && index < limit)
- index += 1
- }
- else
- index += 1
- }
- builder.toString
- }
+ // maxSkip - size of the longest common whitespace prefix of non-empty lines
+ val nonEmptyLines = lines.filter(_.trim.nonEmpty)
+ val maxSkip = if (nonEmptyLines.isEmpty) 0 else nonEmptyLines.map(line => line.prefixLength(_ == ' ')).min
+
+ // remove common whitespace prefix
+ lines.map(line => if (line.trim.nonEmpty) line.substring(maxSkip) else line).mkString("\n")
}
def checkParaEnded(): Boolean = {
@@ -899,6 +865,8 @@ trait CommentFactoryBase { this: MemberLookupBase =>
def jumpWhitespace() = jumpUntil(!isWhitespace(char))
+ def jumpWhitespaceOrNewLine() = jumpUntil(!isWhitespaceOrNewLine(char))
+
/* READERS */
final def readUntil(c: Char): String = {
@@ -938,5 +906,7 @@ trait CommentFactoryBase { this: MemberLookupBase =>
/* CHARS CLASSES */
def isWhitespace(c: Char) = c == ' ' || c == '\t'
+
+ def isWhitespaceOrNewLine(c: Char) = isWhitespace(c) || c == '\n'
}
}
diff --git a/test/files/jvm/innerClassAttribute.check b/test/files/jvm/innerClassAttribute.check
new file mode 100644
index 0000000000..20518aa49e
--- /dev/null
+++ b/test/files/jvm/innerClassAttribute.check
@@ -0,0 +1,54 @@
+#partest !-Ydelambdafy:method
+-- A4 --
+A4$$anonfun$f$1 / null / null / 17
+A4$$anonfun$f$1 / null / null / 17
+A4 / f / (Lscala/collection/immutable/List;)Lscala/collection/immutable/List;
+-- A19 --
+A19$$anonfun$1 / null / null / 17
+A19$$anonfun$2 / null / null / 17
+A19$$anonfun$3 / null / null / 17
+A19$$anonfun$1 / null / null / 17
+A19$$anonfun$2 / null / null / 17
+A19$$anonfun$3 / null / null / 17
+A19 / null / null
+A19 / null / null
+A19 / null / null
+-- A20 --
+A20$$anonfun$4 / null / null / 17
+fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`
+A20$$anonfun$4 / null / null / 17
+A20$$anonfun$4$$anonfun$apply$1 / null / null / 17
+A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
+fun2 () => (): itself and the outer closure
+A20$$anonfun$4 / null / null / 17
+A20$$anonfun$4$$anonfun$apply$1 / null / null / 17
+fun3 () => () => (): itself, the outer closure and its child closure
+A20$$anonfun$4 / null / null / 17
+A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
+A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
+fun4: () => 1: itself and the two outer closures
+A20$$anonfun$4 / null / null / 17
+A20$$anonfun$4$$anonfun$apply$3 / null / null / 17
+A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17
+enclosing: nested closures have the apply method of the outer closure
+A20 / null / null
+A20$$anonfun$4 / apply / ()Lscala/Function0;
+A20$$anonfun$4 / apply / ()Lscala/Function0;
+A20$$anonfun$4$$anonfun$apply$3 / apply / ()Lscala/Function0;
+#partest -Ydelambdafy:method
+-- A4 --
+null / null / null
+-- A19 --
+null / null / null
+null / null / null
+null / null / null
+-- A20 --
+fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`
+fun2 () => (): itself and the outer closure
+fun3 () => () => (): itself, the outer closure and its child closure
+fun4: () => 1: itself and the two outer closures
+enclosing: nested closures have the apply method of the outer closure
+null / null / null
+null / null / null
+null / null / null
+null / null / null
diff --git a/test/files/jvm/innerClassAttribute/Classes_1.scala b/test/files/jvm/innerClassAttribute/Classes_1.scala
index 0875d9160c..9c3ea7f013 100644
--- a/test/files/jvm/innerClassAttribute/Classes_1.scala
+++ b/test/files/jvm/innerClassAttribute/Classes_1.scala
@@ -74,7 +74,7 @@ class A14 {
object A15 {
def f = {
- class B { // static (does not have an outer pointer)
+ class B { // non-static, even though it doesn't have an outer pointer
class C // non-static
}
}
@@ -90,10 +90,98 @@ class A16 {
class V extends A6
new A6 { }
}
+
+ new A6 { }
}
class A17 {
object B {
- class C // not static, has an outer pointer.
+ class C // not static, also has an outer pointer.
+ }
+}
+
+class A18 {
+ def f = {
+ def g = {
+ class A
+ new A6 { }
+ val y = {
+ if ((new Object).hashCode() == 1) {class B {} ; new B} else 2
+ if ((new Object).hashCode() == 1) new A6 { } else "haifish"
+ }
+ }
+ }
+}
+
+class A19 {
+ ((x: Int) => x + 3)
+
+ val x = {
+ ((x: Int) => x + 1)
+ }
+
+ {
+ ((x: Int) => x + 2)
+ }
+}
+
+class A20 {
+ () => {
+ {() => ()}
+ {() => () => 1}
+ }
+}
+
+class A21 {
+ class I1
+ def f = { class J1 }
+}
+object A21 {
+ class I2
+ object I3 {
+ class J2 // static
+ }
+ def g = { class J3 } // non-static
+ val x = { class J4 } // non-static
+ {
+ class J5 // non-static (!)
+ new J5
+ }
+}
+
+class A22 {
+ class C
+ object C {
+ class D // inner class of C$, not of C. Not added to the inner class table of C, only to C$
+ }
+}
+
+class A23 {
+ def f = {
+ val a = new Java_A_1()
+ val c = new Java_A_1.C()
+ val d = new Java_A_1.C.D()
+ val e = new c.E()
+ val f = new a.F()
+ val g = new f.G()
+ }
+}
+
+trait A24Sym
+
+trait A24Base {
+ // trait with concrete members: interface plus (absract) impl class
+ trait DefinitionsApi {
+ def Abs: A24Sym
+ def Conc: A24Sym = new A24Sym { }
+ }
+}
+
+trait A24 extends A24Base {
+ class DefinitionsClass extends DefinitionsApi {
+ // bridge methods are generated for Abs and Conc. there used to be a bug: the bridge symbol was a ModuleSymbol,
+ // calling companionClass would return NoSymbol. i changed erasure to make the bridge symbol is a MethodSymbol.
+ object Abs extends A24Sym
+ override object Conc extends A24Sym
}
}
diff --git a/test/files/jvm/innerClassAttribute/Java_A_1.java b/test/files/jvm/innerClassAttribute/Java_A_1.java
new file mode 100644
index 0000000000..3357d05e2b
--- /dev/null
+++ b/test/files/jvm/innerClassAttribute/Java_A_1.java
@@ -0,0 +1,10 @@
+public class Java_A_1 {
+ public static class C {
+ public static class D { }
+ public class E { }
+ }
+
+ public class F {
+ public class G { }
+ }
+}
diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala
index 6cf60ab92d..882edbcdd7 100644
--- a/test/files/jvm/innerClassAttribute/Test.scala
+++ b/test/files/jvm/innerClassAttribute/Test.scala
@@ -43,10 +43,30 @@ object Test extends BytecodeTest {
assertSame(node.access, flags)
}
- def assertEnclosingMethod(enclosingMethod: EnclosingMethod, outerClass: String, name: String, descriptor: String) = {
- assertSame(enclosingMethod.outerClass, outerClass)
- assertSame(enclosingMethod.name, name)
- assertSame(enclosingMethod.descriptor, descriptor)
+ def assertEnclosingMethod(className: String, outerClass: String, name: String, descriptor: String) = {
+ val encl = enclosingMethod(className)
+ assertSame(encl.outerClass, outerClass)
+ assertSame(encl.name, name)
+ assertSame(encl.descriptor, descriptor)
+ }
+
+ def assertNoEnclosingMethod(className: String) = {
+ assertSame(enclosingMethod(className).outerClass, null)
+ }
+
+ def printInnerClassNodes(className: String) = {
+ for (n <- innerClassNodes(className)) {
+ println(s"${n.name} / ${n.outerName} / ${n.innerName} / ${n.access}")
+ }
+ }
+
+ def printEnclosingMethod(className: String) = {
+ val e = enclosingMethod(className)
+ println(s"${e.outerClass} / ${e.name} / ${e.descriptor}")
+ }
+
+ def lambdaClass(anonfunName: String, lambdaName: String): String = {
+ if (classpath.findClass(anonfunName).isDefined) anonfunName else lambdaName
}
def testA1() = {
@@ -78,13 +98,11 @@ object Test extends BytecodeTest {
}
def testA4() = {
- val List(an1) = innerClassNodes("A4")
- assertAnonymous(an1, "A4$$anonfun$f$1")
- val List(an2) = innerClassNodes("A4$$anonfun$f$1")
- assertAnonymous(an2, "A4$$anonfun$f$1")
- assertEnclosingMethod(
- enclosingMethod("A4$$anonfun$f$1"),
- "A4", "f", "(Lscala/collection/immutable/List;)Lscala/collection/immutable/List;")
+ println("-- A4 --")
+ printInnerClassNodes("A4")
+ val fun = lambdaClass("A4$$anonfun$f$1", "A4$lambda$$f$1")
+ printInnerClassNodes(fun)
+ printEnclosingMethod(fun)
}
def testA5() = {
@@ -93,7 +111,7 @@ object Test extends BytecodeTest {
val List(b2) = innerClassNodes("A5$B$2$")
assertLocal(b2, "A5$B$2$", "B$2$")
assertEnclosingMethod(
- enclosingMethod("A5$B$2$"),
+ "A5$B$2$",
"A5", "f", "()Ljava/lang/Object;")
}
@@ -136,56 +154,175 @@ object Test extends BytecodeTest {
assertLocal(k, "A14$K$1", "K$1")
assertEnclosingMethod(
- enclosingMethod("A14$K$1"),
+ "A14$K$1",
"A14", "f", "()Ljava/lang/Object;")
assertAnonymous(anon, "A14$$anon$1")
assertEnclosingMethod(
- enclosingMethod("A14$$anon$1"),
+ "A14$$anon$1",
"A14", "g", "()V")
}
def testA15() = {
val List(b) = innerClassNodes("A15")
- assertLocal(b, "A15$B$3", "B$3", flags = publicStatic)
+ assertLocal(b, "A15$B$3", "B$3")
val List(_, c) = innerClassNodes("A15$B$3")
- // TODO this is a bug in the backend, C should be a member. Instead, its outerClass is null
- // assertMember(c, "A15$B$3", "C")
- assertLocal(c, "A15$B$3$C", "C")
+ assertMember(c, "A15$B$3", "C")
+
+ assertEnclosingMethod(
+ "A15$B$3",
+ "A15$", "f", "()V")
+ assertNoEnclosingMethod("A15$B$3$C")
}
def testA16() = {
- val List(anon1, anon2, u, v) = innerClassNodes("A16")
- // TODO there's a bug in the backend: anon$2 has outerClass A16, but anonymous classes should have outerClass null
- // assertAnonymous(anon1, "A16$$anon$2")
- assertMember(anon1, "A16", null, name = Some("A16$$anon$2"), flags = Flags.ACC_PUBLIC | Flags.ACC_FINAL)
+ val List(anon1, anon2, anon3, u, v) = innerClassNodes("A16")
+ assertAnonymous(anon1, "A16$$anon$2")
assertAnonymous(anon2, "A16$$anon$3")
- // TODO this is a bug in the backend, U should not be a member, its outerClass should be null
- // assertLocal(u, "A16$U$1", "U$1")
- assertMember(u, "A16", "U$1")
+ assertAnonymous(anon3, "A16$$anon$4")
+
+ assertLocal(u, "A16$U$1", "U$1")
assertLocal(v, "A16$V$1", "V$1")
assertEnclosingMethod(
- enclosingMethod("A16$$anon$2"),
- "A16", "<init>", "()V")
+ "A16$$anon$2",
+ "A16", null, null)
+ assertEnclosingMethod(
+ "A16$$anon$3",
+ "A16", null, null)
+ assertEnclosingMethod(
+ "A16$$anon$4",
+ "A16", null, null)
+
assertEnclosingMethod(
- enclosingMethod("A16$$anon$3"),
- "A16", "<init>", "()V")
- // TODO this is a bug, there should be an enclosingMethod attribute in U
- // assertEnclosingMethod(
- // enclosingMethod("A16$U$1"),
- // "A16", "<init>", "()V")
+ "A16$U$1",
+ "A16", null, null)
assertEnclosingMethod(
- enclosingMethod("A16$V$1"),
- "A16", "<init>", "()V")
+ "A16$V$1",
+ "A16", null, null)
}
def testA17() = {
val List(b, c) = innerClassNodes("A17$B$")
assertMember(b, "A17", "B$")
- // TODO this is a bug, should not be static.
- assertMember(c, "A17$B$", "C", name = Some("A17$B$C"), flags = publicStatic) // (should be) not static, has an outer pointer.
+ assertMember(c, "A17$B$", "C", name = Some("A17$B$C")) // not static, has an outer pointer.
+ }
+
+ def testA18() = {
+ val List(anon1, anon2, a, b) = innerClassNodes("A18")
+ assertAnonymous(anon1, "A18$$anon$5")
+ assertAnonymous(anon2, "A18$$anon$6")
+
+ assertLocal(a, "A18$A$1", "A$1")
+ assertLocal(b, "A18$B$4", "B$4")
+
+ assertEnclosingMethod(
+ "A18$$anon$5",
+ "A18", "g$1", "()V")
+ assertEnclosingMethod(
+ "A18$$anon$6",
+ "A18", "g$1", "()V")
+
+ assertEnclosingMethod(
+ "A18$A$1",
+ "A18", "g$1", "()V")
+ assertEnclosingMethod(
+ "A18$B$4",
+ "A18", "g$1", "()V")
+ }
+
+ def testA19() = {
+ println("-- A19 --")
+
+ printInnerClassNodes("A19")
+
+ val fun1 = lambdaClass("A19$$anonfun$1", "A19$lambda$1")
+ val fun2 = lambdaClass("A19$$anonfun$2", "A19$lambda$2")
+ val fun3 = lambdaClass("A19$$anonfun$3", "A19$lambda$3")
+
+ printInnerClassNodes(fun1)
+ printInnerClassNodes(fun2)
+ printInnerClassNodes(fun3)
+
+ printEnclosingMethod(fun1)
+ printEnclosingMethod(fun2)
+ printEnclosingMethod(fun3)
+ }
+
+ def testA20() = {
+ println("-- A20 --")
+
+ printInnerClassNodes("A20")
+
+ val fun1 = lambdaClass("A20$$anonfun$4", "A20$lambda$1")
+ val fun2 = lambdaClass("A20$$anonfun$4$$anonfun$apply$1", "A20$lambda$$$anonfun$5$1")
+ val fun3 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3", "A20$lambda$$$anonfun$5$2")
+ val fun4 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2", "A20$lambda$$$anonfun$7$1")
+
+ println("fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`")
+ printInnerClassNodes(fun1)
+ println("fun2 () => (): itself and the outer closure")
+ printInnerClassNodes(fun2)
+ println("fun3 () => () => (): itself, the outer closure and its child closure")
+ printInnerClassNodes(fun3)
+ println("fun4: () => 1: itself and the two outer closures")
+ printInnerClassNodes(fun4)
+
+ println("enclosing: nested closures have the apply method of the outer closure")
+ printEnclosingMethod(fun1)
+ printEnclosingMethod(fun2)
+ printEnclosingMethod(fun3)
+ printEnclosingMethod(fun4)
+ }
+
+ def testA21() = {
+ val List(i1c, i2c, i3c, j1) = innerClassNodes("A21")
+ assertMember(i1c, "A21", "I1")
+ assertMember(i2c, "A21", "I2", flags = publicStatic)
+ assertMember(i3c, "A21", "I3$", flags = publicStatic)
+ assertLocal(j1, "A21$J1$1", "J1$1")
+
+ val List(i2m, i3m, j3, j4, j5) = innerClassNodes("A21$")
+ assertMember(i2m, "A21", "I2", flags = publicStatic)
+ assertMember(i3m, "A21", "I3$", flags = publicStatic)
+ assertLocal(j3, "A21$J3$1", "J3$1")
+ assertLocal(j4, "A21$J4$1", "J4$1")
+ assertLocal(j5, "A21$J5$1", "J5$1") // non-static!
+
+ val List(i3x, j2x) = innerClassNodes("A21$I3$J2")
+ assertMember(j2x, "A21$I3$", "J2", name = Some("A21$I3$J2"), flags = publicStatic)
+
+ assertNoEnclosingMethod("A21$I3$J2")
+ assertEnclosingMethod("A21$J3$1", "A21$", "g", "()V")
+ assertEnclosingMethod("A21$J4$1", "A21$", null, null)
+ assertEnclosingMethod("A21$J5$1", "A21$", null, null)
+ }
+
+ def testA22() = {
+ val List(cc) = innerClassNodes("A22$C")
+ assertMember(cc, "A22", "C")
+ val List(cm, d) = innerClassNodes("A22$C$")
+ assertMember(cm, "A22", "C$")
+ assertMember(d, "A22$C$", "D", name = Some("A22$C$D"))
+ }
+
+ def testA23() {
+ val List(c, d, e, f, g) = innerClassNodes("A23")
+ assertMember(c, "Java_A_1", "C", flags = publicStatic)
+ assertMember(d, "Java_A_1$C", "D", flags = publicStatic)
+ assertMember(e, "Java_A_1$C", "E")
+ assertMember(f, "Java_A_1", "F")
+ assertMember(g, "Java_A_1$F", "G")
+ }
+
+ def testA24() {
+ val List(defsCls, abs, conc, defsApi, defsApiImpl) = innerClassNodes("A24$DefinitionsClass")
+ assertMember(defsCls, "A24", "DefinitionsClass")
+ assertMember(abs, "A24$DefinitionsClass", "Abs$")
+ assertMember(conc, "A24$DefinitionsClass", "Conc$")
+ assertMember(defsApi, "A24Base", "DefinitionsApi", flags = publicAbstractInterface)
+ assertMember(defsApiImpl, "A24Base", "DefinitionsApi$class", flags = Flags.ACC_PUBLIC | Flags.ACC_ABSTRACT)
}
def show(): Unit = {
@@ -204,5 +341,12 @@ object Test extends BytecodeTest {
testA15()
testA16()
testA17()
+ testA18()
+ testA19()
+ testA20()
+ testA21()
+ testA22()
+ testA23()
+ testA24()
}
}
diff --git a/test/files/jvm/javaReflection.check b/test/files/jvm/javaReflection.check
new file mode 100644
index 0000000000..aeb894f741
--- /dev/null
+++ b/test/files/jvm/javaReflection.check
@@ -0,0 +1,259 @@
+#partest !-Ydelambdafy:method
+A$$anonfun$$lessinit$greater$1 / null (canon) / $anonfun$$lessinit$greater$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth)
+- properties : true (local) / false (member)
+A$$anonfun$$lessinit$greater$1$$anonfun$apply$1 / null (canon) / $anonfun$apply$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A$$anonfun$$lessinit$greater$1 (cls) / null (constr) / public final scala.Function0 A$$anonfun$$lessinit$greater$1.apply() (meth)
+- properties : true (local) / false (member)
+A$$anonfun$2 / null (canon) / $anonfun$2 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$$anonfun$3 / null (canon) / $anonfun$3 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$$anonfun$4 / null (canon) / $anonfun$4 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$$anonfun$f$1 / null (canon) / $anonfun$f$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$$anonfun$f$2 / null (canon) / $anonfun$f$2 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$D$$anonfun$1 / null (canon) / anonfun$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A$D$ (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+AO$$anonfun$5 / null (canon) / anonfun$5 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class AO$ (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+AT$$anonfun$6 / null (canon) / $anonfun$6 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / interface AT (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+#partest -Ydelambdafy:method
+A$D$lambda$1 / A$D$lambda$1 (canon) / A$D$lambda$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+A$lambda$$$anonfun$7$1 / A$lambda$$$anonfun$7$1 (canon) / A$lambda$$$anonfun$7$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+A$lambda$$$lessinit$greater$1 / A$lambda$$$lessinit$greater$1 (canon) / A$lambda$$$lessinit$greater$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+A$lambda$$f$1 / A$lambda$$f$1 (canon) / A$lambda$$f$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+A$lambda$$f$2 / A$lambda$$f$2 (canon) / A$lambda$$f$2 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+A$lambda$1 / A$lambda$1 (canon) / A$lambda$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+A$lambda$2 / A$lambda$2 (canon) / A$lambda$2 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+A$lambda$3 / A$lambda$3 (canon) / A$lambda$3 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+AO$lambda$1 / AO$lambda$1 (canon) / AO$lambda$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+AT$class$lambda$1 / AT$class$lambda$1 (canon) / AT$class$lambda$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+#partest
+A / A (canon) / A (simple)
+- declared cls: List(class A$B, interface A$C, class A$D$)
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+A$$anon$1 / null (canon) / $anon$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$$anon$3 / null (canon) / $anon$3 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$$anon$4 / null (canon) / $anon$4 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$$anon$5 / null (canon) / $anon$5 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$$anon$6 / null (canon) / $anon$6 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$$anon$7 / null (canon) / $anon$7 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth)
+- properties : true (local) / false (member)
+A$B / A.B (canon) / B (simple)
+- declared cls: List()
+- enclosing : class A (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+A$C / A.C (canon) / C (simple)
+- declared cls: List()
+- enclosing : class A (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+A$D$ / A.D$ (canon) / D$ (simple)
+- declared cls: List(class A$D$B, interface A$D$C, class A$D$D$)
+- enclosing : class A (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+A$D$$anon$2 / null (canon) / anon$2 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A$D$ (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$D$B / Malformed class name (canon) / Malformed class name (simple)
+- declared cls: List()
+- enclosing : class A$D$ (declaring cls) / class A$D$ (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+A$D$C / Malformed class name (canon) / Malformed class name (simple)
+- declared cls: List()
+- enclosing : class A$D$ (declaring cls) / class A$D$ (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+A$D$D$ / Malformed class name (canon) / Malformed class name (simple)
+- declared cls: List()
+- enclosing : class A$D$ (declaring cls) / class A$D$ (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+A$D$KB$1 / null (canon) / Malformed class name (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A$D$ (cls) / null (constr) / public void A$D$.f() (meth)
+- properties : Malformed class name (local) / false (member)
+A$E$1 / null (canon) / E$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$F$1 / null (canon) / F$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$G$2$ / null (canon) / G$2$ (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$H$1 / null (canon) / H$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$I$1 / null (canon) / I$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$J$2$ / null (canon) / J$2$ (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth)
+- properties : true (local) / false (member)
+A$K$1 / null (canon) / K$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$L$1 / null (canon) / L$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$M$2$ / null (canon) / M$2$ (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$N$1 / null (canon) / N$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$O$1 / null (canon) / O$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$P$2$ / null (canon) / P$2$ (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+A$Q$1 / null (canon) / Q$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth)
+- properties : true (local) / false (member)
+A$R$1 / null (canon) / R$1 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth)
+- properties : true (local) / false (member)
+A$S$2$ / null (canon) / S$2$ (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth)
+- properties : true (local) / false (member)
+AO / AO (canon) / AO (simple)
+- declared cls: List(class AO$B, interface AO$C, class AO$D$)
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+AO$ / AO$ (canon) / AO$ (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+AO$$anon$8 / null (canon) / anon$8 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / class AO$ (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+AO$B / AO.B (canon) / B (simple)
+- declared cls: List()
+- enclosing : class AO (declaring cls) / class AO (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+AO$C / AO.C (canon) / C (simple)
+- declared cls: List()
+- enclosing : class AO (declaring cls) / class AO (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+AO$D$ / AO.D$ (canon) / D$ (simple)
+- declared cls: List()
+- enclosing : class AO (declaring cls) / class AO (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+AT / AT (canon) / AT (simple)
+- declared cls: List(class AT$B, interface AT$C, class AT$D$)
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+AT$$anon$9 / null (canon) / $anon$9 (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / interface AT (cls) / null (constr) / null (meth)
+- properties : true (local) / false (member)
+AT$B / AT.B (canon) / B (simple)
+- declared cls: List()
+- enclosing : interface AT (declaring cls) / interface AT (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+AT$C / AT.C (canon) / C (simple)
+- declared cls: List()
+- enclosing : interface AT (declaring cls) / interface AT (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+AT$D$ / AT.D$ (canon) / D$ (simple)
+- declared cls: List()
+- enclosing : interface AT (declaring cls) / interface AT (cls) / null (constr) / null (meth)
+- properties : false (local) / true (member)
+AT$class / AT$class (canon) / AT$class (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+T / T (canon) / T (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
+T$class / T$class (canon) / T$class (simple)
+- declared cls: List()
+- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth)
+- properties : false (local) / false (member)
diff --git a/test/files/jvm/javaReflection/Classes_1.scala b/test/files/jvm/javaReflection/Classes_1.scala
new file mode 100644
index 0000000000..11963e2770
--- /dev/null
+++ b/test/files/jvm/javaReflection/Classes_1.scala
@@ -0,0 +1,84 @@
+// See Test.scala for comments
+
+trait T { def f = 1 }
+
+class A {
+ // member class
+ class B
+ // member trait
+ trait C
+ // member object
+ object D {
+ class B
+ trait C
+ object D
+ new T { }
+ (() => -1)
+ def f = { class KB }
+ }
+
+ // anonymous class, not a member
+ new T { }
+
+ // anonymous function, not a member
+ (() => 1)
+
+ def f = {
+ class E
+ trait F
+ object G
+ new T { }
+ (() => 2)
+
+ if (new Object().hashCode == 1) {
+ class H
+ trait I
+ object J
+ new T { }
+ (() => 3)
+ } else {
+ ()
+ }
+ }
+
+ {
+ class K
+ trait L
+ object M
+ new T { }
+ (() => 4)
+ }
+
+ val x = {
+ class N
+ trait O
+ object P
+ new T { }
+ (() => 5)
+ }
+
+ def this(x: Int) {
+ this()
+ class Q
+ trait R
+ object S
+ new T { }
+ (() => () => 5)
+ }
+}
+
+object AO {
+ class B
+ trait C
+ object D
+ new T { }
+ (() => 1)
+}
+
+trait AT {
+ class B
+ trait C
+ object D
+ new T { }
+ (() => 1)
+}
diff --git a/test/files/jvm/javaReflection/Test.scala b/test/files/jvm/javaReflection/Test.scala
new file mode 100644
index 0000000000..5b6ef1b573
--- /dev/null
+++ b/test/files/jvm/javaReflection/Test.scala
@@ -0,0 +1,137 @@
+/**
+Interesting aspects of Java reflection applied to scala classes. TL;DR: you should not use
+getSimpleName / getCanonicalName / isAnonymousClass / isLocalClass / isSynthetic.
+
+ - Some methods in Java reflection assume a certain structure in the class names. Scalac
+ can produce class files that don't respect this structure. Certain methods in reflection
+ therefore give surprising answers or may even throw an exception.
+
+ In particular, the method "getSimpleName" assumes that classes are named after the Java spec
+ http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.1
+
+ Consider the following Scala example:
+ class A { object B { class C } }
+
+ The classfile for C has the name "A$B$C", while the classfile for the module B has the
+ name "A$B$".
+
+ For "cClass.getSimpleName, the implementation first strips the name of the enclosing class,
+ which produces "C". The implementation then expects a "$" character, which is missing, and
+ throws an InternalError.
+
+ Consider another example:
+ trait T
+ class A { val x = new T {} }
+ object B { val x = new T {} }
+
+ The anonymous classes are named "A$$anon$1" and "B$$anon$2". If you call "getSimpleName",
+ you get "$anon$1" (leading $) and "anon$2" (no leading $).
+
+ - There are certain other methods in the Java reflection API that depend on getSimpleName.
+ These should be avoided, they yield unexpected results:
+
+ - isAnonymousClass is always false. Scala-defined classes are never anonymous for Java
+ reflection. Java reflection insepects the class name to decide whether a class is
+ anonymous, based on the name spec referenced above.
+ Also, the implementation of "isAnonymousClass" calls "getSimpleName", which may throw.
+
+ - isLocalClass: should be true true for local classes (nested classes that are not
+ members), but not for anonymous classes. Since "isAnonymousClass" is always false,
+ Java reflection thinks that all Scala-defined anonymous classes are local.
+ The implementation may also throw, since it uses "isAnonymousClass":
+ class A { object B { def f = { class KB; new KB } } }
+ (new A).B.f.getClass.isLocalClass // boom
+
+ - getCanonicalName: uses "getSimpleName" in the implementation. In the first example,
+ cClass.getCanonicalName also fails with an InternalError.
+
+ - Scala-defined classes are never synthetic for Java reflection. The implementation
+ checks for the SYNTHETEIC flag, which does not seem to be added by scalac (maybe this
+ will change some day).
+*/
+
+object Test {
+
+ def tr[T](m: => T): String = try {
+ val r = m
+ if (r == null) "null"
+ else r.toString
+ } catch { case e: InternalError => e.getMessage }
+
+ def assertNotAnonymous(c: Class[_]) = {
+ val an = try {
+ c.isAnonymousClass
+ } catch {
+ // isAnonymousClass is implemented using getSimpleName, which may throw.
+ case e: InternalError => false
+ }
+ assert(!an, c)
+ }
+
+ def ruleMemberOrLocal(c: Class[_]) = {
+ // if it throws, then it's because of the call from isLocalClass to isAnonymousClass.
+ // we know that isAnonymousClass is always false, so it has to be a local class.
+ val loc = try { c.isLocalClass } catch { case e: InternalError => true }
+ if (loc)
+ assert(!c.isMemberClass, c)
+ if (c.isMemberClass)
+ assert(!loc, c)
+ }
+
+ def ruleMemberDeclaring(c: Class[_]) = {
+ if (c.isMemberClass)
+ assert(c.getDeclaringClass.getDeclaredClasses.toList.map(_.getName) contains c.getName)
+ }
+
+ def ruleScalaAnonClassIsLocal(c: Class[_]) = {
+ if (c.getName contains "$anon$")
+ assert(c.isLocalClass, c)
+ }
+
+ def ruleScalaAnonFunInlineIsLocal(c: Class[_]) = {
+ // exclude lambda classes generated by delambdafy:method. nested closures have both "anonfun" and "lambda".
+ if (c.getName.contains("$anonfun$") && !c.getName.contains("$lambda$"))
+ assert(c.isLocalClass, c)
+ }
+
+ def ruleScalaAnonFunMethodIsToplevel(c: Class[_]) = {
+ if (c.getName.contains("$lambda$"))
+ assert(c.getEnclosingClass == null, c)
+ }
+
+ def showClass(name: String) = {
+ val c = Class.forName(name)
+
+ println(s"${c.getName} / ${tr(c.getCanonicalName)} (canon) / ${tr(c.getSimpleName)} (simple)")
+ println( "- declared cls: "+ c.getDeclaredClasses.toList.sortBy(_.getName))
+ println(s"- enclosing : ${c.getDeclaringClass} (declaring cls) / ${c.getEnclosingClass} (cls) / ${c.getEnclosingConstructor} (constr) / ${c.getEnclosingMethod} (meth)")
+ println(s"- properties : ${tr(c.isLocalClass)} (local) / ${c.isMemberClass} (member)")
+
+ assertNotAnonymous(c)
+ assert(!c.isSynthetic, c)
+
+ ruleMemberOrLocal(c)
+ ruleMemberDeclaring(c)
+ ruleScalaAnonClassIsLocal(c)
+ ruleScalaAnonFunInlineIsLocal(c)
+ ruleScalaAnonFunMethodIsToplevel(c)
+ }
+
+ def main(args: Array[String]): Unit = {
+ def isAnonFunClassName(s: String) = s.contains("$anonfun$") || s.contains("$lambda$")
+
+ val classfiles = new java.io.File(sys.props("partest.output")).listFiles().toList.map(_.getName).collect({
+ // exclude files from Test.scala, just take those from Classes_1.scala
+ case s if !s.startsWith("Test") && s.endsWith(".class") => s.substring(0, s.length - 6)
+ }).sortWith((a, b) => {
+ // sort such that first there are all anonymous funcitions, then all other classes.
+ // within those cathegories, sort lexically.
+ // this makes the check file smaller: it differs for anonymous functions between -Ydelambdafy:inline/method.
+ // the other classes are the same.
+ if (isAnonFunClassName(a)) !isAnonFunClassName(b) || a < b
+ else !isAnonFunClassName(b) && a < b
+ })
+
+ classfiles foreach showClass
+ }
+} \ No newline at end of file
diff --git a/test/files/jvm/t6941/test.scala b/test/files/jvm/t6941/test.scala
index 248617f71f..fceb54487f 100644
--- a/test/files/jvm/t6941/test.scala
+++ b/test/files/jvm/t6941/test.scala
@@ -1,4 +1,4 @@
-import scala.tools.partest.BytecodeTest
+import scala.tools.partest.{BytecodeTest, ASMConverters}
import scala.tools.nsc.util.JavaClassPath
import java.io.InputStream
@@ -10,6 +10,6 @@ import scala.collection.JavaConverters._
object Test extends BytecodeTest {
def show: Unit = {
val classNode = loadClassNode("SameBytecode")
- similarBytecode(getMethod(classNode, "a"), getMethod(classNode, "b"), equalsModuloVar)
+ similarBytecode(getMethod(classNode, "a"), getMethod(classNode, "b"), ASMConverters.equivalentBytecode(_, _))
}
}
diff --git a/test/files/jvm/t7253/test.scala b/test/files/jvm/t7253/test.scala
index 7fe08e8813..a3f1e86e65 100644
--- a/test/files/jvm/t7253/test.scala
+++ b/test/files/jvm/t7253/test.scala
@@ -1,4 +1,4 @@
-import scala.tools.partest.BytecodeTest
+import scala.tools.partest.{BytecodeTest, ASMConverters}
import scala.tools.nsc.util.JavaClassPath
import java.io.InputStream
@@ -8,10 +8,10 @@ import asm.tree.{ClassNode, InsnList}
import scala.collection.JavaConverters._
object Test extends BytecodeTest {
- import instructions._
+ import ASMConverters._
def show: Unit = {
- val instrBaseSeqs = Seq("ScalaClient_1", "JavaClient_1") map (name => instructions.fromMethod(getMethod(loadClassNode(name), "foo")))
+ val instrBaseSeqs = Seq("ScalaClient_1", "JavaClient_1") map (name => instructionsFromMethod(getMethod(loadClassNode(name), "foo")))
val instrSeqs = instrBaseSeqs map (_ filter isInvoke)
cmpInstructions(instrSeqs(0), instrSeqs(1))
}
diff --git a/test/files/jvm/unreachable.flags b/test/files/jvm/unreachable.flags
deleted file mode 100644
index 49f2d2c4c8..0000000000
--- a/test/files/jvm/unreachable.flags
+++ /dev/null
@@ -1 +0,0 @@
--Ybackend:GenASM
diff --git a/test/files/jvm/varargs/VaClass.scala b/test/files/jvm/varargs/VaClass.scala
index e94e8a625a..d83e63ace1 100644
--- a/test/files/jvm/varargs/VaClass.scala
+++ b/test/files/jvm/varargs/VaClass.scala
@@ -9,5 +9,7 @@ class VaClass {
@varargs def vs(a: Int, b: String*) = println(a + b.length)
@varargs def vi(a: Int, b: Int*) = println(a + b.sum)
@varargs def vt[T](a: Int, b: T*) = println(a + b.length)
- @varargs def vt1[T](a: Int, b: T*): T = b.head
+
+ // TODO remove type bound after fixing SI-8786, see also https://github.com/scala/scala/pull/3961
+ @varargs def vt1[T <: String](a: Int, b: T*): T = b.head
}
diff --git a/test/files/neg/compile-time-only-a.check b/test/files/neg/compile-time-only-a.check
index 9bc96f6b9b..b1ed1d24c2 100644
--- a/test/files/neg/compile-time-only-a.check
+++ b/test/files/neg/compile-time-only-a.check
@@ -4,9 +4,6 @@ compile-time-only-a.scala:10: error: C3
compile-time-only-a.scala:12: error: C4
@compileTimeOnly("C4") case class C4(x: Int)
^
-compile-time-only-a.scala:17: error: C5
- implicit class C5(val x: Int) {
- ^
compile-time-only-a.scala:32: error: C1
new C1()
^
@@ -76,4 +73,4 @@ compile-time-only-a.scala:75: error: placebo
compile-time-only-a.scala:75: error: placebo
@placebo def x = (2: @placebo)
^
-26 errors found
+25 errors found
diff --git a/test/files/neg/forgot-interpolator.scala b/test/files/neg/forgot-interpolator.scala
index a53054d890..ca1ac30821 100644
--- a/test/files/neg/forgot-interpolator.scala
+++ b/test/files/neg/forgot-interpolator.scala
@@ -54,8 +54,8 @@ package test {
}
}
import annotation._
- @implicitNotFound("No Z in ${A}") // no warn
- class Z[A]
+ @implicitNotFound("No Z in ${T}") // no warn
+ class Z[T]
}
diff --git a/test/files/neg/macro-basic-mamdmi.check b/test/files/neg/macro-basic-mamdmi.check
index 61df5131cc..54743d4936 100644
--- a/test/files/neg/macro-basic-mamdmi.check
+++ b/test/files/neg/macro-basic-mamdmi.check
@@ -1,5 +1,13 @@
+Impls_Macros_Test_1.scala:33: error: macro implementation not found: foo
+(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
+ println(foo(2) + Macros.bar(2) * new Macros().quux(4))
+ ^
+Impls_Macros_Test_1.scala:33: error: macro implementation not found: bar
+(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
+ println(foo(2) + Macros.bar(2) * new Macros().quux(4))
+ ^
Impls_Macros_Test_1.scala:33: error: macro implementation not found: quux
(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
println(foo(2) + Macros.bar(2) * new Macros().quux(4))
^
-one error found
+three errors found
diff --git a/test/files/neg/t3909.check b/test/files/neg/t3909.check
index 7da0195607..052b49f855 100644
--- a/test/files/neg/t3909.check
+++ b/test/files/neg/t3909.check
@@ -1,4 +1,5 @@
t3909.scala:1: error: in object DO, multiple overloaded alternatives of m1 define default arguments
+Error occurred in an application involving default arguments.
object DO {
^
one error found
diff --git a/test/files/neg/t4851.check b/test/files/neg/t4851.check
index 132dd91b50..d5711a889b 100644
--- a/test/files/neg/t4851.check
+++ b/test/files/neg/t4851.check
@@ -29,13 +29,13 @@ S.scala:7: warning: Adapting argument list by creating a 3-tuple: this may not b
val y2 = new Some(1, 2, 3)
^
S.scala:9: warning: Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want.
- signature: J2[T](x: T): J2[T]
+ signature: J2(x: T): J2[T]
given arguments: <none>
after adaptation: new J2((): Unit)
val z1 = new J2
^
S.scala:10: warning: Adaptation of argument list by inserting () has been deprecated: this is unlikely to be what you want.
- signature: J2[T](x: T): J2[T]
+ signature: J2(x: T): J2[T]
given arguments: <none>
after adaptation: new J2((): Unit)
val z2 = new J2()
diff --git a/test/files/neg/t4851/J2.java b/test/files/neg/t4851/J2.java
index 82954d9489..a90f48e269 100644
--- a/test/files/neg/t4851/J2.java
+++ b/test/files/neg/t4851/J2.java
@@ -1,11 +1,11 @@
public class J2<T> {
T x;
- public <T> J(T x) {
+ public J2(T x) {
this.x = x;
}
public String toString() {
return "J2:" + x.getClass();
}
-} \ No newline at end of file
+}
diff --git a/test/files/neg/t5691.check b/test/files/neg/t5691.check
new file mode 100644
index 0000000000..a51ca98a10
--- /dev/null
+++ b/test/files/neg/t5691.check
@@ -0,0 +1,24 @@
+t5691.scala:7: warning: type parameter D defined in method foobar shadows trait D defined in class B. You may want to rename your type parameter, or possibly remove it.
+ def foobar[D](in: D) = in.toString
+ ^
+t5691.scala:10: warning: type parameter D defined in type MySeq shadows trait D defined in class B. You may want to rename your type parameter, or possibly remove it.
+ type MySeq[D] = Seq[D]
+ ^
+t5691.scala:15: warning: type parameter T defined in method bar shadows type T defined in class Foo. You may want to rename your type parameter, or possibly remove it.
+ def bar[T](w: T) = w.toString
+ ^
+t5691.scala:13: warning: type parameter T defined in class Foo shadows type T defined in class B. You may want to rename your type parameter, or possibly remove it.
+ class Foo[T](t: T) {
+ ^
+t5691.scala:19: warning: type parameter List defined in type M shadows type List defined in package object scala. You may want to rename your type parameter, or possibly remove it.
+ class C[M[List[_]]]
+ ^
+t5691.scala:20: warning: type parameter List defined in type M shadows type List defined in package object scala. You may want to rename your type parameter, or possibly remove it.
+ type E[M[List[_]]] = Int
+ ^
+t5691.scala:21: warning: type parameter List defined in type M shadows type List defined in package object scala. You may want to rename your type parameter, or possibly remove it.
+ def foo[N[M[List[_]]]] = ???
+ ^
+error: No warnings can be incurred under -Xfatal-warnings.
+7 warnings found
+one error found
diff --git a/test/files/neg/t5691.flags b/test/files/neg/t5691.flags
new file mode 100644
index 0000000000..0e09b8575b
--- /dev/null
+++ b/test/files/neg/t5691.flags
@@ -0,0 +1 @@
+-Xlint:type-parameter-shadow -language:higherKinds -Xfatal-warnings
diff --git a/test/files/neg/t5691.scala b/test/files/neg/t5691.scala
new file mode 100644
index 0000000000..e6a9bdc16a
--- /dev/null
+++ b/test/files/neg/t5691.scala
@@ -0,0 +1,27 @@
+class B {
+
+ type T = Int
+ trait D
+
+ // method parameter shadows some other type
+ def foobar[D](in: D) = in.toString
+
+ // type member's parameter shadows some other type
+ type MySeq[D] = Seq[D]
+
+ // class parameter shadows some other type
+ class Foo[T](t: T) {
+ // a type parameter shadows another type parameter
+ def bar[T](w: T) = w.toString
+ }
+
+ // even deeply nested...
+ class C[M[List[_]]]
+ type E[M[List[_]]] = Int
+ def foo[N[M[List[_]]]] = ???
+
+ // ...but not between type parameters in the same list
+ class F[A, M[L[A]]] // no warning!
+ type G[A, M[L[A]]] = Int // no warning!
+ def bar[A, N[M[L[A]]]] = ??? // no warning!
+}
diff --git a/test/files/neg/t6675b.scala b/test/files/neg/t6675b.scala
index c86c9c3955..da27e1b91f 100644
--- a/test/files/neg/t6675b.scala
+++ b/test/files/neg/t6675b.scala
@@ -13,7 +13,7 @@ object NativelyTwo {
}
-class A {
+class E {
def f1 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case LeftOrRight(a) => a } // warn
def f2 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case LeftOrRight((a, b)) => a } // no warn
def f3 = (Left((0, 0)): Either[(Int, Int), (Int, Int)]) match { case LeftOrRight((a, b, c)) => a } // fail
diff --git a/test/files/neg/t8764.check b/test/files/neg/t8764.check
new file mode 100644
index 0000000000..6d89ebe106
--- /dev/null
+++ b/test/files/neg/t8764.check
@@ -0,0 +1,6 @@
+t8764.scala:8: error: type mismatch;
+ found : AnyVal
+ required: Double
+ val d: Double = a.productElement(0)
+ ^
+one error found
diff --git a/test/files/neg/t8764.flags b/test/files/neg/t8764.flags
new file mode 100644
index 0000000000..48fd867160
--- /dev/null
+++ b/test/files/neg/t8764.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/neg/t8764.scala b/test/files/neg/t8764.scala
new file mode 100644
index 0000000000..dc5bfb0160
--- /dev/null
+++ b/test/files/neg/t8764.scala
@@ -0,0 +1,9 @@
+object Main {
+
+ case class IntAndDouble(i: Int, d: Double)
+
+ // a.productElement used to be Int => Double
+ // now: Int => AnyVal
+ val a = IntAndDouble(1, 5.0)
+ val d: Double = a.productElement(0)
+}
diff --git a/test/files/neg/warn-unused-privates.check b/test/files/neg/warn-unused-privates.check
index d012869c93..4876ed8fc2 100644
--- a/test/files/neg/warn-unused-privates.check
+++ b/test/files/neg/warn-unused-privates.check
@@ -10,57 +10,60 @@ warn-unused-privates.scala:6: warning: private val in class Bippy is never used
warn-unused-privates.scala:13: warning: private val in object Bippy is never used
private val HEY_INSTANCE: Int = 1000 // warn
^
-warn-unused-privates.scala:35: warning: private val in class Boppy is never used
+warn-unused-privates.scala:14: warning: private val in object Bippy is never used
+ private lazy val BOOL: Boolean = true // warn
+ ^
+warn-unused-privates.scala:36: warning: private val in class Boppy is never used
private val hummer = "def" // warn
^
-warn-unused-privates.scala:42: warning: private var in trait Accessors is never used
+warn-unused-privates.scala:43: warning: private var in trait Accessors is never used
private var v1: Int = 0 // warn
^
-warn-unused-privates.scala:42: warning: private setter in trait Accessors is never used
+warn-unused-privates.scala:43: warning: private setter in trait Accessors is never used
private var v1: Int = 0 // warn
^
-warn-unused-privates.scala:43: warning: private setter in trait Accessors is never used
+warn-unused-privates.scala:44: warning: private setter in trait Accessors is never used
private var v2: Int = 0 // warn, never set
^
-warn-unused-privates.scala:44: warning: private var in trait Accessors is never used
+warn-unused-privates.scala:45: warning: private var in trait Accessors is never used
private var v3: Int = 0 // warn, never got
^
-warn-unused-privates.scala:56: warning: private default argument in trait DefaultArgs is never used
+warn-unused-privates.scala:57: warning: private default argument in trait DefaultArgs is never used
private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3
^
-warn-unused-privates.scala:56: warning: private default argument in trait DefaultArgs is never used
+warn-unused-privates.scala:57: warning: private default argument in trait DefaultArgs is never used
private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3
^
-warn-unused-privates.scala:67: warning: local var in method f0 is never used
+warn-unused-privates.scala:68: warning: local var in method f0 is never used
var x = 1 // warn
^
-warn-unused-privates.scala:74: warning: local val in method f1 is never used
+warn-unused-privates.scala:75: warning: local val in method f1 is never used
val b = new Outer // warn
^
-warn-unused-privates.scala:84: warning: private object in object Types is never used
+warn-unused-privates.scala:85: warning: private object in object Types is never used
private object Dongo { def f = this } // warn
^
-warn-unused-privates.scala:94: warning: local object in method l1 is never used
+warn-unused-privates.scala:95: warning: local object in method l1 is never used
object HiObject { def f = this } // warn
^
-warn-unused-privates.scala:78: warning: local var x in method f2 is never set - it could be a val
+warn-unused-privates.scala:79: warning: local var x in method f2 is never set - it could be a val
var x = 100 // warn about it being a var
^
-warn-unused-privates.scala:85: warning: private class Bar1 in object Types is never used
+warn-unused-privates.scala:86: warning: private class Bar1 in object Types is never used
private class Bar1 // warn
^
-warn-unused-privates.scala:87: warning: private type Alias1 in object Types is never used
+warn-unused-privates.scala:88: warning: private type Alias1 in object Types is never used
private type Alias1 = String // warn
^
-warn-unused-privates.scala:95: warning: local class Hi is never used
+warn-unused-privates.scala:96: warning: local class Hi is never used
class Hi { // warn
^
-warn-unused-privates.scala:99: warning: local class DingDongDoobie is never used
+warn-unused-privates.scala:100: warning: local class DingDongDoobie is never used
class DingDongDoobie // warn
^
-warn-unused-privates.scala:102: warning: local type OtherThing is never used
+warn-unused-privates.scala:103: warning: local type OtherThing is never used
type OtherThing = String // warn
^
error: No warnings can be incurred under -Xfatal-warnings.
-21 warnings found
+22 warnings found
one error found
diff --git a/test/files/neg/warn-unused-privates.scala b/test/files/neg/warn-unused-privates.scala
index cb6e946a34..2faa07e759 100644
--- a/test/files/neg/warn-unused-privates.scala
+++ b/test/files/neg/warn-unused-privates.scala
@@ -11,6 +11,7 @@ object Bippy {
def hi(x: Bippy) = x.HI_COMPANION
private val HI_INSTANCE: Int = 500 // no warn, accessed from instance
private val HEY_INSTANCE: Int = 1000 // warn
+ private lazy val BOOL: Boolean = true // warn
}
class A(val msg: String)
diff --git a/test/files/pos/t8410.flags b/test/files/pos/t8410.flags
new file mode 100644
index 0000000000..dcd5943c2f
--- /dev/null
+++ b/test/files/pos/t8410.flags
@@ -0,0 +1 @@
+-optimise -Xfatal-warnings -deprecation:false -Yinline-warnings:false
diff --git a/test/files/pos/t8410.scala b/test/files/pos/t8410.scala
new file mode 100644
index 0000000000..4d862311fa
--- /dev/null
+++ b/test/files/pos/t8410.scala
@@ -0,0 +1,15 @@
+
+object Test extends App {
+ @deprecated("","") def f = 42
+ @deprecated("","") def z = f
+ def g = { @deprecated("","") def _f = f ; _f } // warns in 2.11.0-M8
+ def x = { @deprecated("","") class X { def x = f } ; new X().x } // warns in 2.11.0-M8
+ Console println g
+ Console println f // warns
+
+ @deprecated("","") trait T
+ object T extends T { def t = f }
+ Console println T.t
+
+ def k = List(0).dropWhile(_ < 1) // inlining warns doubly
+}
diff --git a/test/files/pos/t8498.scala b/test/files/pos/t8498.scala
new file mode 100644
index 0000000000..6808c89051
--- /dev/null
+++ b/test/files/pos/t8498.scala
@@ -0,0 +1,6 @@
+import scala.annotation.compileTimeOnly
+
+class C(val s: String) extends AnyVal {
+ @compileTimeOnly("error")
+ def error = ???
+}
diff --git a/test/files/pos/t8793.scala b/test/files/pos/t8793.scala
new file mode 100644
index 0000000000..1276155675
--- /dev/null
+++ b/test/files/pos/t8793.scala
@@ -0,0 +1,15 @@
+package regr
+
+trait F[A]
+
+class G(val a: F[_], val b: F[_])
+
+object G {
+ def unapply(g: G) = Option((g.a, g.b))
+}
+
+object H {
+ def unapply(g: G) = g match {
+ case G(a, _) => Option(a)
+ }
+}
diff --git a/test/files/pos/t8828.flags b/test/files/pos/t8828.flags
new file mode 100644
index 0000000000..e68991f643
--- /dev/null
+++ b/test/files/pos/t8828.flags
@@ -0,0 +1 @@
+-Xlint:inaccessible -Xfatal-warnings
diff --git a/test/files/pos/t8828.scala b/test/files/pos/t8828.scala
new file mode 100644
index 0000000000..92092b4dd4
--- /dev/null
+++ b/test/files/pos/t8828.scala
@@ -0,0 +1,20 @@
+
+package outer
+
+package inner {
+
+ private[inner] class A
+
+ // the class is final: no warning
+ private[outer] final class B {
+ def doWork(a: A): A = a
+ }
+
+ // the trait is sealed and doWork is not
+ // and cannot be overriden: no warning
+ private[outer] sealed trait C {
+ def doWork(a: A): A = a
+ }
+
+ private[outer] final class D extends C
+}
diff --git a/test/files/run/delambdafyLambdaClassNames.check b/test/files/run/delambdafyLambdaClassNames.check
new file mode 100644
index 0000000000..d425d15dd0
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames.check
@@ -0,0 +1 @@
+A$$nestedInAnon$1$lambda$$run$1
diff --git a/test/files/run/delambdafyLambdaClassNames.flags b/test/files/run/delambdafyLambdaClassNames.flags
new file mode 100644
index 0000000000..b10233d322
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames.flags
@@ -0,0 +1 @@
+-Ybackend:GenBCode -Ydelambdafy:method \ No newline at end of file
diff --git a/test/files/run/delambdafyLambdaClassNames/A_1.scala b/test/files/run/delambdafyLambdaClassNames/A_1.scala
new file mode 100644
index 0000000000..10489414b7
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames/A_1.scala
@@ -0,0 +1,5 @@
+class A {
+ def f = new Runnable {
+ def run(): Unit = List(1,2).foreach(println)
+ }
+}
diff --git a/test/files/run/delambdafyLambdaClassNames/Test.scala b/test/files/run/delambdafyLambdaClassNames/Test.scala
new file mode 100644
index 0000000000..49a397d1d2
--- /dev/null
+++ b/test/files/run/delambdafyLambdaClassNames/Test.scala
@@ -0,0 +1,4 @@
+object Test extends App {
+ val c = Class.forName("A$$nestedInAnon$1$lambda$$run$1")
+ println(c.getName)
+}
diff --git a/test/files/run/nothingTypeDce.flags b/test/files/run/nothingTypeDce.flags
new file mode 100644
index 0000000000..d85321ca0e
--- /dev/null
+++ b/test/files/run/nothingTypeDce.flags
@@ -0,0 +1 @@
+-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code
diff --git a/test/files/run/nothingTypeDce.scala b/test/files/run/nothingTypeDce.scala
new file mode 100644
index 0000000000..5f3692fd33
--- /dev/null
+++ b/test/files/run/nothingTypeDce.scala
@@ -0,0 +1,63 @@
+// See comment in BCodeBodyBuilder
+
+// -target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code
+// target enables stack map frames generation
+
+class C {
+ // can't just emit a call to ???, that returns value of type Nothing$ (not Int).
+ def f1: Int = ???
+
+ def f2: Int = throw new Error("")
+
+ def f3(x: Boolean) = {
+ var y = 0
+ // cannot assign an object of type Nothing$ to Int
+ if (x) y = ???
+ else y = 1
+ y
+ }
+
+ def f4(x: Boolean) = {
+ var y = 0
+ // tests that whatever is emitted after the throw is valid (what? depends on opts, presence of stack map frames)
+ if (x) y = throw new Error("")
+ else y = 1
+ y
+ }
+
+ def f5(x: Boolean) = {
+ // stack heights need to be the same. ??? looks to the jvm like returning a value of
+ // type Nothing$, need to drop or throw it.
+ println(
+ if (x) { ???; 10 }
+ else 20
+ )
+ }
+
+ def f6(x: Boolean) = {
+ println(
+ if (x) { throw new Error(""); 10 }
+ else 20
+ )
+ }
+
+ def f7(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+
+ def f8(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+}
+
+object Test extends App {
+ // creating an instance is enough to trigger bytecode verification for all methods,
+ // no need to invoke the methods.
+ new C()
+}
diff --git a/test/files/run/nothingTypeNoFramesNoDce.check b/test/files/run/nothingTypeNoFramesNoDce.check
new file mode 100644
index 0000000000..b1d08b45ff
--- /dev/null
+++ b/test/files/run/nothingTypeNoFramesNoDce.check
@@ -0,0 +1 @@
+warning: -target:jvm-1.5 is deprecated: use target for Java 1.6 or above.
diff --git a/test/files/run/nothingTypeNoFramesNoDce.flags b/test/files/run/nothingTypeNoFramesNoDce.flags
new file mode 100644
index 0000000000..a035c86179
--- /dev/null
+++ b/test/files/run/nothingTypeNoFramesNoDce.flags
@@ -0,0 +1 @@
+-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none -deprecation
diff --git a/test/files/run/nothingTypeNoFramesNoDce.scala b/test/files/run/nothingTypeNoFramesNoDce.scala
new file mode 100644
index 0000000000..3d1298303a
--- /dev/null
+++ b/test/files/run/nothingTypeNoFramesNoDce.scala
@@ -0,0 +1,61 @@
+// See comment in BCodeBodyBuilder
+
+// -target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none
+// target disables stack map frame generation. in this mode, the ClssWriter just emits dead code as is.
+
+class C {
+ // can't just emit a call to ???, that returns value of type Nothing$ (not Int).
+ def f1: Int = ???
+
+ def f2: Int = throw new Error("")
+
+ def f3(x: Boolean) = {
+ var y = 0
+ // cannot assign an object of type Nothing$ to Int
+ if (x) y = ???
+ else y = 1
+ y
+ }
+
+ def f4(x: Boolean) = {
+ var y = 0
+ // tests that whatever is emitted after the throw is valid (what? depends on opts, presence of stack map frames)
+ if (x) y = throw new Error("")
+ else y = 1
+ y
+ }
+
+ def f5(x: Boolean) = {
+ // stack heights need to be the smae. ??? looks to the jvm like returning a value of
+ // type Nothing$, need to drop or throw it.
+ println(
+ if (x) { ???; 10 }
+ else 20
+ )
+ }
+
+ def f6(x: Boolean) = {
+ println(
+ if (x) { throw new Error(""); 10 }
+ else 20
+ )
+ }
+
+ def f7(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+
+ def f8(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+}
+
+object Test extends App {
+ new C()
+}
diff --git a/test/files/run/nothingTypeNoOpt.flags b/test/files/run/nothingTypeNoOpt.flags
new file mode 100644
index 0000000000..b3b518051b
--- /dev/null
+++ b/test/files/run/nothingTypeNoOpt.flags
@@ -0,0 +1 @@
+-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none
diff --git a/test/files/run/nothingTypeNoOpt.scala b/test/files/run/nothingTypeNoOpt.scala
new file mode 100644
index 0000000000..5c5a20fa3b
--- /dev/null
+++ b/test/files/run/nothingTypeNoOpt.scala
@@ -0,0 +1,61 @@
+// See comment in BCodeBodyBuilder
+
+// -target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none
+// target enables stack map frame generation
+
+class C {
+ // can't just emit a call to ???, that returns value of type Nothing$ (not Int).
+ def f1: Int = ???
+
+ def f2: Int = throw new Error("")
+
+ def f3(x: Boolean) = {
+ var y = 0
+ // cannot assign an object of type Nothing$ to Int
+ if (x) y = ???
+ else y = 1
+ y
+ }
+
+ def f4(x: Boolean) = {
+ var y = 0
+ // tests that whatever is emitted after the throw is valid (what? depends on opts, presence of stack map frames)
+ if (x) y = throw new Error("")
+ else y = 1
+ y
+ }
+
+ def f5(x: Boolean) = {
+ // stack heights need to be the smae. ??? looks to the jvm like returning a value of
+ // type Nothing$, need to drop or throw it.
+ println(
+ if (x) { ???; 10 }
+ else 20
+ )
+ }
+
+ def f6(x: Boolean) = {
+ println(
+ if (x) { throw new Error(""); 10 }
+ else 20
+ )
+ }
+
+ def f7(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+
+ def f8(x: Boolean) = {
+ println(
+ if (x) throw new Error("")
+ else 20
+ )
+ }
+}
+
+object Test extends App {
+ new C()
+}
diff --git a/test/files/run/t4332.scala b/test/files/run/t4332.scala
index 5a67922911..1c7e7d73de 100644
--- a/test/files/run/t4332.scala
+++ b/test/files/run/t4332.scala
@@ -12,7 +12,7 @@ object Test extends DirectTest {
}
def isExempt(sym: Symbol) = {
- val exempt = Set("view", "repr", "sliceWithKnownDelta", "sliceWithKnownBound", "transform")
+ val exempt = Set("view", "repr", "sliceWithKnownDelta", "sliceWithKnownBound", "transform", "filterImpl")
(exempt contains sym.name.decoded)
}
diff --git a/test/files/run/t5256c.check b/test/files/run/t5256c.check
index 7fcd0eb722..3eb7b13a97 100644
--- a/test/files/run/t5256c.check
+++ b/test/files/run/t5256c.check
@@ -2,5 +2,5 @@ class A$1
Test.A$1
java.lang.Object {
def foo(): Nothing
- def <init>(): A$1
+ def <init>(): Test.A$1
}
diff --git a/test/files/run/t8764.check b/test/files/run/t8764.check
new file mode 100644
index 0000000000..6260069602
--- /dev/null
+++ b/test/files/run/t8764.check
@@ -0,0 +1,5 @@
+IntOnly: should return an unboxed int
+Int: int
+IntAndDouble: should just box and return Anyval
+Double: class java.lang.Double
+Int: class java.lang.Integer
diff --git a/test/files/run/t8764.flags b/test/files/run/t8764.flags
new file mode 100644
index 0000000000..48fd867160
--- /dev/null
+++ b/test/files/run/t8764.flags
@@ -0,0 +1 @@
+-Xexperimental
diff --git a/test/files/run/t8764.scala b/test/files/run/t8764.scala
new file mode 100644
index 0000000000..decc658f6e
--- /dev/null
+++ b/test/files/run/t8764.scala
@@ -0,0 +1,16 @@
+object Test extends App {
+case class IntOnly(i: Int, j: Int)
+
+println("IntOnly: should return an unboxed int")
+val a = IntOnly(1, 2)
+val i: Int = a.productElement(0)
+println(s"Int: ${a.productElement(0).getClass}")
+
+case class IntAndDouble(i: Int, d: Double)
+
+println("IntAndDouble: should just box and return Anyval")
+val b = IntAndDouble(1, 2.0)
+val j: AnyVal = b.productElement(0)
+println(s"Double: ${b.productElement(1).getClass}")
+println(s"Int: ${b.productElement(0).getClass}")
+}
diff --git a/test/files/run/t8803.check b/test/files/run/t8803.check
new file mode 100644
index 0000000000..bd26a0fb14
--- /dev/null
+++ b/test/files/run/t8803.check
@@ -0,0 +1,16 @@
+a
+b
+b
+c
+a
+b
+b
+c
+a
+b
+b
+c
+a
+b
+b
+c
diff --git a/test/files/run/t8803.scala b/test/files/run/t8803.scala
new file mode 100644
index 0000000000..2e56180502
--- /dev/null
+++ b/test/files/run/t8803.scala
@@ -0,0 +1,57 @@
+class A {
+ def m = "a"
+ protected def n = "a"
+}
+
+trait B {
+ def m = "b"
+ protected def n = "b"
+}
+
+class C extends A with B {
+ override def m = "c"
+ override protected def n = "c"
+
+ val f1 = () => super[A].m
+ val f2 = () => super[B].m
+ val f3 = () => super.m
+ val f4 = () => this.m
+
+ val g1 = new runtime.AbstractFunction0[String] { def apply() = C.super[A].m }
+ val g2 = new runtime.AbstractFunction0[String] { def apply() = C.super[B].m }
+ val g3 = new runtime.AbstractFunction0[String] { def apply() = C.super.m }
+ val g4 = new runtime.AbstractFunction0[String] { def apply() = C.this.m }
+
+ val h1 = () => super[A].n
+ val h2 = () => super[B].n
+ val h3 = () => super.n
+ val h4 = () => this.n
+
+ val i1 = new runtime.AbstractFunction0[String] { def apply() = C.super[A].n }
+ val i2 = new runtime.AbstractFunction0[String] { def apply() = C.super[B].n }
+ val i3 = new runtime.AbstractFunction0[String] { def apply() = C.super.n }
+ val i4 = new runtime.AbstractFunction0[String] { def apply() = C.this.n }
+}
+
+object Test extends App {
+ val c = new C
+ println(c.f1())
+ println(c.f2())
+ println(c.f3())
+ println(c.f4())
+
+ println(c.g1())
+ println(c.g2())
+ println(c.g3())
+ println(c.g4())
+
+ println(c.h1())
+ println(c.h2())
+ println(c.h3())
+ println(c.h4())
+
+ println(c.i1())
+ println(c.i2())
+ println(c.i3())
+ println(c.i4())
+}
diff --git a/test/files/run/t8823.scala b/test/files/run/t8823.scala
new file mode 100644
index 0000000000..0ac653566a
--- /dev/null
+++ b/test/files/run/t8823.scala
@@ -0,0 +1,10 @@
+class Tuple2Int(val encoding: Long) extends AnyVal with Product2[Int, Int] {
+ def canEqual(that: Any) = false
+ def _1: Int = 1
+ def _2: Int = 2
+}
+
+object Test extends App {
+ assert(new Tuple2Int(0)._1 == 1)
+ assert(new Tuple2Int(0)._2 == 2)
+}
diff --git a/test/junit/scala/collection/StreamTest.scala b/test/junit/scala/collection/StreamTest.scala
new file mode 100644
index 0000000000..6dc1c79a48
--- /dev/null
+++ b/test/junit/scala/collection/StreamTest.scala
@@ -0,0 +1,18 @@
+package scala.collection.immutable
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import org.junit.Assert._
+
+@RunWith(classOf[JUnit4])
+class StreamTest {
+
+ @Test
+ def t6727_and_t6440(): Unit = {
+ assertTrue(Stream.continually(()).filter(_ => true).take(2) == Seq((), ()))
+ assertTrue(Stream.continually(()).filterNot(_ => false).take(2) == Seq((), ()))
+ assertTrue(Stream(1,2,3,4,5).filter(_ < 4) == Seq(1,2,3))
+ assertTrue(Stream(1,2,3,4,5).filterNot(_ > 4) == Seq(1,2,3,4))
+ }
+}
diff --git a/test/junit/scala/tools/nsc/ScriptRunnerTest.scala b/test/junit/scala/tools/nsc/ScriptRunnerTest.scala
new file mode 100644
index 0000000000..9bae7a0487
--- /dev/null
+++ b/test/junit/scala/tools/nsc/ScriptRunnerTest.scala
@@ -0,0 +1,23 @@
+package scala.tools.nsc
+
+import org.junit.Assert._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(classOf[JUnit4])
+class ScriptRunnerTest {
+ @Test
+ def testEmptyScriptSucceeds: Unit = {
+ val s = new GenericRunnerSettings(s => ())
+ s.nc.value = true
+ s.usejavacp.value = true
+
+ // scala -nc -e ''
+ assertTrue(ScriptRunner.runCommand(s, "", Nil))
+
+ // scala -nc -save -e ''
+ s.save.value = true
+ assertTrue(ScriptRunner.runCommand(s, "", Nil))
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
index b592d06501..221aad6536 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala
@@ -1,7 +1,6 @@
package scala.tools.nsc
package backend.jvm
-import scala.tools.testing.AssertUtil._
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
@@ -10,29 +9,33 @@ import org.junit.Assert._
@RunWith(classOf[JUnit4])
class BTypesTest {
- val g: Global = new Global(new Settings())
+ val settings = new Settings()
+ settings.processArgumentString("-usejavacp")
+ val g: Global = new Global(settings)
+ val run = new g.Run() // initializes some compiler internals
+ import g.{definitions => d, Symbol}
- val btypes = new BTypes[g.type](g) {
- def chrs = g.chrs
- override type BTypeName = g.TypeName
- override def createNewName(s: String) = g.newTypeName(s)
- }
+ def duringBackend[T](f: => T) = g.exitingDelambdafy(f)
+ val btypes = new BTypesFromSymbols[g.type](g)
import btypes._
+ duringBackend(btypes.intializeCoreBTypes())
+
+ def classBTypeFromSymbol(sym: Symbol) = duringBackend(btypes.classBTypeFromSymbol(sym))
- val jls = "java/lang/String"
- val jlo = "java/lang/Object"
+ val jlo = d.ObjectClass
+ val jls = d.StringClass
- val o = ClassBType(jlo)
- val s = ClassBType(jls)
+ val o = classBTypeFromSymbol(jlo)
+ val s = classBTypeFromSymbol(jls)
val oArr = ArrayBType(o)
val method = MethodBType(List(oArr, INT, DOUBLE, s), UNIT)
@Test
def classBTypesEquality() {
- val s1 = ClassBType(jls)
- val s2 = ClassBType(jls)
- val o = ClassBType(jlo)
+ val s1 = classBTypeFromSymbol(jls)
+ val s2 = classBTypeFromSymbol(jls)
+ val o = classBTypeFromSymbol(jlo)
assertEquals(s1, s2)
assertEquals(s1.hashCode, s2.hashCode)
assert(s1 != o)
@@ -40,11 +43,6 @@ class BTypesTest {
}
@Test
- def classBTypeRequiresInternalName() {
- assertThrows[AssertionError](ClassBType(s"L$jls;"), _ contains "Descriptor instead of internal name")
- }
-
- @Test
def typedOpcodes() {
assert(UNIT.typedOpcode(Opcodes.IALOAD) == Opcodes.IALOAD)
assert(INT.typedOpcode(Opcodes.IALOAD) == Opcodes.IALOAD)
@@ -55,7 +53,7 @@ class BTypesTest {
assert(FLOAT.typedOpcode(Opcodes.IALOAD) == Opcodes.FALOAD)
assert(LONG.typedOpcode(Opcodes.IALOAD) == Opcodes.LALOAD)
assert(DOUBLE.typedOpcode(Opcodes.IALOAD) == Opcodes.DALOAD)
- assert(ClassBType(jls).typedOpcode(Opcodes.IALOAD) == Opcodes.AALOAD)
+ assert(classBTypeFromSymbol(jls).typedOpcode(Opcodes.IALOAD) == Opcodes.AALOAD)
assert(UNIT.typedOpcode(Opcodes.IRETURN) == Opcodes.RETURN)
assert(BOOL.typedOpcode(Opcodes.IRETURN) == Opcodes.IRETURN)
@@ -66,7 +64,7 @@ class BTypesTest {
assert(FLOAT.typedOpcode(Opcodes.IRETURN) == Opcodes.FRETURN)
assert(LONG.typedOpcode(Opcodes.IRETURN) == Opcodes.LRETURN)
assert(DOUBLE.typedOpcode(Opcodes.IRETURN) == Opcodes.DRETURN)
- assert(ClassBType(jls).typedOpcode(Opcodes.IRETURN) == Opcodes.ARETURN)
+ assert(classBTypeFromSymbol(jls).typedOpcode(Opcodes.IRETURN) == Opcodes.ARETURN)
}
@Test
@@ -84,21 +82,9 @@ class BTypesTest {
}
}
+ // TODO @lry do more tests
@Test
- def parseMethodDescriptorTest() {
- val descriptors = List(
- "()V",
- "(ID)I",
- "([[I[D)[D",
- s"(L$jls;[L$jlo;)[[L$jls;",
- s"(IL$jlo;)L$jls;"
- )
- for (d <- descriptors) {
- assertEquals(d, MethodBType(d).descriptor)
- }
+ def maxTypeTest() {
- // class types in method descriptor need surrounding 'L' and ';'
- assertThrows[MatchError](MethodBType("(java/lang/String)V"), _ == "j (of class java.lang.Character)")
- assertThrows[AssertionError](MethodBType("I"), _ contains "Not a valid method descriptor")
}
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
new file mode 100644
index 0000000000..15bc1f427d
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
@@ -0,0 +1,79 @@
+package scala.tools.nsc.backend.jvm
+
+import org.junit.Assert._
+
+import scala.reflect.internal.util.BatchSourceFile
+import scala.reflect.io.VirtualDirectory
+import scala.tools.asm.Opcodes
+import scala.tools.asm.tree.{AbstractInsnNode, LabelNode, ClassNode, MethodNode}
+import scala.tools.cmd.CommandLineParser
+import scala.tools.nsc.{Settings, Global}
+import scala.tools.partest.ASMConverters
+import scala.collection.JavaConverters._
+
+object CodeGenTools {
+ import ASMConverters._
+
+ def genMethod( flags: Int = Opcodes.ACC_PUBLIC,
+ name: String = "m",
+ descriptor: String = "()V",
+ genericSignature: String = null,
+ throwsExceptions: Array[String] = null,
+ handlers: List[ExceptionHandler] = Nil,
+ localVars: List[LocalVariable] = Nil)(body: Instruction*): MethodNode = {
+ val node = new MethodNode(flags, name, descriptor, genericSignature, throwsExceptions)
+ applyToMethod(node, Method(body.toList, handlers, localVars))
+ node
+ }
+
+ def wrapInClass(method: MethodNode): ClassNode = {
+ val cls = new ClassNode()
+ cls.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "C", null, "java/lang/Object", null)
+ cls.methods.add(method)
+ cls
+ }
+
+ private def resetOutput(compiler: Global): Unit = {
+ compiler.settings.outputDirs.setSingleOutput(new VirtualDirectory("(memory)", None))
+ }
+
+ def newCompiler(defaultArgs: String = "-usejavacp", extraArgs: String = ""): Global = {
+ val settings = new Settings()
+ val args = (CommandLineParser tokenize defaultArgs) ++ (CommandLineParser tokenize extraArgs)
+ settings.processArguments(args, processAll = true)
+ val compiler = new Global(settings)
+ resetOutput(compiler)
+ compiler
+ }
+
+ def compile(compiler: Global)(code: String): List[(String, Array[Byte])] = {
+ compiler.reporter.reset()
+ resetOutput(compiler)
+ val run = new compiler.Run()
+ run.compileSources(List(new BatchSourceFile("unitTestSource.scala", code)))
+ val outDir = compiler.settings.outputDirs.getSingleOutput.get
+ (for (f <- outDir.iterator if !f.isDirectory) yield (f.name, f.toByteArray)).toList
+ }
+
+ def compileClasses(compiler: Global)(code: String): List[ClassNode] = {
+ compile(compiler)(code).map(p => AsmUtils.readClass(p._2)).sortBy(_.name)
+ }
+
+ def compileMethods(compiler: Global)(code: String): List[MethodNode] = {
+ compileClasses(compiler)(s"class C { $code }").head.methods.asScala.toList.filterNot(_.name == "<init>")
+ }
+
+ def singleMethodInstructions(compiler: Global)(code: String): List[Instruction] = {
+ val List(m) = compileMethods(compiler)(code)
+ instructionsFromMethod(m)
+ }
+
+ def singleMethod(compiler: Global)(code: String): Method = {
+ val List(m) = compileMethods(compiler)(code)
+ convertMethod(m)
+ }
+
+ def assertSameCode(actual: List[Instruction], expected: List[Instruction]): Unit = {
+ assertTrue(s"\nExpected: $expected\nActual : $actual", actual === expected)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
new file mode 100644
index 0000000000..2fb5bb8052
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/DirectCompileTest.scala
@@ -0,0 +1,81 @@
+package scala.tools.nsc.backend.jvm
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Assert._
+import CodeGenTools._
+import scala.tools.asm.Opcodes._
+import scala.tools.partest.ASMConverters._
+
+@RunWith(classOf[JUnit4])
+class DirectCompileTest {
+ val compiler = newCompiler(extraArgs = "-Ybackend:GenBCode")
+
+ @Test
+ def testCompile(): Unit = {
+ val List(("C.class", bytes)) = compile(compiler)(
+ """
+ |class C {
+ | def f = 1
+ |}
+ """.stripMargin)
+ def s(i: Int, n: Int) = (bytes(i) & 0xff) << n
+ assertTrue((s(0, 24) | s(1, 16) | s(2, 8) | s(3, 0)) == 0xcafebabe) // mocha java latte machiatto surpreme dark roasted espresso
+ }
+
+ @Test
+ def testCompileClasses(): Unit = {
+ val List(cClass, cModuleClass) = compileClasses(compiler)(
+ """
+ |class C
+ |object C
+ """.stripMargin)
+
+ assertTrue(cClass.name == "C")
+ assertTrue(cModuleClass.name == "C$")
+
+ val List(dMirror, dModuleClass) = compileClasses(compiler)(
+ """
+ |object D
+ """.stripMargin)
+
+ assertTrue(dMirror.name == "D")
+ assertTrue(dModuleClass.name == "D$")
+ }
+
+ @Test
+ def testCompileMethods(): Unit = {
+ val List(f, g) = compileMethods(compiler)(
+ """
+ |def f = 10
+ |def g = f
+ """.stripMargin)
+ assertTrue(f.name == "f")
+ assertTrue(g.name == "g")
+
+ assertTrue(instructionsFromMethod(f).dropNonOp ===
+ List(IntOp(BIPUSH, 10), Op(IRETURN)))
+
+ assertTrue(instructionsFromMethod(g).dropNonOp ===
+ List(VarOp(ALOAD, 0), Invoke(INVOKEVIRTUAL, "C", "f", "()I", false), Op(IRETURN)))
+ }
+
+ @Test
+ def testDropNonOpAliveLabels(): Unit = {
+ val List(f) = compileMethods(compiler)("""def f(x: Int) = if (x == 0) "a" else "b"""")
+ assertTrue(instructionsFromMethod(f).dropNonOp === List(
+ VarOp(ILOAD, 1),
+ Op(ICONST_0),
+ Jump(IF_ICMPEQ, Label(6)),
+ Jump(GOTO, Label(10)),
+ Label(6),
+ Ldc(LDC, "a"),
+ Jump(GOTO, Label(13)),
+ Label(10),
+ Ldc(LDC, "b"),
+ Label(13),
+ Op(ARETURN)
+ ))
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala
new file mode 100644
index 0000000000..57fa1a7b66
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/EmptyExceptionHandlersTest.scala
@@ -0,0 +1,92 @@
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import scala.tools.asm.Opcodes._
+import org.junit.Assert._
+
+import CodeGenTools._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+
+@RunWith(classOf[JUnit4])
+class EmptyExceptionHandlersTest {
+
+ val exceptionDescriptor = "java/lang/Exception"
+
+ @Test
+ def eliminateEmpty(): Unit = {
+ val handlers = List(ExceptionHandler(Label(1), Label(2), Label(2), Some(exceptionDescriptor)))
+ val asmMethod = genMethod(handlers = handlers)(
+ Label(1),
+ Label(2),
+ Op(RETURN)
+ )
+ assertTrue(convertMethod(asmMethod).handlers.length == 1)
+ LocalOpt.removeEmptyExceptionHandlers(asmMethod)
+ assertTrue(convertMethod(asmMethod).handlers.isEmpty)
+ }
+
+ @Test
+ def eliminateHandlersGuardingNops(): Unit = {
+ val handlers = List(ExceptionHandler(Label(1), Label(2), Label(2), Some(exceptionDescriptor)))
+ val asmMethod = genMethod(handlers = handlers)(
+ Label(1), // nops only
+ Op(NOP),
+ Op(NOP),
+ Jump(GOTO, Label(3)),
+ Op(NOP),
+ Label(3),
+ Op(NOP),
+ Jump(GOTO, Label(4)),
+
+ Label(2), // handler
+ Op(ACONST_NULL),
+ Op(ATHROW),
+
+ Label(4), // return
+ Op(RETURN)
+ )
+ assertTrue(convertMethod(asmMethod).handlers.length == 1)
+ LocalOpt.removeEmptyExceptionHandlers(asmMethod)
+ assertTrue(convertMethod(asmMethod).handlers.isEmpty)
+ }
+
+ val noOptCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:l:none")
+ val dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
+
+ @Test
+ def eliminateUnreachableHandler(): Unit = {
+ val code = "def f: Unit = try { } catch { case _: Exception => println(0) }; println(1)"
+
+ assertTrue(singleMethod(noOptCompiler)(code).handlers.length == 1)
+ val optMethod = singleMethod(dceCompiler)(code)
+ assertTrue(optMethod.handlers.isEmpty)
+
+ val code2 =
+ """def f: Unit = {
+ | println(0)
+ | return
+ | try { throw new Exception("") } // removed by dce, so handler will be removed as well
+ | catch { case _: Exception => println(1) }
+ | println(2)
+ |}""".stripMargin
+
+ assertTrue(singleMethod(dceCompiler)(code2).handlers.isEmpty)
+ }
+
+ @Test
+ def keepAliveHandlers(): Unit = {
+ val code =
+ """def f: Int = {
+ | println(0)
+ | try { 1 }
+ | catch { case _: Exception => 2 }
+ |}""".stripMargin
+
+ assertTrue(singleMethod(dceCompiler)(code).handlers.length == 1)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
new file mode 100644
index 0000000000..a3bd7ae6fe
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
@@ -0,0 +1,217 @@
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import scala.tools.asm.Opcodes._
+import org.junit.Assert._
+
+import scala.tools.testing.AssertUtil._
+
+import CodeGenTools._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+
+@RunWith(classOf[JUnit4])
+class UnreachableCodeTest {
+ import UnreachableCodeTest._
+
+ // jvm-1.6 enables emitting stack map frames, which impacts the code generation wrt dead basic blocks,
+ // see comment in BCodeBodyBuilder
+ val dceCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:unreachable-code")
+ val noOptCompiler = newCompiler(extraArgs = "-target:jvm-1.6 -Ybackend:GenBCode -Yopt:l:none")
+
+ // jvm-1.5 disables computing stack map frames, and it emits dead code as-is.
+ val noOptNoFramesCompiler = newCompiler(extraArgs = "-target:jvm-1.5 -Ybackend:GenBCode -Yopt:l:none")
+
+ @Test
+ def basicElimination(): Unit = {
+ assertEliminateDead(
+ Op(ACONST_NULL),
+ Op(ATHROW),
+ Op(RETURN).dead
+ )
+
+ assertEliminateDead(
+ Op(RETURN)
+ )
+
+ assertEliminateDead(
+ Op(RETURN),
+ Op(ACONST_NULL).dead,
+ Op(ATHROW).dead
+ )
+ }
+
+ @Test
+ def eliminateNop(): Unit = {
+ assertEliminateDead(
+ // not dead, since visited by data flow analysis. need a different opt to eliminate it.
+ Op(NOP),
+ Op(RETURN),
+ Op(NOP).dead
+ )
+ }
+
+ @Test
+ def eliminateBranchOver(): Unit = {
+ assertEliminateDead(
+ Jump(GOTO, Label(1)),
+ Op(ACONST_NULL).dead,
+ Op(ATHROW).dead,
+ Label(1),
+ Op(RETURN)
+ )
+
+ assertEliminateDead(
+ Jump(GOTO, Label(1)),
+ Label(1),
+ Op(RETURN)
+ )
+ }
+
+ @Test
+ def deadLabelsRemain(): Unit = {
+ assertEliminateDead(
+ Op(RETURN),
+ Jump(GOTO, Label(1)).dead,
+ // not dead - labels may be referenced from other places in a classfile (eg exceptions table).
+ // will need a different opt to get rid of them
+ Label(1)
+ )
+ }
+
+ @Test
+ def pushPopNotEliminated(): Unit = {
+ assertEliminateDead(
+ // not dead, visited by data flow analysis.
+ Op(ACONST_NULL),
+ Op(POP),
+ Op(RETURN)
+ )
+ }
+
+ @Test
+ def nullnessNotConsidered(): Unit = {
+ assertEliminateDead(
+ Op(ACONST_NULL),
+ Jump(IFNULL, Label(1)),
+ Op(RETURN), // not dead
+ Label(1),
+ Op(RETURN)
+ )
+ }
+
+ @Test
+ def basicEliminationCompiler(): Unit = {
+ val code = "def f: Int = { return 1; 2 }"
+ val withDce = singleMethodInstructions(dceCompiler)(code)
+ assertSameCode(withDce.dropNonOp, List(Op(ICONST_1), Op(IRETURN)))
+
+ val noDce = singleMethodInstructions(noOptCompiler)(code)
+
+ // The emitted code is ICONST_1, IRETURN, ICONST_2, IRETURN. The latter two are dead.
+ //
+ // GenBCode puts the last IRETURN into a new basic block: it emits a label before the second
+ // IRETURN. This is an implementation detail, it may change; it affects the outcome of this test.
+ //
+ // During classfile writing with COMPUTE_FAMES (-target:jvm-1.6 or larger), the ClassfileWriter
+ // puts the ICONST_2 into a new basic block, because the preceding operation (IRETURN) ends
+ // the current block. We get something like
+ //
+ // L1: ICONST_1; IRETURN
+ // L2: ICONST_2 << dead
+ // L3: IRETURN << dead
+ //
+ // Finally, instructions in the dead basic blocks are replaced by ATHROW, as explained in
+ // a comment in BCodeBodyBuilder.
+ assertSameCode(noDce.dropNonOp, List(Op(ICONST_1), Op(IRETURN), Op(ATHROW), Op(ATHROW)))
+
+ // when NOT computing stack map frames, ASM's ClassWriter does not replace dead code by NOP/ATHROW
+ val noDceNoFrames = singleMethodInstructions(noOptNoFramesCompiler)(code)
+ assertSameCode(noDceNoFrames.dropNonOp, List(Op(ICONST_1), Op(IRETURN), Op(ICONST_2), Op(IRETURN)))
+ }
+
+ @Test
+ def eliminateDeadCatchBlocks(): Unit = {
+ val code = "def f: Int = { return 0; try { 1 } catch { case _: Exception => 2 } }"
+ assertSameCode(singleMethodInstructions(dceCompiler)(code).dropNonOp,
+ List(Op(ICONST_0), Op(IRETURN)))
+
+ val code2 = "def f: Unit = { try { } catch { case _: Exception => () }; () }"
+ // DCE only removes dead basic blocks, but not NOPs, and also not useless jumps
+ assertSameCode(singleMethodInstructions(dceCompiler)(code2).dropNonOp,
+ List(Op(NOP), Jump(GOTO, Label(33)), Label(33), Op(RETURN)))
+
+ val code3 = "def f: Unit = { try { } catch { case _: Exception => try { } catch { case _: Exception => () } }; () }"
+ assertSameCode(singleMethodInstructions(dceCompiler)(code3).dropNonOp,
+ List(Op(NOP), Jump(GOTO, Label(33)), Label(33), Op(RETURN)))
+
+ val code4 = "def f: Unit = { try { try { } catch { case _: Exception => () } } catch { case _: Exception => () }; () }"
+ assertSameCode(singleMethodInstructions(dceCompiler)(code4).dropNonOp,
+ List(Op(NOP), Jump(GOTO, Label(4)), Label(4), Jump(GOTO, Label(7)), Label(7), Op(RETURN)))
+ }
+
+ @Test // test the dce-testing tools
+ def metaTest(): Unit = {
+ assertEliminateDead() // no instructions
+
+ assertThrows[AssertionError](
+ assertEliminateDead(Op(RETURN).dead),
+ _.contains("Expected: List()\nActual : List(Op(RETURN))")
+ )
+
+ assertThrows[AssertionError](
+ assertEliminateDead(Op(RETURN), Op(RETURN)),
+ _.contains("Expected: List(Op(RETURN), Op(RETURN))\nActual : List(Op(RETURN))")
+ )
+ }
+
+ @Test
+ def bytecodeEquivalence: Unit = {
+ assertTrue(List(VarOp(ILOAD, 1)) ===
+ List(VarOp(ILOAD, 2)))
+ assertTrue(List(VarOp(ILOAD, 1), VarOp(ISTORE, 1)) ===
+ List(VarOp(ILOAD, 2), VarOp(ISTORE, 2)))
+
+ // the first Op will associate 1->2, then the 2->2 will fail
+ assertFalse(List(VarOp(ILOAD, 1), VarOp(ISTORE, 2)) ===
+ List(VarOp(ILOAD, 2), VarOp(ISTORE, 2)))
+
+ // will associate 1->2 and 2->1, which is OK
+ assertTrue(List(VarOp(ILOAD, 1), VarOp(ISTORE, 2)) ===
+ List(VarOp(ILOAD, 2), VarOp(ISTORE, 1)))
+
+ assertTrue(List(Label(1), Label(2), Label(1)) ===
+ List(Label(2), Label(4), Label(2)))
+ assertTrue(List(LineNumber(1, Label(1)), Label(1)) ===
+ List(LineNumber(1, Label(3)), Label(3)))
+ assertFalse(List(LineNumber(1, Label(1)), Label(1)) ===
+ List(LineNumber(1, Label(3)), Label(1)))
+
+ assertTrue(List(TableSwitch(TABLESWITCH, 1, 3, Label(4), List(Label(5), Label(6))), Label(4), Label(5), Label(6)) ===
+ List(TableSwitch(TABLESWITCH, 1, 3, Label(9), List(Label(3), Label(4))), Label(9), Label(3), Label(4)))
+
+ assertTrue(List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(3)), List("java/lang/Object", Label(4))), Label(3), Label(4)) ===
+ List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(1)), List("java/lang/Object", Label(3))), Label(1), Label(3)))
+ }
+}
+
+object UnreachableCodeTest {
+ import scala.language.implicitConversions
+ implicit def aliveInstruction(ins: Instruction): (Instruction, Boolean) = (ins, true)
+
+ implicit class MortalInstruction(val ins: Instruction) extends AnyVal {
+ def dead: (Instruction, Boolean) = (ins, false)
+ }
+
+ def assertEliminateDead(code: (Instruction, Boolean)*): Unit = {
+ val cls = wrapInClass(genMethod()(code.map(_._1): _*))
+ LocalOpt.removeUnreachableCode(cls)
+ val nonEliminated = instructionsFromMethod(cls.methods.get(0))
+ val expectedLive = code.filter(_._2).map(_._1).toList
+ assertSameCode(nonEliminated, expectedLive)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala
new file mode 100644
index 0000000000..24a1f9d1c1
--- /dev/null
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnusedLocalVariablesTest.scala
@@ -0,0 +1,87 @@
+package scala.tools.nsc
+package backend.jvm
+package opt
+
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.junit.Test
+import scala.tools.asm.Opcodes._
+import org.junit.Assert._
+import scala.collection.JavaConverters._
+
+import CodeGenTools._
+import scala.tools.partest.ASMConverters
+import ASMConverters._
+
+@RunWith(classOf[JUnit4])
+class UnusedLocalVariablesTest {
+ val dceCompiler = newCompiler(extraArgs = "-Ybackend:GenBCode -Yopt:unreachable-code")
+
+ @Test
+ def removeUnusedVar(): Unit = {
+ val code = """def f(a: Long, b: String, c: Double): Unit = { return; var x = a; var y = x + 10 }"""
+ assertLocalVarCount(code, 4) // `this, a, b, c`
+
+ val code2 = """def f(): Unit = { var x = if (true) return else () }"""
+ assertLocalVarCount(code2, 1) // x is eliminated, constant folding in scalac removes the if
+
+ val code3 = """def f: Unit = return""" // paramless method
+ assertLocalVarCount(code3, 1) // this
+ }
+
+ @Test
+ def keepUsedVar(): Unit = {
+ val code = """def f(a: Long, b: String, c: Double): Unit = { val x = 10 + a; val y = x + 10 }"""
+ assertLocalVarCount(code, 6)
+
+ val code2 = """def f(a: Long): Unit = { var x = if (a == 0l) return else () }"""
+ assertLocalVarCount(code2, 3) // remains
+ }
+
+ @Test
+ def constructorLocals(): Unit = {
+ val code = """class C {
+ | def this(a: Int) = {
+ | this()
+ | throw new Exception("")
+ | val y = 0
+ | }
+ |}
+ |""".stripMargin
+ val cls = compileClasses(dceCompiler)(code).head
+ val m = convertMethod(cls.methods.asScala.toList.find(_.desc == "(I)V").get)
+ assertTrue(m.localVars.length == 2) // this, a, but not y
+
+
+ val code2 =
+ """class C {
+ | {
+ | throw new Exception("")
+ | val a = 0
+ | }
+ |}
+ |
+ |object C {
+ | {
+ | throw new Exception("")
+ | val b = 1
+ | }
+ |}
+ """.stripMargin
+
+ val clss2 = compileClasses(dceCompiler)(code2)
+ val cls2 = clss2.find(_.name == "C").get
+ val companion2 = clss2.find(_.name == "C$").get
+
+ val clsConstr = convertMethod(cls2.methods.asScala.toList.find(_.name == "<init>").get)
+ val companionConstr = convertMethod(companion2.methods.asScala.toList.find(_.name == "<init>").get)
+
+ assertTrue(clsConstr.localVars.length == 1) // this
+ assertTrue(companionConstr.localVars.length == 1) // this
+ }
+
+ def assertLocalVarCount(code: String, numVars: Int): Unit = {
+ assertTrue(singleMethod(dceCompiler)(code).localVars.length == numVars)
+ }
+
+}
diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala
index 960d7f8ac1..eda0c27834 100644
--- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala
+++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala
@@ -39,7 +39,7 @@ class SettingsTest {
}
// for the given args, select the desired setting
- private def check(args: String*)(b: MutableSettings => MutableSettings#BooleanSetting): MutableSettings#BooleanSetting = {
+ private def check(args: String*)(b: MutableSettings => Boolean): Boolean = {
val s = new MutableSettings(msg => throw new IllegalArgumentException(msg))
val (ok, residual) = s.processArguments(args.toList, processAll = true)
assert(residual.isEmpty)
@@ -54,7 +54,114 @@ class SettingsTest {
@Test def anonymousLintersCanBeNamed() {
assertTrue(check("-Xlint")(_.warnMissingInterpolator)) // among Xlint
assertFalse(check("-Xlint:-missing-interpolator")(_.warnMissingInterpolator))
+
+ // positive overrides negative, but not the other way around
+ assertTrue(check("-Xlint:-missing-interpolator,missing-interpolator")(_.warnMissingInterpolator))
+ assertTrue(check("-Xlint:-missing-interpolator", "-Xlint:missing-interpolator")(_.warnMissingInterpolator))
+
+ assertTrue(check("-Xlint:missing-interpolator,-missing-interpolator")(_.warnMissingInterpolator))
+ assertTrue(check("-Xlint:missing-interpolator", "-Xlint:-missing-interpolator")(_.warnMissingInterpolator))
+
+ // -Xlint:_ adds all possible choices, but explicit negative settings will override
+ assertFalse(check("-Xlint:-missing-interpolator,_")(_.warnMissingInterpolator))
+ assertFalse(check("-Xlint:-missing-interpolator", "-Xlint:_")(_.warnMissingInterpolator))
+ assertFalse(check("-Xlint:_", "-Xlint:-missing-interpolator")(_.warnMissingInterpolator))
+ assertFalse(check("-Xlint:_,-missing-interpolator")(_.warnMissingInterpolator))
+
+ // -Xlint is the same as -Xlint:_
assertFalse(check("-Xlint:-missing-interpolator", "-Xlint")(_.warnMissingInterpolator))
assertFalse(check("-Xlint", "-Xlint:-missing-interpolator")(_.warnMissingInterpolator))
+
+ // combination of positive, negative and _
+ assertTrue(check("-Xlint:_,-missing-interpolator,missing-interpolator")(_.warnMissingInterpolator))
+ assertTrue(check("-Xlint:-missing-interpolator,_,missing-interpolator")(_.warnMissingInterpolator))
+ assertTrue(check("-Xlint:-missing-interpolator,missing-interpolator,_")(_.warnMissingInterpolator))
+ assertTrue(check("-Xlint:missing-interpolator,-missing-interpolator,_")(_.warnMissingInterpolator))
+ assertTrue(check("-Xlint:missing-interpolator,_,-missing-interpolator")(_.warnMissingInterpolator))
+ }
+
+ @Test def xLintInvalidChoices(): Unit = {
+ assertThrows[IllegalArgumentException](check("-Xlint:-_")(_.warnAdaptedArgs))
+ assertThrows[IllegalArgumentException](check("-Xlint:-warn-adapted-args")(_.warnAdaptedArgs)) // "warn-" should not be there
+ }
+
+ @Test def xLintNonColonated(): Unit = {
+ assertTrue(check("-Xlint", "adapted-args", "-deprecation")(_.warnAdaptedArgs))
+ assertFalse(check("-Xlint", "adapted-args", "-deprecation")(_.warnMissingInterpolator))
+ assertTrue(check("-Xlint", "adapted-args", "missing-interpolator", "-deprecation")(s => s.warnMissingInterpolator && s.warnAdaptedArgs))
+ assertThrows[IllegalArgumentException](check("-Xlint", "adapted-args", "-missing-interpolator")(_.warnAdaptedArgs)) // non-colonated: cannot provide negative args
+ }
+
+ @Test def xLintContainsValues(): Unit = {
+ // make sure that lint.contains and lint.value.contains are consistent
+ def t(s: MutableSettings, v: String) = {
+ val r = s.lint.contains(v)
+ assertSame(r, s.lint.value.contains((s.LintWarnings withName v).asInstanceOf[s.lint.domain.Value]))
+ r
+ }
+
+ assertTrue(check("-Xlint")(t(_, "adapted-args")))
+ assertTrue(check("-Xlint:_")(t(_, "adapted-args")))
+ assertFalse(check("-Xlint:_,-adapted-args")(t(_, "adapted-args")))
+ assertFalse(check("-Xlint:-adapted-args,_")(t(_, "adapted-args")))
+ assertTrue(check("-Xlint:-adapted-args,_,adapted-args")(t(_, "adapted-args")))
+ }
+
+ @Test def xLintDeprecatedAlias(): Unit = {
+ assertTrue(check("-Ywarn-adapted-args")(_.warnAdaptedArgs))
+ assertTrue(check("-Xlint:_,-adapted-args", "-Ywarn-adapted-args")(_.warnAdaptedArgs))
+ assertTrue(check("-Xlint:-adapted-args", "-Ywarn-adapted-args")(_.warnAdaptedArgs))
+ assertTrue(check("-Ywarn-adapted-args", "-Xlint:-adapted-args,_")(_.warnAdaptedArgs))
+
+ assertFalse(check("-Ywarn-adapted-args:false")(_.warnAdaptedArgs))
+ assertFalse(check("-Ywarn-adapted-args:false", "-Xlint:_")(_.warnAdaptedArgs))
+ assertFalse(check("-Ywarn-adapted-args:false", "-Xlint:_,-adapted-args")(_.warnAdaptedArgs))
+ assertTrue(check("-Ywarn-adapted-args:false", "-Xlint:_,adapted-args")(_.warnAdaptedArgs))
+ }
+
+ @Test def expandingMultichoice(): Unit = {
+ val s = new MutableSettings(msg => throw new IllegalArgumentException(msg))
+ object mChoices extends s.MultiChoiceEnumeration {
+ val a = Choice("a")
+ val b = Choice("b")
+ val c = Choice("c")
+ val d = Choice("d")
+
+ val ab = Choice("ab", expandsTo = List(a, b))
+ val ac = Choice("ac", expandsTo = List(a, c))
+ val uber = Choice("uber", expandsTo = List(ab, d))
+ }
+ val m = s.MultiChoiceSetting("-m", "args", "magic sauce", mChoices, Some(List("ac")))
+
+ def check(args: String*)(t: s.MultiChoiceSetting[mChoices.type] => Boolean): Boolean = {
+ m.clear()
+ val (ok, rest) = s.processArguments(args.toList, processAll = true)
+ assert(rest.isEmpty)
+ t(m)
+ }
+
+ import mChoices._
+
+ assertTrue(check("-m")(_.value == Set(a,c)))
+ assertTrue(check("-m:a,-b,c")(_.value == Set(a,c)))
+
+ // expanding options don't end up in the value set, only the terminal ones
+ assertTrue(check("-m:ab,ac")(_.value == Set(a,b,c)))
+ assertTrue(check("-m:_")(_.value == Set(a,b,c,d)))
+ assertTrue(check("-m:uber,ac")(_.value == Set(a,b,c,d))) // recursive expansion of uber
+
+ // explicit nays
+ assertTrue(check("-m:_,-b")(_.value == Set(a,c,d)))
+ assertTrue(check("-m:b,_,-b")(_.value == Set(a,b,c,d)))
+ assertTrue(check("-m:ac,-c")(_.value == Set(a)))
+ assertTrue(check("-m:ac,-a,-c")(_.value == Set()))
+ assertTrue(check("-m:-d,ac")(_.value == Set(a,c)))
+ assertTrue(check("-m:-b,ac,uber")(_.value == Set(a,c,d)))
+
+ assertFalse(check("-m:uber")(_.contains("i-m-not-an-option")))
+
+ assertThrows[IllegalArgumentException](check("-m:-_")(_ => true), _ contains "'-_' is not a valid choice")
+ assertThrows[IllegalArgumentException](check("-m:a,b,-ab")(_ => true), _ contains "'ab' cannot be negated")
+ assertThrows[IllegalArgumentException](check("-m:a,ac,-uber,uber")(_ => true), _ contains "'uber' cannot be negated")
}
}
diff --git a/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala b/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala
index 355771bf04..d424f12710 100644
--- a/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala
+++ b/test/junit/scala/tools/nsc/symtab/CannotHaveAttrsTest.scala
@@ -47,7 +47,7 @@ class CannotHaveAttrsTest {
assertEquals(t.tpe, NoType)
}
- @Test
+ @Test @org.junit.Ignore // SI-8816
def nonDefaultPosAssignmentFails = {
val pos = new OffsetPosition(null, 0)
attrlessTrees.foreach { t =>
@@ -56,7 +56,7 @@ class CannotHaveAttrsTest {
}
}
- @Test
+ @Test @org.junit.Ignore // SI-8816
def nonDefaultTpeAssignmentFails = {
val tpe = typeOf[Int]
attrlessTrees.foreach { t =>
diff --git a/test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala b/test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala
index cf09abdfff..effbfb2f7c 100644
--- a/test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala
+++ b/test/junit/scala/tools/nsc/symtab/FreshNameExtractorTest.scala
@@ -36,7 +36,7 @@ class FreshNameExtractorTest {
}
}
- @Test
+ @Test @org.junit.Ignore // SI-8818
def extractionsFailsIfNameDoesntEndWithNumber = {
val Creator = new FreshNameCreator(prefixes.head)
val Extractor = new FreshNameExtractor(prefixes.head)
@@ -44,4 +44,4 @@ class FreshNameExtractorTest {
val Extractor(_) = TermName(Creator.newName("foo") + "bar")
}
}
-} \ No newline at end of file
+}
diff --git a/test/junit/scala/tools/nsc/symtab/StdNamesTest.scala b/test/junit/scala/tools/nsc/symtab/StdNamesTest.scala
index 4a39cf9d48..524d2e45e0 100644
--- a/test/junit/scala/tools/nsc/symtab/StdNamesTest.scala
+++ b/test/junit/scala/tools/nsc/symtab/StdNamesTest.scala
@@ -15,12 +15,16 @@ class StdNamesTest {
@Test
def testNewTermNameInvalid(): Unit = {
- assertThrows[IllegalArgumentException](newTermName("foo".toCharArray, 0, -1))
- assertThrows[IllegalArgumentException](newTermName("foo".toCharArray, 0, 0))
assertThrows[IllegalArgumentException](newTermName("foo".toCharArray, -1, 1))
}
@Test
+ def testNewTermNameNegativeLenght(): Unit = {
+ assertEquals(nme.EMPTY, newTermName("foo".toCharArray, 0, -1))
+ assertEquals(nme.EMPTY, newTermName("foo".toCharArray, 0, 0))
+ }
+
+ @Test
def testUnspecializedName(): Unit = {
def test(expected: Name, nme: Name) {
assertEquals(expected, unspecializedName(nme))
diff --git a/test/junit/scala/tools/testing/AssertThrowsTest.scala b/test/junit/scala/tools/testing/AssertThrowsTest.scala
index a70519e63c..d91e450bac 100644
--- a/test/junit/scala/tools/testing/AssertThrowsTest.scala
+++ b/test/junit/scala/tools/testing/AssertThrowsTest.scala
@@ -31,4 +31,13 @@ class AssertThrowsTest {
}
})
-} \ No newline at end of file
+ @Test
+ def errorIfNoThrow: Unit = {
+ try {
+ assertThrows[Foo] { () }
+ } catch {
+ case e: AssertionError => return
+ }
+ assert(false, "assertThrows should error if the tested expression does not throw anything")
+ }
+}
diff --git a/test/junit/scala/tools/testing/AssertUtil.scala b/test/junit/scala/tools/testing/AssertUtil.scala
index 9a97c5114f..9b4833d46b 100644
--- a/test/junit/scala/tools/testing/AssertUtil.scala
+++ b/test/junit/scala/tools/testing/AssertUtil.scala
@@ -19,6 +19,8 @@ object AssertUtil {
val clazz = manifest.runtimeClass
if (!clazz.isAssignableFrom(e.getClass))
throw e
+ else return
}
+ throw new AssertionError("Expression did not throw!")
}
}
diff --git a/test/scaladoc/resources/code-indent.scala b/test/scaladoc/resources/code-indent.scala
index 88946ffc7f..2eee3352b4 100644
--- a/test/scaladoc/resources/code-indent.scala
+++ b/test/scaladoc/resources/code-indent.scala
@@ -20,6 +20,12 @@
* an alternative
* the e l s e branch
* }}}
+ * {{{
+ * Trait example {
+ * Val x = a
+ * Val y = b
+ * }
+ * }}}
* NB: Trailing spaces are necessary for this test!
* {{{
* l1
diff --git a/test/scaladoc/run/t8113.check b/test/scaladoc/run/t8113.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/t8113.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/t8113.scala b/test/scaladoc/run/t8113.scala
new file mode 100644
index 0000000000..f006213ef2
--- /dev/null
+++ b/test/scaladoc/run/t8113.scala
@@ -0,0 +1,36 @@
+import scala.tools.nsc.doc.base._
+import scala.tools.nsc.doc.base.comment._
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ /**
+ * Check out [[http://www.scala-lang.org
+ * this great website]]!
+ */
+ class Test
+ """
+
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ import access._
+
+ val test = rootPackage._class("Test")
+
+ // find Link
+ def find(body: Any): Option[Link] = body match {
+ case l: Link => Some(l)
+ case s: Seq[_] => s.toList.map(find(_)).flatten.headOption
+ case p: Product => p.productIterator.toList.map(find(_)).flatten.headOption
+ case _ => None
+ }
+
+ val link = find(test.comment.get.body).collect { case Link(ta, Text(ti)) => (ta, ti) }
+ assert(link.isDefined)
+ val expected = ("http://www.scala-lang.org", "this great website")
+ link.foreach {l => assert(l == expected, s"$l != $expected")}
+ }
+}
diff --git a/test/scaladoc/run/t8314.check b/test/scaladoc/run/t8314.check
new file mode 100644
index 0000000000..aa04c12c8f
--- /dev/null
+++ b/test/scaladoc/run/t8314.check
@@ -0,0 +1,3 @@
+Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This should be ), Monospace(Text(monospaced))))))))))
+
+Done.
diff --git a/test/scaladoc/run/t8314.scala b/test/scaladoc/run/t8314.scala
new file mode 100644
index 0000000000..7f6d6fdb00
--- /dev/null
+++ b/test/scaladoc/run/t8314.scala
@@ -0,0 +1,16 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+ override def code = """
+ /** This should be `monospaced` */
+ class A
+ """
+
+ def scaladocSettings = ""
+
+ def testModel(root: Package) = {
+ import access._
+ root._class("A").comment foreach println
+ }
+}
diff --git a/test/scaladoc/scalacheck/HtmlFactoryTest.scala b/test/scaladoc/scalacheck/HtmlFactoryTest.scala
index ef70e0bf21..da0f253a37 100644
--- a/test/scaladoc/scalacheck/HtmlFactoryTest.scala
+++ b/test/scaladoc/scalacheck/HtmlFactoryTest.scala
@@ -659,6 +659,7 @@ object Test extends Properties("HtmlFactory") {
s.contains("<pre>two lines, one useful</pre>") &&
s.contains("<pre>line1\nline2\nline3\nline4</pre>") &&
s.contains("<pre>a ragged example\na (condition)\n the t h e n branch\nan alternative\n the e l s e branch</pre>") &&
+ s.contains("<pre>Trait example {\n Val x = a\n Val y = b\n}</pre>") &&
s.contains("<pre>l1\n\nl2\n\nl3\n\nl4\n\nl5</pre>")
}
case _ => false
@@ -683,7 +684,7 @@ object Test extends Properties("HtmlFactory") {
oneAuthor match {
case node: scala.xml.Node => {
val s = node.toString
- s.contains("<h6>Author:</h6>")
+ s.contains("<h6>Author:</h6>") &&
s.contains("<p>The Only Author\n</p>")
}
case _ => false
@@ -696,8 +697,8 @@ object Test extends Properties("HtmlFactory") {
twoAuthors match {
case node: scala.xml.Node => {
val s = node.toString
- s.contains("<h6>Authors:</h6>")
- s.contains("<p>The First Author\n</p>")
+ s.contains("<h6>Authors:</h6>") &&
+ s.contains("<p>The First Author</p>") &&
s.contains("<p>The Second Author\n</p>")
}
case _ => false
diff --git a/versions.properties b/versions.properties
index cd53a343f6..454df6b757 100644
--- a/versions.properties
+++ b/versions.properties
@@ -27,7 +27,7 @@ actors-migration.version.number=1.1.0
jline.version=2.12
# external modules, used internally (not shipped)
-partest.version.number=1.0.0
+partest.version.number=1.0.1
scalacheck.version.number=1.11.4
# TODO: modularize the compiler