diff --git a/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala b/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala index 9821c69713c0..0e1e159846f7 100644 --- a/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc.core import dotty.tools.dotc.ast.tpd._ import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types.Type /** Phantom erasure erases: @@ -27,4 +27,7 @@ object PhantomErasure { /** Returns the default erased tree for a phantom parameter ref */ def erasedParameterRef(implicit ctx: Context): Tree = ref(defn.ErasedPhantom_UNIT) + /** Is it a pure term inserted by the phantom erasure? */ + def isErasedPhantom(sym: Symbol)(implicit ctx: Context): Boolean = sym eq defn.ErasedPhantom_UNIT + } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index f91f19b23014..2d653d581e3c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -98,6 +98,7 @@ public enum ErrorMessageID { ExpectedStartOfTopLevelDefinitionID, MissingReturnTypeWithReturnStatementID, NoReturnFromInlineID, + ReturnOutsideMethodDefinitionID, ; public int errorNumber() { diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index b471a6b2453c..c6baa509f179 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -4,7 +4,7 @@ package reporting package diagnostic import dotc.core._ -import Contexts.Context +import Contexts.{Context, NoContext} import Decorators._ import Symbols._ import Names._ @@ -1747,4 +1747,14 @@ object messages { |returned from a method. |""" } + + case class ReturnOutsideMethodDefinition(owner: Symbol)(implicit ctx: Context) + extends Message(ReturnOutsideMethodDefinitionID) { + val kind = "Syntax" + val msg = hl"${"return"} outside method definition" + val explanation = + hl"""You used ${"return"} in ${owner}. + |${"return"} is a keyword and may only be used within method declarations. + |""" + } } diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/Simplify.scala b/compiler/src/dotty/tools/dotc/transform/localopt/Simplify.scala index c8fc99ff6322..c78536c3b4fc 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/Simplify.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/Simplify.scala @@ -11,6 +11,7 @@ import core.NameOps._ import transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} import config.Printers.simplify import ast.tpd +import dotty.tools.dotc.core.PhantomErasure import scala.annotation.tailrec @@ -62,6 +63,7 @@ class Simplify extends MiniPhaseTransform with IdentityDenotTransformer { new Devalify :: new Jumpjump :: new DropGoodCasts :: + new DropNoEffects(this) :: new ConstantFold(this) :: Nil @@ -174,13 +176,16 @@ object Simplify { } def isImmutableAccessor(t: Tree)(implicit ctx: Context): Boolean = { - val isImmutableGetter = t.symbol.isGetter && !t.symbol.is(Mutable | Lazy) - val isCaseAccessor = t.symbol.is(CaseAccessor) && !t.symbol.is(Mutable | Lazy) - val isProductAccessor = t.symbol.exists && - t.symbol.owner.derivesFrom(defn.ProductClass) && - t.symbol.owner.is(CaseClass) && - t.symbol.name.isSelectorName && - !t.symbol.info.decls.exists(_.is(Mutable | Lazy)) // Conservatively covers case class A(var x: Int) - isImmutableGetter || isCaseAccessor || isProductAccessor + val sym = t.symbol + val isImmutableGetter = sym.isGetter && !sym.is(Mutable | Lazy) + val isCaseAccessor = sym.is(CaseAccessor) && !sym.is(Mutable | Lazy) + val isProductAccessor = sym.exists && + sym.owner.derivesFrom(defn.ProductClass) && + sym.owner.is(CaseClass) && + sym.name.isSelectorName && + !sym.info.decls.exists(_.is(Mutable | Lazy)) // Conservatively covers case class A(var x: Int) + val isErasedPhantom = PhantomErasure.isErasedPhantom(sym) + + isImmutableGetter || isCaseAccessor || isProductAccessor || isErasedPhantom } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a69f509a677e..ab63b6f51580 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -979,8 +979,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def enclMethInfo(cx: Context): (Tree, Type) = { val owner = cx.owner - if (cx == NoContext || owner.isType) { - ctx.error("return outside method definition", tree.pos) + if (owner.isType) { + ctx.error(ReturnOutsideMethodDefinition(owner), tree.pos) (EmptyTree, WildcardType) } else if (owner != cx.outer.owner && owner.isRealMethod) { diff --git a/compiler/test/dotc/comptest.scala b/compiler/test/dotc/comptest.scala index 318f9cd80612..de9fb0b1e515 100644 --- a/compiler/test/dotc/comptest.scala +++ b/compiler/test/dotc/comptest.scala @@ -12,8 +12,6 @@ object comptest extends ParallelTesting { def isInteractive = true def testFilter = None - implicit val defaultOutputDir: String = "." - val posDir = "./tests/pos/" val negDir = "./tests/neg/" val dotcDir = "./src/dotty/" @@ -26,6 +24,7 @@ object comptest extends ParallelTesting { dotcDir + "tools/dotc/core/Types.scala", dotcDir + "tools/dotc/ast/Trees.scala" ), - TestFlags("", Array("-Ylog:frontend", "-Xprompt")) + TestFlags("", Array("-Ylog:frontend", "-Xprompt")), + outDirectory = "." ) } diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 2863bff4e48d..3eba2b1e7e8a 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -15,6 +15,7 @@ import dotty.tools.io.JFile class CompilationTests extends ParallelTesting { + import ParallelTesting._ import TestConfiguration._ import CompilationTests._ @@ -68,7 +69,6 @@ class CompilationTests extends ParallelTesting { compileFilesInDir("../tests/pos-special/strawman-collections", defaultOptions) + compileFile("../scala2-library/src/library/scala/collection/immutable/IndexedSeq.scala", defaultOptions) + compileFile("../scala2-library/src/library/scala/collection/parallel/mutable/ParSetLike.scala", defaultOptions) + - compileFile("../tests/pos/t2171.scala", defaultOptimised) + compileList( "parSetSubset", List( @@ -159,6 +159,7 @@ class CompilationTests extends ParallelTesting { @Test def compileNeg: Unit = { compileShallowFilesInDir("../tests/neg", defaultOptions) + + compileShallowFilesInDir("../tests/neg/no-optimise", 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")) + @@ -186,12 +187,7 @@ class CompilationTests extends ParallelTesting { @Test def runAll: Unit = { compileFilesInDir("../tests/run", defaultOptions) + - compileFile("../tests/run/i3018.scala", defaultOptimised) + - compileFile("../tests/run/blame_eye_triple_eee-double.scala", defaultOptimised) + - compileFile("../tests/run/blame_eye_triple_eee-float.scala", defaultOptimised) + - compileFile("../tests/run/run-bug4840.scala", defaultOptimised) + - compileFile("../tests/run/optimizer-array-load.scala", defaultOptimised) + - compileFile("../tests/run/constant-optimization.scala", defaultOptimised) + compileFilesInDir("../tests/run-no-optimise", defaultOptions) }.checkRuns() // Pickling Tests ------------------------------------------------------------ @@ -298,6 +294,13 @@ class CompilationTests extends ParallelTesting { tests.foreach(_.delete()) } + @Test def testOptimised: Unit = { + val outputDir = defaultOutputDir + "optimised/" + compileFilesInDir("../tests/pos", defaultOptimised, outputDir).checkCompile() + compileFilesInDir("../tests/run", defaultOptimised, outputDir).checkRuns() + compileShallowFilesInDir("../tests/neg", defaultOptimised, outputDir).checkExpectedErrors() + } + private val (compilerSources, backendSources, backendJvmSources) = { val compilerDir = Paths.get("../compiler/src") val compilerSources0 = sources(Files.walk(compilerDir)) diff --git a/compiler/test/dotty/tools/dotc/LinkOptimiseTests.scala b/compiler/test/dotty/tools/dotc/LinkOptimiseTests.scala index 70fa01c5f053..7aa2f0bcb651 100644 --- a/compiler/test/dotty/tools/dotc/LinkOptimiseTests.scala +++ b/compiler/test/dotty/tools/dotc/LinkOptimiseTests.scala @@ -13,6 +13,7 @@ import scala.concurrent.duration._ import scala.collection.JavaConverters._ class LinkOptimiseTests extends ParallelTesting { + import ParallelTesting._ import TestConfiguration._ import LinkOptimiseTests._ diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index 90497f54f8b4..c00d971a83e6 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -985,4 +985,18 @@ class ErrorMessagesTests extends ErrorMessagesTest { val NoReturnFromInline(method) :: Nil = messages assertEquals("method usesReturn", method.show) } + + @Test def returnOutsideMethodDefinition = + checkMessagesAfter("frontend") { + """object A { + | return 5 + |} + """.stripMargin + }.expect { (ictx, messages) => + implicit val ctx: Context = ictx + assertMessageCount(1, messages) + val ReturnOutsideMethodDefinition(owner) :: Nil = messages + assertEquals("object A", owner.show) + } + } diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index b5534337e82b..2fad926c0a79 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -1057,7 +1057,7 @@ trait ParallelTesting extends RunnerOrchestration { self => } /** Compiles a single file from the string path `f` using the supplied flags */ - def compileFile(f: String, flags: TestFlags)(implicit outDirectory: String): CompilationTest = { + def compileFile(f: String, flags: TestFlags, outDirectory: String = defaultOutputDir): CompilationTest = { val callingMethod = getCallingMethod() val sourceFile = new JFile(f) val parent = sourceFile.getParentFile @@ -1087,7 +1087,7 @@ trait ParallelTesting extends RunnerOrchestration { self => * By default, files are compiled in alphabetical order. An optional seed * can be used for randomization. */ - def compileDir(f: String, flags: TestFlags, randomOrder: Option[Int] = None)(implicit outDirectory: String): CompilationTest = { + def compileDir(f: String, flags: TestFlags, randomOrder: Option[Int] = None, outDirectory: String = defaultOutputDir): CompilationTest = { val callingMethod = getCallingMethod() val outDir = outDirectory + callingMethod + "/" val sourceDir = new JFile(f) @@ -1116,7 +1116,7 @@ trait ParallelTesting extends RunnerOrchestration { self => * `testName` since files can be in separate directories and or be otherwise * dissociated */ - def compileList(testName: String, files: List[String], flags: TestFlags, callingMethod: String = getCallingMethod())(implicit outDirectory: String): CompilationTest = { + def compileList(testName: String, files: List[String], flags: TestFlags, callingMethod: String = getCallingMethod(), outDirectory: String = defaultOutputDir): CompilationTest = { val outDir = outDirectory + callingMethod + "/" + testName + "/" // Directories in which to compile all containing files with `flags`: @@ -1147,7 +1147,7 @@ trait ParallelTesting extends RunnerOrchestration { self => * - Directories can have an associated check-file, where the check file has * the same name as the directory (with the file extension `.check`) */ - def compileFilesInDir(f: String, flags: TestFlags)(implicit outDirectory: String): CompilationTest = { + def compileFilesInDir(f: String, flags: TestFlags, outDirectory: String = defaultOutputDir): CompilationTest = { val callingMethod = getCallingMethod() val outDir = outDirectory + callingMethod + "/" val sourceDir = new JFile(f) @@ -1167,7 +1167,7 @@ trait ParallelTesting extends RunnerOrchestration { self => * sub-directories and as such, does **not** perform separate compilation * tests. */ - def compileShallowFilesInDir(f: String, flags: TestFlags)(implicit outDirectory: String): CompilationTest = { + def compileShallowFilesInDir(f: String, flags: TestFlags, outDirectory: String = defaultOutputDir): CompilationTest = { val callingMethod = getCallingMethod() val outDir = outDirectory + callingMethod + "/" val sourceDir = new JFile(f) @@ -1185,6 +1185,9 @@ trait ParallelTesting extends RunnerOrchestration { self => } object ParallelTesting { + + def defaultOutputDir: String = "../out/" + def isSourceFile(f: JFile): Boolean = { val name = f.getName name.endsWith(".scala") || name.endsWith(".java") diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index 1c94ba230a10..9ebd7a6cae91 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -3,7 +3,6 @@ package tools package vulpix object TestConfiguration { - implicit val defaultOutputDir: String = "../out/" val noCheckOptions = Array( "-pagewidth", "120", diff --git a/doc-tool/resources/_layouts/main.html b/doc-tool/resources/_layouts/main.html index 364da6fb7ade..137d517f1bbf 100644 --- a/doc-tool/resources/_layouts/main.html +++ b/doc-tool/resources/_layouts/main.html @@ -61,5 +61,12 @@ } }); + + + diff --git a/doc-tool/resources/css/dottydoc.css b/doc-tool/resources/css/dottydoc.css index 46055f35bc34..350559355141 100644 --- a/doc-tool/resources/css/dottydoc.css +++ b/doc-tool/resources/css/dottydoc.css @@ -270,3 +270,7 @@ aside.success { border-left: 3px solid #36bf1d; background-color: #ebfddd; } + +.gitter-open-chat-button { + background-color: gray; +} diff --git a/project/Build.scala b/project/Build.scala index e8b8a7e1e3b6..c01c80a82b5b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -871,7 +871,7 @@ object Build { sbtPlugin := true, - version := "0.1.5", + version := "0.1.6-SNAPSHOT", ScriptedPlugin.scriptedSettings, ScriptedPlugin.sbtTestDirectory := baseDirectory.value / "sbt-test", ScriptedPlugin.scriptedBufferLog := false, diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala index 8b7eedb0b9a6..c9f3c4e72062 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala @@ -1,6 +1,7 @@ package dotty.tools.sbtplugin import sbt._ +import sbt.Def.Initialize import sbt.Keys._ import java.io._ import java.lang.ProcessBuilder @@ -196,34 +197,38 @@ object DottyIDEPlugin extends AutoPlugin { origState } + private def projectConfigTask(config: Configuration): Initialize[Task[Option[ProjectConfig]]] = Def.task { + if ((sources in config).value.isEmpty) None + else { + // Not needed to generate the config, but this guarantees that the + // generated config is usable by an IDE without any extra compilation + // step. + val _ = (compile in config).value + + val id = s"${thisProject.value.id}/${config.name}" + val compilerVersion = (scalaVersion in config).value + val compilerArguments = (scalacOptions in config).value + val sourceDirectories = (unmanagedSourceDirectories in config).value ++ (managedSourceDirectories in config).value + val depClasspath = Attributed.data((dependencyClasspath in config).value) + val classDir = (classDirectory in config).value + + Some(new ProjectConfig( + id, + compilerVersion, + compilerArguments.toArray, + sourceDirectories.toArray, + depClasspath.toArray, + classDir + )) + } + } + override def projectSettings: Seq[Setting[_]] = Seq( - // Use Def.derive so `projectConfig` is only defined in the configurations where the - // tasks/settings it depends on are defined. - Def.derive(projectConfig := { - if (sources.value.isEmpty) None - else { - // Not needed to generate the config, but this guarantees that the - // generated config is usable by an IDE without any extra compilation - // step. - val _ = compile.value - - val id = s"${thisProject.value.id}/${configuration.value.name}" - val compilerVersion = scalaVersion.value - val compilerArguments = scalacOptions.value - val sourceDirectories = unmanagedSourceDirectories.value ++ managedSourceDirectories.value - val depClasspath = Attributed.data(dependencyClasspath.value) - val classDir = classDirectory.value - - Some(new ProjectConfig( - id, - compilerVersion, - compilerArguments.toArray, - sourceDirectories.toArray, - depClasspath.toArray, - classDir - )) - } - }) + // TODO: It would be better to use Def.derive to define projectConfig in + // every configuration where the keys it depends on exist, however this + // currently breaks aggregated tasks: https://github.com/sbt/sbt/issues/3580 + projectConfig in Compile := projectConfigTask(Compile).value, + projectConfig in Test := projectConfigTask(Test).value ) override def buildSettings: Seq[Setting[_]] = Seq( diff --git a/tests/neg/patmat.scala b/tests/neg/no-optimise/patmat.scala similarity index 100% rename from tests/neg/patmat.scala rename to tests/neg/no-optimise/patmat.scala diff --git a/tests/neg/tryPatternMatchError.scala b/tests/neg/no-optimise/tryPatternMatchError.scala similarity index 100% rename from tests/neg/tryPatternMatchError.scala rename to tests/neg/no-optimise/tryPatternMatchError.scala diff --git a/tests/run/2772.scala b/tests/run-no-optimise/2772.scala similarity index 100% rename from tests/run/2772.scala rename to tests/run-no-optimise/2772.scala