aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/src/dotty/tools/dotc/ast/TreeInfo.scala14
-rw-r--r--compiler/src/dotty/tools/dotc/core/Constants.scala14
-rw-r--r--compiler/src/dotty/tools/dotc/transform/FirstTransform.scala17
-rw-r--r--compiler/test/dotc/scala-collections.blacklist2
-rw-r--r--compiler/test/dotty/tools/dotc/CompilationTests.scala67
-rw-r--r--sbt-bridge/src/xsbt/CompilerClassLoader.scala16
-rw-r--r--tests/run/if-with-constant-cond.check4
-rw-r--r--tests/run/if-with-constant-cond.scala9
-rw-r--r--tests/run/inline-constant-in-constructor-1.check3
-rw-r--r--tests/run/inline-constant-in-constructor-1.scala15
-rw-r--r--tests/run/inline-constant-in-constructor-2.check3
-rw-r--r--tests/run/inline-constant-in-constructor-2.scala14
-rw-r--r--tests/run/inline-constant-in-constructor-3.check4
-rw-r--r--tests/run/inline-constant-in-constructor-3.scala19
-rw-r--r--tests/run/inline-constant-lazy-val.check2
-rw-r--r--tests/run/inline-constant-lazy-val.scala10
16 files changed, 159 insertions, 54 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
index f3bce4000..49187492e 100644
--- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -3,7 +3,7 @@ package dotc
package ast
import core._
-import Flags._, Trees._, Types._, Contexts._
+import Flags._, Trees._, Types._, Contexts._, Constants._
import Names._, StdNames._, NameOps._, Decorators._, Symbols._
import util.HashSet
import typer.ConstFold
@@ -426,8 +426,18 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
*/
def constToLiteral(tree: Tree)(implicit ctx: Context): Tree = {
val tree1 = ConstFold(tree)
+ def canInlineConstant(value: Constant): Boolean = {
+ val sym = tree1.symbol
+ isIdempotentExpr(tree1) && // see note in documentation
+ // lazy value must be initialized (would not be needed with isPureExpr)
+ !sym.is(Lazy) &&
+ // could hide initialization order issues (ex. val with constant type read before initialized)
+ (!ctx.owner.isLocalDummy || (!sym.is(Method) && !sym.is(Lazy) && value.isZero) ||
+ ctx.scala2Mode // ignore in Scala 2 because of inlined `final val` values
+ )
+ }
tree1.tpe.widenTermRefExpr match {
- case ConstantType(value) if isIdempotentExpr(tree1) => Literal(value)
+ case ConstantType(value) if canInlineConstant(value) => Literal(value)
case _ => tree1
}
}
diff --git a/compiler/src/dotty/tools/dotc/core/Constants.scala b/compiler/src/dotty/tools/dotc/core/Constants.scala
index ed388b7ec..8ea285c8d 100644
--- a/compiler/src/dotty/tools/dotc/core/Constants.scala
+++ b/compiler/src/dotty/tools/dotc/core/Constants.scala
@@ -53,6 +53,20 @@ object Constants {
def isNonUnitAnyVal = BooleanTag <= tag && tag <= DoubleTag
def isAnyVal = UnitTag <= tag && tag <= DoubleTag
+ /** Is the zero or un-initialized value of the type */
+ def isZero(implicit ctx: Context): Boolean = tag match {
+ case BooleanTag => !value.asInstanceOf[Boolean]
+ case ByteTag => value.asInstanceOf[Byte] == 0
+ case ShortTag => value.asInstanceOf[Short] == 0
+ case CharTag => value.asInstanceOf[Char] == 0
+ case IntTag => value.asInstanceOf[Int] == 0
+ case LongTag => value.asInstanceOf[Long] == 0L
+ case FloatTag => value.asInstanceOf[Float] == 0.0
+ case DoubleTag => value.asInstanceOf[Double] == 0.0
+ case NullTag => true
+ case _ => false
+ }
+
def tpe(implicit ctx: Context): Type = tag match {
case UnitTag => defn.UnitType
case BooleanTag => defn.BooleanType
diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala
index a3cf71ef2..767ee0901 100644
--- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala
+++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala
@@ -9,7 +9,7 @@ import dotty.tools.dotc.transform.TreeTransforms._
import ast.Trees._
import Flags._
import Types._
-import Constants.Constant
+import Constants._
import Contexts.Context
import Symbols._
import SymDenotations._
@@ -34,6 +34,8 @@ import StdNames._
* - drops branches of ifs using the rules
* if (true) A else B --> A
* if (false) A else B --> B
+ * if (C: true) A else B --> C; A
+ * if (C: false) A else B --> C; B
*/
class FirstTransform extends MiniPhaseTransform with InfoTransformer with AnnotationTransformer { thisTransformer =>
import ast.tpd._
@@ -190,11 +192,16 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota
override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo) =
constToLiteral(tree)
- override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) =
- tree.cond match {
- case Literal(Constant(c: Boolean)) => if (c) tree.thenp else tree.elsep
- case _ => tree
+ override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) = {
+ tree.cond.tpe.widenTermRefExpr match {
+ case ConstantType(Constant(condVal: Boolean)) =>
+ val selected = if (condVal) tree.thenp else tree.elsep
+ if (isPureExpr(tree.cond)) selected
+ else Block(tree.cond :: Nil, selected)
+ case _ =>
+ tree
}
+ }
// invariants: all modules have companion objects
// all types are TypeTrees
diff --git a/compiler/test/dotc/scala-collections.blacklist b/compiler/test/dotc/scala-collections.blacklist
index d6972a645..ae43e6eb4 100644
--- a/compiler/test/dotc/scala-collections.blacklist
+++ b/compiler/test/dotc/scala-collections.blacklist
@@ -81,3 +81,5 @@ scala/util/control/Exception.scala
# 51 | implicit def throwableSubtypeToCatcher[Ex <: Throwable: ClassTag, T](pf: PartialFunction[Ex, T]) =
# | ^
# | cyclic reference involving method mkCatcher
+
+scala/concurrent/duration/Duration.scala
diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala
index 334d347fc..91a453494 100644
--- a/compiler/test/dotty/tools/dotc/CompilationTests.scala
+++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala
@@ -29,13 +29,17 @@ class CompilationTests extends ParallelTesting {
@Test def compilePos: Unit = {
compileList("compileStdLib", StdLibSources.whitelisted, scala2Mode.and("-migration", "-Yno-inline")) +
- compileFilesInDir("../tests/pos", defaultOptions)
- }.checkCompile()
-
- @Test def compilePosScala2: Unit =
- compileFilesInDir("../tests/pos-scala2", scala2Mode).checkCompile()
-
- @Test def compilePosMixedFlags: Unit = {
+ compileDir("../compiler/src/dotty/tools/dotc/ast", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/config", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/core", allowDeepSubtypes) +
+ compileDir("../compiler/src/dotty/tools/dotc/transform", allowDeepSubtypes) +
+ compileDir("../compiler/src/dotty/tools/dotc/parsing", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/printing", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/reporting", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/typer", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/util", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/io", defaultOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc/core", noCheckOptions ++ classPath) +
compileFile("../tests/pos/nullarify.scala", defaultOptions.and("-Ycheck:nullarify")) +
compileFile("../tests/pos-scala2/rewrites.scala", scala2Mode.and("-rewrite")).copyToTarget() +
compileFile("../tests/pos-special/t8146a.scala", allowDeepSubtypes) +
@@ -65,23 +69,10 @@ class CompilationTests extends ParallelTesting {
"../scala-scala/src/library/scala/collection/mutable/SetLike.scala"
),
scala2Mode
- )
- }.checkCompile()
-
- @Test def compileCoreNoCheck: Unit =
- compileDir("../compiler/src/dotty/tools/dotc/core", noCheckOptions ++ classPath).checkCompile()
-
- @Test def compileDotcInternals: Unit = {
- compileDir("../compiler/src/dotty/tools/dotc/ast", defaultOptions) +
- compileDir("../compiler/src/dotty/tools/dotc/config", defaultOptions) +
- compileDir("../compiler/src/dotty/tools/dotc/core", allowDeepSubtypes) +
- compileDir("../compiler/src/dotty/tools/dotc/transform", allowDeepSubtypes) +
- compileDir("../compiler/src/dotty/tools/dotc/parsing", defaultOptions) +
- compileDir("../compiler/src/dotty/tools/dotc/printing", defaultOptions) +
- compileDir("../compiler/src/dotty/tools/dotc/reporting", defaultOptions) +
- compileDir("../compiler/src/dotty/tools/dotc/typer", defaultOptions) +
- compileDir("../compiler/src/dotty/tools/dotc/util", defaultOptions) +
- compileDir("../compiler/src/dotty/tools/io", defaultOptions)
+ ) +
+ compileFilesInDir("../tests/new", defaultOptions) +
+ compileFilesInDir("../tests/pos-scala2", scala2Mode)
+ compileFilesInDir("../tests/pos", defaultOptions)
}.checkCompile()
@Test def posTwice: Unit = {
@@ -140,17 +131,10 @@ class CompilationTests extends ParallelTesting {
)
}.times(2).checkCompile()
- // New tests -----------------------------------------------------------------
-
- @Test def compileNew: Unit =
- compileFilesInDir("../tests/new", defaultOptions).checkCompile()
-
// Negative tests ------------------------------------------------------------
- @Test def compileNeg: Unit =
- compileShallowFilesInDir("../tests/neg", defaultOptions).checkExpectedErrors()
-
- @Test def compileNegCustomFlags: Unit = {
+ @Test def compileNeg: Unit = {
+ compileShallowFilesInDir("../tests/neg", defaultOptions) +
compileFile("../tests/neg/customArgs/typers.scala", allowDoubleBindings) +
compileFile("../tests/neg/customArgs/overrideClass.scala", scala2Mode) +
compileFile("../tests/neg/customArgs/autoTuplingTest.scala", defaultOptions.and("-language:noAutoTupling")) +
@@ -180,7 +164,9 @@ class CompilationTests extends ParallelTesting {
// Pickling tests are very memory intensive and as such need to be run with a
// lower level of concurrency as to not kill their running VMs
- @Test def testPickling1: Unit = {
+ @Test def testPickling: Unit = {
+ compileDir("../compiler/src/dotty/tools", picklingOptions) +
+ compileDir("../compiler/src/dotty/tools/dotc", picklingOptions) +
compileFilesInDir("../tests/new", picklingOptions) +
compileFilesInDir("../tests/pickling", picklingOptions) +
compileDir("../library/src/dotty/runtime", picklingOptions) +
@@ -196,23 +182,12 @@ class CompilationTests extends ParallelTesting {
compileDir("../compiler/src/dotty/tools/dotc/typer", picklingOptions) +
compileDir("../compiler/src/dotty/tools/dotc/util", picklingOptions) +
compileDir("../compiler/src/dotty/tools/io", picklingOptions) +
- compileFile("../tests/pos/pickleinf.scala", picklingOptions)
- }.limitThreads(4).checkCompile()
-
- @Test def testPickling2: Unit = {
+ compileFile("../tests/pos/pickleinf.scala", picklingOptions) +
compileDir("../compiler/src/dotty/tools/dotc/core/classfile", picklingOptions) +
compileDir("../compiler/src/dotty/tools/dotc/core/tasty", picklingOptions) +
compileDir("../compiler/src/dotty/tools/dotc/core/unpickleScala2", picklingOptions)
}.limitThreads(4).checkCompile()
- @Test def testPickling3: Unit = {
- compileDir("../compiler/src/dotty/tools", picklingOptions)
- }.limitThreads(4).checkCompile()
-
- @Test def testPickling4: Unit = {
- compileDir("../compiler/src/dotty/tools/dotc", picklingOptions)
- }.limitThreads(4).checkCompile()
-
/** The purpose of this test is two-fold, being able to compile dotty
* bootstrapped, and making sure that TASTY can link against a compiled
* version of Dotty
diff --git a/sbt-bridge/src/xsbt/CompilerClassLoader.scala b/sbt-bridge/src/xsbt/CompilerClassLoader.scala
index 3cb3f344f..071141dcf 100644
--- a/sbt-bridge/src/xsbt/CompilerClassLoader.scala
+++ b/sbt-bridge/src/xsbt/CompilerClassLoader.scala
@@ -2,6 +2,8 @@ package xsbt
import java.net.{URL, URLClassLoader}
+import scala.collection.mutable
+
/** A classloader to run the compiler
*
* A CompilerClassLoader is constructed from a list of `urls` that need to be on
@@ -42,6 +44,14 @@ class CompilerClassLoader(urls: Array[URL], sbtLoader: ClassLoader)
}
object CompilerClassLoader {
+ /** Cache the result of `fixBridgeLoader`.
+ *
+ * Reusing ClassLoaders is important for warm performance since otherwise the
+ * JIT code cache for the compiler will be discarded between every call to
+ * the sbt `compile` task.
+ */
+ private[this] val fixedLoaderCache = new mutable.WeakHashMap[ClassLoader, ClassLoader]
+
/** Fix the compiler bridge ClassLoader
*
* Soundtrack: https://www.youtube.com/watch?v=imamcajBEJs
@@ -70,7 +80,11 @@ object CompilerClassLoader {
* @param bridgeLoader The classloader that sbt uses to load the compiler bridge
* @return A fixed classloader that works with dotty
*/
- def fixBridgeLoader(bridgeLoader: ClassLoader) = bridgeLoader match {
+ def fixBridgeLoader(bridgeLoader: ClassLoader): ClassLoader = synchronized {
+ fixedLoaderCache.getOrElseUpdate(bridgeLoader, computeFixedLoader(bridgeLoader))
+ }
+
+ private[this] def computeFixedLoader(bridgeLoader: ClassLoader) = bridgeLoader match {
case bridgeLoader: URLClassLoader =>
val dualLoader = bridgeLoader.getParent
val dualLoaderClass = dualLoader.getClass
diff --git a/tests/run/if-with-constant-cond.check b/tests/run/if-with-constant-cond.check
new file mode 100644
index 000000000..2e31d4f23
--- /dev/null
+++ b/tests/run/if-with-constant-cond.check
@@ -0,0 +1,4 @@
+cond1
+then1
+cond2
+else2
diff --git a/tests/run/if-with-constant-cond.scala b/tests/run/if-with-constant-cond.scala
new file mode 100644
index 000000000..db5959c84
--- /dev/null
+++ b/tests/run/if-with-constant-cond.scala
@@ -0,0 +1,9 @@
+
+object Test {
+
+ def main(args: Array[String]): Unit = {
+ if ({ println("cond1"); true }: true) println("then1") else println("else1")
+ if ({ println("cond2"); false }: false) println("then2") else println("else2")
+ }
+
+} \ No newline at end of file
diff --git a/tests/run/inline-constant-in-constructor-1.check b/tests/run/inline-constant-in-constructor-1.check
new file mode 100644
index 000000000..05035c26d
--- /dev/null
+++ b/tests/run/inline-constant-in-constructor-1.check
@@ -0,0 +1,3 @@
+assert
+s
+r init
diff --git a/tests/run/inline-constant-in-constructor-1.scala b/tests/run/inline-constant-in-constructor-1.scala
new file mode 100644
index 000000000..11a727fd6
--- /dev/null
+++ b/tests/run/inline-constant-in-constructor-1.scala
@@ -0,0 +1,15 @@
+
+
+abstract class A {
+ def s: Boolean = { println("s"); r }
+ def r: Boolean
+}
+
+object Test extends A {
+ assert({ println("assert"); r == s }) // r constant type not replaced by true, r not initialized yet
+ override val r: true = {
+ println("r init")
+ true
+ }
+ def main(args: Array[String]): Unit = {}
+}
diff --git a/tests/run/inline-constant-in-constructor-2.check b/tests/run/inline-constant-in-constructor-2.check
new file mode 100644
index 000000000..05035c26d
--- /dev/null
+++ b/tests/run/inline-constant-in-constructor-2.check
@@ -0,0 +1,3 @@
+assert
+s
+r init
diff --git a/tests/run/inline-constant-in-constructor-2.scala b/tests/run/inline-constant-in-constructor-2.scala
new file mode 100644
index 000000000..a8d351ab6
--- /dev/null
+++ b/tests/run/inline-constant-in-constructor-2.scala
@@ -0,0 +1,14 @@
+
+abstract class A {
+ def s: Boolean = { println("s"); r }
+ def r: Boolean
+}
+
+object Test extends A {
+ assert({ println("assert"); r == s }) // r constant type replaced by false
+ override val r: false = {
+ println("r init")
+ false
+ }
+ def main(args: Array[String]): Unit = {}
+}
diff --git a/tests/run/inline-constant-in-constructor-3.check b/tests/run/inline-constant-in-constructor-3.check
new file mode 100644
index 000000000..b0171e3d9
--- /dev/null
+++ b/tests/run/inline-constant-in-constructor-3.check
@@ -0,0 +1,4 @@
+assert
+r2
+s
+r init
diff --git a/tests/run/inline-constant-in-constructor-3.scala b/tests/run/inline-constant-in-constructor-3.scala
new file mode 100644
index 000000000..621ace231
--- /dev/null
+++ b/tests/run/inline-constant-in-constructor-3.scala
@@ -0,0 +1,19 @@
+
+
+abstract class A {
+ def s: Boolean = { println("s"); r }
+ def r: Boolean
+}
+
+object Test extends A {
+ assert({ println("assert"); r2 != s }) // s not initialized yet
+ def r2: true = {
+ println("r2")
+ true
+ }
+ override val r: true = {
+ println("r init")
+ true
+ }
+ def main(args: Array[String]): Unit = {}
+}
diff --git a/tests/run/inline-constant-lazy-val.check b/tests/run/inline-constant-lazy-val.check
new file mode 100644
index 000000000..16858db7a
--- /dev/null
+++ b/tests/run/inline-constant-lazy-val.check
@@ -0,0 +1,2 @@
+X
+Y
diff --git a/tests/run/inline-constant-lazy-val.scala b/tests/run/inline-constant-lazy-val.scala
new file mode 100644
index 000000000..5f4aaa12b
--- /dev/null
+++ b/tests/run/inline-constant-lazy-val.scala
@@ -0,0 +1,10 @@
+
+object Test {
+ lazy val x: true = { println("X"); true }
+
+ def main(args: Array[String]): Unit = {
+ lazy val y: true = { println("Y"); true }
+ x
+ y
+ }
+} \ No newline at end of file