From 97ee5c0f308381826818bc07b0ad6e64f16861ff Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 29 Mar 2021 13:36:06 +0200 Subject: [PATCH 01/40] Fix #11731: Remove symbols with targetName correctly The symbol might have original name in scope other than its current target name (after erasure). --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 4 +++- tests/pos/i11731.scala | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i11731.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a7dec8281d89..f170304d20d4 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1899,7 +1899,9 @@ object SymDenotations { * someone does a findMember on a subclass. */ def delete(sym: Symbol)(using Context): Unit = { - info.decls.openForMutations.unlink(sym) + val scope = info.decls.openForMutations + scope.unlink(sym, sym.name) + if sym.name != sym.originalName then scope.unlink(sym, sym.originalName) if (myMemberCache != null) myMemberCache.remove(sym.name) if (!sym.flagsUNSAFE.is(Private)) invalidateMemberNamesCache() } diff --git a/tests/pos/i11731.scala b/tests/pos/i11731.scala new file mode 100644 index 000000000000..bef8fdef00f4 --- /dev/null +++ b/tests/pos/i11731.scala @@ -0,0 +1,5 @@ +import scala.annotation.targetName + +trait Example: + @targetName("funfun") + inline def fun: Unit = ??? From 7bed8912057b1bd32e7c6a1eb6a84a04a1398140 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 29 Mar 2021 14:10:00 +0200 Subject: [PATCH 02/40] Performance tweak for initialization check Propagate visited set properly. --- .../tools/dotc/transform/init/Checking.scala | 38 ++++++++++++------- tests/init/neg/function1.scala | 2 +- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Checking.scala b/compiler/src/dotty/tools/dotc/transform/init/Checking.scala index e47c3b08d264..cc8f95b20e8c 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Checking.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Checking.scala @@ -39,7 +39,18 @@ object Checking { safePromoted: mutable.Set[Potential], // Potentials that can be safely promoted env: Env ) { - def withOwner(sym: Symbol): State = copy(env = env.withOwner(sym)) + def withOwner[T](sym: Symbol)(op: State ?=> T): T = + val state = this.copy(env = env.withOwner(sym)) + val res = op(using state) + this.visited = state.visited + res + + + def visit[T](eff: Effect)(op: State ?=> T): T = + val state: State = this.copy(path = path :+ eff.source, visited = this.visited + eff) + val res = op(using state) + this.visited = state.visited + res def test(op: State ?=> Errors): Errors = { val savedVisited = visited @@ -58,15 +69,14 @@ object Checking { traceIndented("Already checked " + eff.show, init) Errors.empty } - else { - state.visited = state.visited + eff - val state2: State = state.copy(path = state.path :+ eff.source) - eff match { - case eff: Promote => Checking.checkPromote(eff)(using state2) - case eff: FieldAccess => Checking.checkFieldAccess(eff)(using state2) - case eff: MethodCall => Checking.checkMethodCall(eff)(using state2) + else + state.visit(eff) { + eff match { + case eff: Promote => Checking.checkPromote(eff) + case eff: FieldAccess => Checking.checkFieldAccess(eff) + case eff: MethodCall => Checking.checkMethodCall(eff) + } } - } } } @@ -118,11 +128,11 @@ object Checking { def checkConstructor(ctor: Symbol, tp: Type, source: Tree)(using state: State): Unit = traceOp("checking " + ctor.show, init) { val cls = ctor.owner val classDef = cls.defTree - if (!classDef.isEmpty) { - given State = state.withOwner(cls) - if (ctor.isPrimaryConstructor) checkClassBody(classDef.asInstanceOf[TypeDef]) - else checkSecondaryConstructor(ctor) - } + if (!classDef.isEmpty) + state.withOwner(cls) { + if (ctor.isPrimaryConstructor) checkClassBody(classDef.asInstanceOf[TypeDef]) + else checkSecondaryConstructor(ctor) + } } def checkSecondaryConstructor(ctor: Symbol)(using state: State): Unit = traceOp("checking " + ctor.show, init) { diff --git a/tests/init/neg/function1.scala b/tests/init/neg/function1.scala index 15427f3de750..e01864ae1f47 100644 --- a/tests/init/neg/function1.scala +++ b/tests/init/neg/function1.scala @@ -4,7 +4,7 @@ class Foo { val fun2: Int => Int = n => 1 + n + list.size fun2(5) - List(5, 9).map(n => 2 + n + list.size) // error + List(5, 9).map(n => 2 + n + list.size) final val list = List(1, 2, 3) // error From 5bb0e92b33cd96ac96d394068165b38e4ce5b035 Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Mon, 29 Mar 2021 15:24:32 +0200 Subject: [PATCH 03/40] Set baseVersion to 3.0.1-RC1 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 9dc136eeb3cc..02474e8a648b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -67,7 +67,7 @@ object NonBootstrappedDottyJSPlugin extends DottyJSPlugin(Build.commonNonBootstr object Build { val referenceVersion = "3.0.0-RC2-bin-20210325-ab2664f-NIGHTLY" - val baseVersion = "3.0.0-RC2" + val baseVersion = "3.0.1-RC1" val baseSbtDottyVersion = "0.5.4" // Versions used by the vscode extension to create a new project From 4246c58a63046f022384e3712e4d57d80a0239b8 Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Tue, 30 Mar 2021 11:58:12 +0200 Subject: [PATCH 04/40] Fix detecting Scala3 version in useScaladoc --- sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index ba2c072177cf..30582dc35933 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -374,7 +374,7 @@ object DottyPlugin extends AutoPlugin { resolvers ++= (if(!useScaladoc.value) Nil else Seq(Resolver.jcenterRepo)), useScaladoc := { val v = scalaVersion.value - v.startsWith("3.0.0") && !v.startsWith("3.0.0-M1") && !v.startsWith("3.0.0-M2") + v.startsWith("3") && !v.startsWith("3.0.0-M1") && !v.startsWith("3.0.0-M2") }, // We need to add doctool classes to the classpath so they can be called doc / scalaInstance := Def.taskDyn { From ca17ab60460ed85d49a8d78a3a24e53fd10accab Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 30 Mar 2021 13:27:37 +0200 Subject: [PATCH 05/40] Deprecate sbt-dotty when using sbt >= 1.5.0-RC2 --- sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index 30582dc35933..1432aaa625f4 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -181,6 +181,11 @@ object DottyPlugin extends AutoPlugin { if (!VersionNumber(sbtV).matchesSemVer(SemanticSelector(requiredVersion))) sys.error(s"The sbt-dotty plugin cannot work with this version of sbt ($sbtV), sbt $requiredVersion is required.") + val deprecatedVersion = ">=1.5.0-RC2" + val logger = sLog.value + if (VersionNumber(sbtV).matchesSemVer(SemanticSelector(deprecatedVersion))) + logger.warn(s"The sbt-dotty plugin is no longer neeeded with sbt >= 1.5, please remove it from your build.") + state } ) From a450551c0fbc0b14b05eb1098057213c097691f7 Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Mon, 29 Mar 2021 17:28:46 +0200 Subject: [PATCH 06/40] Make community build docs experiment-aware --- .../src/scala/dotty/communitybuild/Main.scala | 5 +++- .../scala/dotty/communitybuild/projects.scala | 23 +++++++++++++++---- .../communitybuild/CommunityBuildTest.scala | 13 +++++++---- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/community-build/src/scala/dotty/communitybuild/Main.scala b/community-build/src/scala/dotty/communitybuild/Main.scala index 9709c22493dd..d135de12bdb0 100644 --- a/community-build/src/scala/dotty/communitybuild/Main.scala +++ b/community-build/src/scala/dotty/communitybuild/Main.scala @@ -54,7 +54,10 @@ object Main: Seq("rm", "-rf", destStr).! Files.createDirectory(dest) val (toRun, ignored) = - allProjects.partition(_.docCommand != null) + allProjects.partition( p => + p.docCommand != null + && (!p.requiresExperimental || compilerSupportExperimental) + ) val paths = toRun.map { project => val name = project.project diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index a44b44d6085f..4ee17cb535cc 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -40,6 +40,7 @@ sealed trait CommunityProject: val dependencies: List[CommunityProject] val binaryName: String val runCommandsArgs: List[String] = Nil + val requiresExperimental: Boolean final val projectDir = communitybuildDir.resolve("community-projects").resolve(project) @@ -48,6 +49,7 @@ sealed trait CommunityProject: /** Publish this project to the local Maven repository */ final def publish(): Unit = + // TODO what should this do with .requiresExperimental? if !published then publishDependencies() log(s"Publishing $project") @@ -59,6 +61,11 @@ sealed trait CommunityProject: published = true final def doc(): Unit = + if this.requiresExperimental && !compilerSupportExperimental then + log( + s"Skipping ${this.project} - it needs experimental features unsupported in this build." + ) + return publishDependencies() log(s"Documenting $project") if docCommand eq null then @@ -78,6 +85,7 @@ final case class MillCommunityProject( baseCommand: String, dependencies: List[CommunityProject] = Nil, ignoreDocs: Boolean = false, + requiresExperimental: Boolean = false, ) extends CommunityProject: override val binaryName: String = "./mill" override val testCommand = s"$baseCommand.test" @@ -94,7 +102,8 @@ final case class SbtCommunityProject( dependencies: List[CommunityProject] = Nil, sbtPublishCommand: String = null, sbtDocCommand: String = null, - scalacOptions: List[String] = SbtCommunityProject.scalacOptions + scalacOptions: List[String] = SbtCommunityProject.scalacOptions, + requiresExperimental: Boolean = false, ) extends CommunityProject: override val binaryName: String = "sbt" @@ -237,13 +246,15 @@ object projects: lazy val scas = MillCommunityProject( project = "scas", - baseCommand = "scas.application" + baseCommand = "scas.application", + requiresExperimental = true ) lazy val intent = SbtCommunityProject( project = "intent", sbtTestCommand = "test", - sbtDocCommand = "doc" + sbtDocCommand = "doc", + requiresExperimental = true, ) lazy val algebra = SbtCommunityProject( @@ -398,7 +409,8 @@ object projects: sbtTestCommand = "coreJVM/test;coreJS/test", sbtPublishCommand = "coreJVM/publishLocal;coreJS/publishLocal", sbtDocCommand = "coreJVM/doc", - dependencies = List(munit) + dependencies = List(munit), + requiresExperimental = true, ) lazy val scodec = SbtCommunityProject( @@ -406,7 +418,8 @@ object projects: sbtTestCommand = "unitTests/test", // Adds package sbtDocCommand = "coreJVM/doc", - dependencies = List(munit, scodecBits) + dependencies = List(munit, scodecBits), + requiresExperimental = true, ) lazy val scalaParserCombinators = SbtCommunityProject( diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 82f635ffa508..8b169385ddf1 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -20,7 +20,12 @@ abstract class CommunityBuildTest: * and avoid network overhead. See https://github.com/lampepfl/dotty-drone * for more infrastructural details. */ - extension (self: CommunityProject) def run()(using suite: CommunityBuildTest) = + extension (self: CommunityProject) def run()(using suite: CommunityBuildTest): Unit = + if self.requiresExperimental && !compilerSupportExperimental then + println( + s"Skipping ${self.project} - it needs experimental features unsupported in this build." + ) + return self.dependencies.foreach(_.publish()) suite.test(self) @@ -116,8 +121,8 @@ class CommunityBuildTestB extends CommunityBuildTest: @Test def disciplineSpecs2 = projects.disciplineSpecs2.run() @Test def munit = projects.munit.run() @Test def perspective = projects.perspective.run() - @Test def scodec = if compilerSupportExperimental then projects.scodec.run() - @Test def scodecBits = if compilerSupportExperimental then projects.scodecBits.run() + @Test def scodec = projects.scodec.run() + @Test def scodecBits = projects.scodecBits.run() @Test def simulacrumScalafixAnnotations = projects.simulacrumScalafixAnnotations.run() end CommunityBuildTestB @@ -134,7 +139,7 @@ class CommunityBuildTestC extends CommunityBuildTest: @Test def fansi = projects.fansi.run() @Test def fastparse = projects.fastparse.run() @Test def geny = projects.geny.run() - @Test def intent = if compilerSupportExperimental then projects.intent.run() + @Test def intent = projects.intent.run() @Test def minitest = projects.minitest.run() @Test def onnxScala = projects.onnxScala.run() @Test def oslib = projects.oslib.run() From 08b9b6d3d6225984e28916766c7c7104cbf2c80b Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 30 Mar 2021 10:04:33 +0200 Subject: [PATCH 07/40] Fix #11885: disable flaky idempotency test on Windows --- tests/idempotency/IdempotencyCheck.scala | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/idempotency/IdempotencyCheck.scala b/tests/idempotency/IdempotencyCheck.scala index 6f9a8de588de..546b13487ff6 100644 --- a/tests/idempotency/IdempotencyCheck.scala +++ b/tests/idempotency/IdempotencyCheck.scala @@ -6,13 +6,12 @@ import java.util.stream.Stream as JStream import scala.collection.JavaConverters.* object IdempotencyCheck { - val blacklisted = Set( - // No fix needed. Bridges on collections in different order. Second one in scala2 order. - s"pos{JFile.separator}Map{JFile.separator}scala{JFile.separator}collection{JFile.separator}immutable/Map", - s"pos{JFile.separator}Map{JFile.separator}scala{JFile.separator}collection{JFile.separator}immutable{JFile.separator}AbstractMap", - s"pos{JFile.separator}t1203a/NodeSeq", - s"pos{JFile.separator}i2345{JFile.separator}Whatever" - ) + val flakyTestsOnWindows = + if scala.util.Properties.isWin + then Set(s"pos${JFile.separator}i6507b") + else Set.empty + + val blacklisted = flakyTestsOnWindows def checkIdempotency(dir1: String, dir2: String): Unit = { var failed = 0 From e1aeeca649314c6406e50101a14e901230f7f438 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 30 Mar 2021 19:09:20 +0200 Subject: [PATCH 08/40] sbt-dotty: Bump to 0.5.5-SNAPSHOT --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 02474e8a648b..8fa40d3e049c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -68,14 +68,14 @@ object Build { val referenceVersion = "3.0.0-RC2-bin-20210325-ab2664f-NIGHTLY" val baseVersion = "3.0.1-RC1" - val baseSbtDottyVersion = "0.5.4" + val baseSbtDottyVersion = "0.5.5" // Versions used by the vscode extension to create a new project // This should be the latest published releases. // TODO: Have the vscode extension fetch these numbers from the Internet // instead of hardcoding them ? val publishedDottyVersion = referenceVersion - val publishedSbtDottyVersion = "0.5.3" + val publishedSbtDottyVersion = "0.5.4" /** scala-library version required to compile Dotty. * From b80c3b799f8372fac987ec6cb7babd1bcf55a46a Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 30 Mar 2021 18:41:27 +0200 Subject: [PATCH 09/40] Fix typo in idempotency test filter --- tests/idempotency/IdempotencyCheck.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/idempotency/IdempotencyCheck.scala b/tests/idempotency/IdempotencyCheck.scala index 546b13487ff6..79f236d3d24a 100644 --- a/tests/idempotency/IdempotencyCheck.scala +++ b/tests/idempotency/IdempotencyCheck.scala @@ -25,7 +25,7 @@ object IdempotencyCheck { val bytecodeFiles = { def bytecodeFiles(paths: JStream[JPath], dir: String): List[(String, JPath)] = { def isBytecode(file: String) = file.endsWith(".class") || file.endsWith(".tasty") - def tupleWithName(f: JPath) = (f.toString.substring(dir.length + 1, f.toString.length - 6), f) + def tupleWithName(f: JPath) = (f.toString.substring(dir.length, f.toString.length - 6), f) paths.iterator.asScala.filter(path => isBytecode(path.toString)).map(tupleWithName).toList } bytecodeFiles(JFiles.walk(dir1Path), dir1String) ++ bytecodeFiles(JFiles.walk(dir2Path), dir2String) From 9c94edcb39f13553add36955ec4a86a8ea906234 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 30 Mar 2021 19:49:30 +0200 Subject: [PATCH 10/40] Refactor filter in idempotency test --- compiler/test/dotty/tools/dotc/IdempotencyTests.scala | 8 ++++++-- compiler/test/dotty/tools/vulpix/FileFilter.scala | 4 ++++ tests/idempotency/IdempotencyCheck.scala | 9 +-------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/IdempotencyTests.scala b/compiler/test/dotty/tools/dotc/IdempotencyTests.scala index 30dbb47475ff..980cfee4634a 100644 --- a/compiler/test/dotty/tools/dotc/IdempotencyTests.scala +++ b/compiler/test/dotty/tools/dotc/IdempotencyTests.scala @@ -18,14 +18,18 @@ class IdempotencyTests { import IdempotencyTests._ import CompilationTest.aggregateTests + val filter = FileFilter.exclude( + s"pos${JFile.separator}i6507b" + ) + @Category(Array(classOf[SlowTests])) @Test def idempotency: Unit = { implicit val testGroup: TestGroup = TestGroup("idempotency") val opt = defaultOptions val posIdempotency = aggregateTests( - compileFilesInDir("tests/pos", opt)(TestGroup("idempotency/posIdempotency1")), - compileFilesInDir("tests/pos", opt)(TestGroup("idempotency/posIdempotency2")), + compileFilesInDir("tests/pos", opt, filter)(TestGroup("idempotency/posIdempotency1")), + compileFilesInDir("tests/pos", opt, filter)(TestGroup("idempotency/posIdempotency2")), ) val orderIdempotency = { diff --git a/compiler/test/dotty/tools/vulpix/FileFilter.scala b/compiler/test/dotty/tools/vulpix/FileFilter.scala index 75c17089a4e1..b2aef8af038e 100644 --- a/compiler/test/dotty/tools/vulpix/FileFilter.scala +++ b/compiler/test/dotty/tools/vulpix/FileFilter.scala @@ -5,6 +5,10 @@ sealed trait FileFilter { } object FileFilter { + def exclude(file: String): FileFilter = exclude(file :: Nil) + + def exclude(file: String, files: String*): FileFilter = + exclude(file :: files.toList) def exclude(files: List[String]): FileFilter = new FileFilter { private val blackList = files.toSet diff --git a/tests/idempotency/IdempotencyCheck.scala b/tests/idempotency/IdempotencyCheck.scala index 79f236d3d24a..598f68a8b347 100644 --- a/tests/idempotency/IdempotencyCheck.scala +++ b/tests/idempotency/IdempotencyCheck.scala @@ -6,13 +6,6 @@ import java.util.stream.Stream as JStream import scala.collection.JavaConverters.* object IdempotencyCheck { - val flakyTestsOnWindows = - if scala.util.Properties.isWin - then Set(s"pos${JFile.separator}i6507b") - else Set.empty - - val blacklisted = flakyTestsOnWindows - def checkIdempotency(dir1: String, dir2: String): Unit = { var failed = 0 var total = 0 @@ -32,7 +25,7 @@ object IdempotencyCheck { } val groups = bytecodeFiles.groupBy(_._1).mapValues(_.map(_._2)) - groups.filterNot(x => blacklisted(x._1)).iterator.flatMap { g => + groups.iterator.flatMap { g => def pred(f: JPath, dir: String, isTasty: Boolean) = f.toString.contains(dir) && f.toString.endsWith(if (isTasty) ".tasty" else ".class") val class1 = g._2.find(f => pred(f, dir1String, isTasty = false)) From f0d22a2350560a119e53ea2469bb1711d222a39b Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Tue, 30 Mar 2021 21:09:04 +0200 Subject: [PATCH 11/40] Upgrade Dotty to 3.0.0-RC2 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 8fa40d3e049c..85d0e0458a3b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -65,7 +65,7 @@ object BootstrappedDottyJSPlugin extends DottyJSPlugin(Build.commonBootstrappedS object NonBootstrappedDottyJSPlugin extends DottyJSPlugin(Build.commonNonBootstrappedSettings) object Build { - val referenceVersion = "3.0.0-RC2-bin-20210325-ab2664f-NIGHTLY" + val referenceVersion = "3.0.0-RC2" val baseVersion = "3.0.1-RC1" val baseSbtDottyVersion = "0.5.5" From ea7abac6ebe1b53706cff005c6060bd2da8a5e8b Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 31 Mar 2021 10:03:29 +0200 Subject: [PATCH 12/40] Fix broken script on Windows --- dist/bin/common | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/bin/common b/dist/bin/common index a3847b2b1dbd..fd7b8be9149e 100755 --- a/dist/bin/common +++ b/dist/bin/common @@ -7,7 +7,7 @@ # save terminal settings saved_stty=$(stty -g 2>/dev/null) # clear on error so we don't later try to restore them -if [[ ! $? ]]; then +if [[ ! $? ]]; then saved_stty="" fi @@ -135,7 +135,7 @@ fi find_lib () { local lib=$(find $PROG_HOME/lib/ -name "$1") if [ -n "$CYGPATHCMD" ]; then - $CYGPATHCMD -am $lib + "$CYGPATHCMD" -am $lib elif [[ $mingw || $msys ]]; then echo $lib | sed 's|/|\\\\|g' else From 71e101805b1025c1489da7b077efa8e203961ae3 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 31 Mar 2021 10:44:44 +0200 Subject: [PATCH 13/40] Test scripts on Windows with Cygwin and MSys --- .github/workflows/ci.yaml | 12 ++++++++++++ project/scripts/bootstrapCmdTests | 3 +++ project/scripts/options | 2 ++ project/scripts/winCmdTests | 10 ++++++++++ 4 files changed, 27 insertions(+) create mode 100644 project/scripts/options create mode 100644 project/scripts/winCmdTests diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 401de0eb51aa..3d401a57711a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -149,6 +149,18 @@ jobs: run: sbt ";scala3-bootstrapped/compile" shell: cmd + - name: build binary + run: sbt "dist/pack" & bash -version + shell: cmd + + - name: cygwin tests + run: bash ./project/scripts/winCmdTests + shell: cmd + + # - name: msys tests + # run: 'C:\Program Files\Git\bin\bash' ./project/scripts/winCmdTests + # shell: cmd + - name: Scala.js Test run: sbt ";sjsJUnitTests/test ;sjsCompilerTests/test" shell: cmd diff --git a/project/scripts/bootstrapCmdTests b/project/scripts/bootstrapCmdTests index 7683c53f9e82..31b8368d5215 100755 --- a/project/scripts/bootstrapCmdTests +++ b/project/scripts/bootstrapCmdTests @@ -64,3 +64,6 @@ echo "testing i11644" cwd=$(pwd) clear_out "$OUT" (cd "$OUT" && "$cwd/bin/scalac" "$cwd/tests/pos/i11644.scala" && "$cwd/bin/scalac" "$cwd/tests/pos/i11644.scala") + +# check options specified in files +./bin/scalac @project/scripts/options "$SOURCE" diff --git a/project/scripts/options b/project/scripts/options new file mode 100644 index 000000000000..86d2c8fd8edb --- /dev/null +++ b/project/scripts/options @@ -0,0 +1,2 @@ +-Xprint:frontend -Ylog:frontend +-Ycheck:all diff --git a/project/scripts/winCmdTests b/project/scripts/winCmdTests new file mode 100644 index 000000000000..4298203ddfed --- /dev/null +++ b/project/scripts/winCmdTests @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e +PREFIX="dist/target/pack" +SOURCE="tests/pos/HelloWorld.scala" +$PREFIX/bin/scalac @project/scripts/options "$SOURCE" +$PREFIX/bin/scalac -d out "$SOURCE" +$PREFIX/bin/scala -classpath out HelloWorld +$PREFIX/bin/scala -classpath out -J-Xmx512m HelloWorld +mkdir -p _site && $PREFIX/bin/scaladoc -siteroot _site -project Hello "$SOURCE" From 9a22d775a7a17c8c1dca5c926dcaad62dbaa7f97 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 31 Mar 2021 11:52:19 +0200 Subject: [PATCH 14/40] Enable msys test --- .github/workflows/ci.yaml | 8 ++++---- project/scripts/winCmdTests | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3d401a57711a..eb333c59d8c5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -154,12 +154,12 @@ jobs: shell: cmd - name: cygwin tests - run: bash ./project/scripts/winCmdTests + run: '"C:\Program Files\cygwin64\bin\bash" ./project/scripts/winCmdTests' shell: cmd - # - name: msys tests - # run: 'C:\Program Files\Git\bin\bash' ./project/scripts/winCmdTests - # shell: cmd + - name: msys tests + run: '"C:\Program Files\Git\bin\bash" ./project/scripts/winCmdTests' + shell: cmd - name: Scala.js Test run: sbt ";sjsJUnitTests/test ;sjsCompilerTests/test" diff --git a/project/scripts/winCmdTests b/project/scripts/winCmdTests index 4298203ddfed..d287b60992b2 100644 --- a/project/scripts/winCmdTests +++ b/project/scripts/winCmdTests @@ -7,4 +7,4 @@ $PREFIX/bin/scalac @project/scripts/options "$SOURCE" $PREFIX/bin/scalac -d out "$SOURCE" $PREFIX/bin/scala -classpath out HelloWorld $PREFIX/bin/scala -classpath out -J-Xmx512m HelloWorld -mkdir -p _site && $PREFIX/bin/scaladoc -siteroot _site -project Hello "$SOURCE" +mkdir -p _site && $PREFIX/bin/scaladoc -d _site -project Hello "$SOURCE" From fea5ccf6897fabe8e5e9b7ec1560c020a93564fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 31 Mar 2021 13:21:18 +0200 Subject: [PATCH 15/40] Fix derivedNames test on mac --- tests/generic-java-signatures/derivedNames.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/generic-java-signatures/derivedNames.scala b/tests/generic-java-signatures/derivedNames.scala index 0608bc54142b..f42c6ebab6a2 100644 --- a/tests/generic-java-signatures/derivedNames.scala +++ b/tests/generic-java-signatures/derivedNames.scala @@ -8,6 +8,8 @@ object Test { val out2 = "Test$Foo$A$B$>" // Linux and sometimes Windows if (scala.util.Properties.isWin) assert(returnType.toString == out1 || returnType.toString == out2) + else if (scala.util.Properties.isMac) + assert(returnType.toString == out1) else assert(returnType.toString == out2) } From 660902295fe259f2d620496a2e19f6a8ad882b12 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 31 Mar 2021 12:58:56 +0200 Subject: [PATCH 16/40] Better handling of leading infix operators in indented code ```code @main def Test = val x = false val y = 1 val result = x || y.match case 1 => false case 3 => false case _ => true || !x assert(result) ``` In this code, the last `|| !x` was seen as a part of the previous case, so the code was parsed as ```scala @main def Test = val x = false val y = 1 val result = x || y.match case 1 => false case 3 => false case _ => true || !x assert(result) ``` This is highly surprising and unintuitive. The fix will insert an token instead if the leading infix operator is too far to the left. Too far means: (1) left of the current indentation region, (2) and not to the right of any outer indentation widths. (2) allows to still parse code like this ```scala if xyz then one + two + three ``` Here, the width of the indentation region after `then` is 4, but the `+` operator is to the right of the outer indentation width of 0., so the indentation region is not closed. In other words, we do not close an indentation region if the result would not be legal, since it matches none of the previous indentation widths. --- .../dotty/tools/dotc/parsing/Scanners.scala | 22 ++++++++++++++---- .../other-new-features/indentation.md | 23 ++++++++++++++++++- tests/run/indent.scala | 12 ++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 tests/run/indent.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 1412b5a04f47..15b3f6e63134 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -369,7 +369,7 @@ object Scanners { * token that can start an expression. * If a leading infix operator is found and the source version is `3.0-migration`, emit a change warning. */ - def isLeadingInfixOperator(inConditional: Boolean = true) = + def isLeadingInfixOperator(nextWidth: IndentWidth = indentWidth(offset), inConditional: Boolean = true) = allowLeadingInfixOperators && isOperator && (isWhitespace(ch) || ch == LF) @@ -397,6 +397,20 @@ object Scanners { assumeStartsExpr(lookahead) || lookahead.token == NEWLINE && assumeStartsExpr(lookahead.next) } + && { + currentRegion match + case r: Indented => + r.width <= nextWidth + || { + r.outer match + case null => true + case Indented(outerWidth, others, _, _) => + outerWidth < nextWidth && !others.contains(nextWidth) + case outer => + outer.indentWidth < nextWidth + } + case _ => true + } && { if migrateTo3 then val (what, previous) = @@ -512,7 +526,7 @@ object Scanners { if newlineIsSeparating && canEndStatTokens.contains(lastToken) && canStartStatTokens.contains(token) - && !isLeadingInfixOperator() + && !isLeadingInfixOperator(nextWidth) && !(lastWidth < nextWidth && isContinuing(lastToken)) then insert(if (pastBlankLine) NEWLINES else NEWLINE, lineOffset) @@ -521,7 +535,7 @@ object Scanners { || nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE then if currentRegion.isOutermost then if nextWidth < lastWidth then currentRegion = topLevelRegion(nextWidth) - else if !isLeadingInfixOperator() && !statCtdTokens.contains(lastToken) then + else if !isLeadingInfixOperator(nextWidth) && !statCtdTokens.contains(lastToken) then currentRegion match case r: Indented => currentRegion = r.enclosing @@ -1105,7 +1119,7 @@ object Scanners { putChar(ch) nextRawChar() getStringPart(multiLine) - } + } else if (ch == '$') { nextRawChar() if (ch == '$' || ch == '"') { diff --git a/docs/docs/reference/other-new-features/indentation.md b/docs/docs/reference/other-new-features/indentation.md index 7d28c821cbf3..4b1b78a9bf4e 100644 --- a/docs/docs/reference/other-new-features/indentation.md +++ b/docs/docs/reference/other-new-features/indentation.md @@ -80,8 +80,11 @@ There are two rules: ``` then else do catch finally yield match ``` - - the first token on the next line is not a + - if the first token on the next line is a [leading infix operator](../changed-features/operators.md). + then its indentation width is less then the current indentation width, + and it either matches a previous indentation width or is also less + than the enclosing indentation width. If an `` is inserted, the top element is popped from `IW`. If the indentation width of the token on the next line is still less than the new current indentation width, step (2) repeats. Therefore, several `` tokens @@ -105,6 +108,24 @@ if x < 0 then Indentation tokens are only inserted in regions where newline statement separators are also inferred: at the top-level, inside braces `{...}`, but not inside parentheses `(...)`, patterns or types. +**Note:** The rules for leading infix operators above are there to make sure that +```scala + one + + two.match + case 1 => b + case 2 => c + + three +``` +is parsed as `one + (two.match ...) + three`. Also, that +```scala +if x then + a + + b + + c +else d +``` +is parsed as `if x then a + b + c else d`. + ### Optional Braces Around Template Bodies The Scala grammar uses the term _template body_ for the definitions of a class, trait, or object that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule. diff --git a/tests/run/indent.scala b/tests/run/indent.scala new file mode 100644 index 000000000000..24cb22a5c354 --- /dev/null +++ b/tests/run/indent.scala @@ -0,0 +1,12 @@ +@main def Test = + val x = false + val y = 1 + val result = + x + || y.match + case 1 => false + case 3 => false + case _ => true + || !x + assert(result) + From a6237138ce50cf98ab213b0097ef61670b0acbe1 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 26 Nov 2020 16:43:18 +0100 Subject: [PATCH 17/40] Add test case --- tests/pos/9871.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/pos/9871.scala diff --git a/tests/pos/9871.scala b/tests/pos/9871.scala new file mode 100644 index 000000000000..e5e2a9aded4c --- /dev/null +++ b/tests/pos/9871.scala @@ -0,0 +1,10 @@ +object Test { + type IsTypeInTuple[T, Tup <: Tuple] = Tup match { + case EmptyTuple => false + case T *: ts => true + case _ *: ts => IsTypeInTuple[T, ts] + } + summon[(Int *: String *: EmptyTuple) =:= (Int, String)] //they are the same + summon[IsTypeInTuple[String, Int *: String *: EmptyTuple] =:= true] //compiles + summon[IsTypeInTuple[String, (Int, String)] =:= true] //doesn't compile +} From edbb81575e81566fd8cb9b9663b4d64a3c8bb6cc Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 31 Mar 2021 08:24:25 +0200 Subject: [PATCH 18/40] Fix #9871: use toNestedPairs in provablyDisjoint --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index e04f3f2c6523..62b06aea39a7 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2449,8 +2449,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling decompose(cls2, tp2).forall(x => provablyDisjoint(x, tp1)) else false - case (AppliedType(tycon1, args1), AppliedType(tycon2, args2)) - if tycon1.typeSymbol == tycon2.typeSymbol && tycon1 =:= tycon2 => + case (AppliedType(tycon1, args1), AppliedType(tycon2, args2)) if isSame(tycon1, tycon2) => // It is possible to conclude that two types applies are disjoint by // looking at covariant type parameters if the said type parameters // are disjoin and correspond to fields. @@ -2520,6 +2519,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling provablyDisjoint(tp1, gadtBounds(tp2.symbol).hi) || provablyDisjoint(tp1, tp2.superType) case (tp1: TermRef, tp2: TermRef) if isEnumValueOrModule(tp1) && isEnumValueOrModule(tp2) => tp1.termSymbol != tp2.termSymbol + case (tp1: Type, tp2: Type) if defn.isTupleType(tp1) => + provablyDisjoint(tp1.toNestedPairs, tp2) + case (tp1: Type, tp2: Type) if defn.isTupleType(tp2) => + provablyDisjoint(tp1, tp2.toNestedPairs) case (tp1: TypeProxy, tp2: TypeProxy) => provablyDisjoint(tp1.superType, tp2) || provablyDisjoint(tp1, tp2.superType) case (tp1: TypeProxy, _) => From 3406b3a13de411cb5710f229bf478afdc15950d7 Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Mon, 29 Mar 2021 17:28:19 +0200 Subject: [PATCH 19/40] Add 3.0.0-RC2 blog article --- docs/blog/_posts/2021-03-31-scala3-rc2.md | 137 ++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 docs/blog/_posts/2021-03-31-scala3-rc2.md diff --git a/docs/blog/_posts/2021-03-31-scala3-rc2.md b/docs/blog/_posts/2021-03-31-scala3-rc2.md new file mode 100644 index 000000000000..a8c49e10a419 --- /dev/null +++ b/docs/blog/_posts/2021-03-31-scala3-rc2.md @@ -0,0 +1,137 @@ +--- +layout: blog-page +title: Scala 3.0.0-RC2 – getting ready for 3.0.0 +author: Anatolii Kmetiuk +authorImg: /images/anatolii.png +date: 2020-03-31 +--- + +Hello! We are happy to announce Scala 3.0.0-RC2. With this release, we are getting ready for 3.0.0. The significance of it is to give the community the chance and time to test all the changes meant for 3.0.0 final. A lot of bug fixes found their way into this release to ensure stability for 3.0.0 – more than [250 PRs](https://github.com/lampepfl/dotty/pulls?q=is%3Apr+is%3Aclosed+closed%3A%3E2021-02-16) were merged after the 3.0.0-RC1 release and until today! + +Read more about this release below. + + +# Significance and further steps +The 3.0.0-RC releases are release candidates for the 3.0.0 final. This means we aim to pack all the features meant for 3.0.0 into these releases and ensure their stability. After a grace period during which the community tests the latest release candidate, if no major problems are discovered, that release candidate becomes 3.0.0 stable. + +3.0.0-RC2 is the next iteration of this release model. Its primary objective is to get tested by the community and generate feedback. If no major issues are discovered, we are planning to release 3.0.0 stable in 4 weeks from now. If there are indeed major issues discovered with RC2, we will have to release RC3 in between – possibly earlier than 4 weeks from now to speed up the way to 3.0.0. + +Only critical issue fixes will find their way into 3.0.0 from now on, and any such fix will mean RC3. In the meanwhile, all the non-critical commits will appear only in 3.0.1. The way it will work is that we have a separate branch for 3.0.0, and we are going to backport the critical issue fixes into that branch from the main, 3.0.1, branch. + +Post-3.0.0, we are planning to proceed with the release cycle we have had before 3.0.0. That is, we bump the stable patch version every 6 weeks and make an RC for that version. Simultaneously we make the RC of the previous patch version into a stable version. + +Concretely, this means in 6 weeks from now we'll have 3.0.1-RC1, and in 12 weeks, we'll promote 3.0.1-RC1 into 3.0.1 stable and release 3.0.2-RC1. + +# Changes for 3.0.0-RC2 +As mentioned above, we are currently in an issue-fixing mode. So a lot of those 200+ PRs for this release were about issue fixing. + +There are some notable changes worth mentioning. + +## Restrict experimental features to unstable releases only +PR [#11920](https://github.com/lampepfl/dotty/pull/11920) restricts usage of experimental features only to nightlies and snapshots. This change ensures that changes deemed experimental will not propagate into the wider ecosystem provided that the wider ecosystem depends on stable releases. This is needed so that if an experimental feature is modified or removed from the language, the ecosystem will not be impacted. + +## New `unsafeNulls` language feature +PR [#9884](https://github.com/lampepfl/dotty/pull/9884) adds a new language feature which enables unsafe null operations under explicit nulls. This is a tool to help projects migrating to full explicit nulls gradually. From now on, you can use an import `import scala.language.unsafeNulls` to create an unsafe scope. For discussion, see the PR linked above, and for more information on the feature, see the [documentation](https://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html). + +## Treat Scala.js pseudo-unions as real unions +In PR [#11671](https://github.com/lampepfl/dotty/pull/11671), we now treat the `scala.scalajs.js.|[A, B]` as if it was a real Scala 3 union `A | B`, which further boosts the support for Scala.js in Scala 3. + +## Other API changes +`-Ycheck-init` was renamed to `-Ysafe-init`. This flag is used to check safe initialization, more about which you can read in the [documentation](https://dotty.epfl.ch/docs/reference/other-new-features/safe-initialization.html). See also PR [#11920](https://github.com/lampepfl/dotty/pull/11920). + +PR [#11745](https://github.com/lampepfl/dotty/pull/11745) changes the `compiletime` package API a bit. `compiletime.S` was moved to `compiletime.ops.int.S` and the package object `compiletime` was removed in favor of top-level definitions. + +## Metaprogramming +The following are some notable metaprogramming changes included into this release: + +- Add quotes.Type.valueOfConstant [#11715](https://github.com/lampepfl/dotty/pull/11715) +- Remove compiletime.Widen [#11569](https://github.com/lampepfl/dotty/pull/11569) +- Add -Xcheck-macros scalac option [#11655](https://github.com/lampepfl/dotty/pull/11655) + + +# Let us know what you think! +If you have questions or any sort of feedback, feel free to send us a message on our +[Gitter channel](https://gitter.im/lampepfl/dotty). If you encounter a bug, please +[open an issue on GitHub](https://github.com/lampepfl/dotty/issues/new). + + +## Contributors +Thank you to all the contributors who made this release possible πŸŽ‰ + +According to `git shortlog -sn --no-merges 3.0.0-RC1..3.0.0-RC2` these are: + +``` + 150 Martin Odersky + 83 Liu Fengyun + 44 Nicolas Stucki + 40 Guillaume Martres + 17 Tom Grigg + 14 PaweΕ‚ Marks + 12 Andrzej Ratajczak + 11 Aleksander Boruch-Gruszecki + 11 Yichen Xu + 10 Phil + 10 Filip ZybaΕ‚a + 10 bjornregnell + 10 MichaΕ‚ PaΕ‚ka + 9 Jamie Thompson + 9 Raphael Jolly + 8 Krzysztof Romanowski + 7 Lan, Jian + 6 SΓ©bastien Doeraene + 6 Adrien Piquerez + 5 Olivier Blanvillain + 5 Alex Merritt + 4 Lukas Rytz + 4 Anatolii Kmetiuk + 3 StΓ©phane Micheloud + 3 Ruslan Shevchenko + 3 Jakob Odersky + 3 Eric K Richardson + 2 Som Snytt + 2 xhudik + 2 Ayush + 2 noti0na1 + 1 Jonathan BrachthΓ€user + 1 Ivano Pagano + 1 Hanns Holger Rutz + 1 Mathias + 1 Guillaume Raffin + 1 Miles Sabin + 1 Fengyun Liu + 1 Ben Hutchison + 1 Roberto Bonvallet + 1 Alexandre Archambault + 1 Jakub KozΕ‚owski +``` + +If you want to get your hands dirty and contribute to Dotty, now is a good time to get involved! +Head to our [Getting Started page for new contributors](https://dotty.epfl.ch/docs/contributing/getting-started.html), +and have a look at some of the [good first issues](https://github.com/lampepfl/dotty/issues?q=is%3Aissue+is%3Aopen+label%3Aexp%3Anovice). +They make perfect entry points into hacking on the compiler. + +We are looking forward to having you join the team of contributors. + +## Library authors: Join our community build + +Dotty now has a set of widely-used community libraries that are built against every nightly Dotty +snapshot. Currently, this includes shapeless, ScalaPB, algebra, scalatest, scopt and squants. +Join our [community build](https://github.com/lampepfl/dotty/tree/master/community-build) +to make sure that our regression suite includes your library. + +[Scastie]: https://scastie.scala-lang.org/?target=dotty + +[@odersky]: https://github.com/odersky +[@DarkDimius]: https://github.com/DarkDimius +[@smarter]: https://github.com/smarter +[@felixmulder]: https://github.com/felixmulder +[@nicolasstucki]: https://github.com/nicolasstucki +[@liufengyun]: https://github.com/liufengyun +[@OlivierBlanvillain]: https://github.com/OlivierBlanvillain +[@biboudis]: https://github.com/biboudis +[@allanrenucci]: https://github.com/allanrenucci +[@Blaisorblade]: https://github.com/Blaisorblade +[@Duhemm]: https://github.com/Duhemm +[@AleksanderBG]: https://github.com/abgruszecki +[@milessabin]: https://github.com/milessabin +[@anatoliykmetyuk]: https://github.com/anatoliykmetyuk From acdbb47f8024e9bcbbe6e39bee3e77028db60686 Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Thu, 1 Apr 2021 11:00:00 +0200 Subject: [PATCH 20/40] RC2 post date fix --- docs/blog/_posts/2021-03-31-scala3-rc2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blog/_posts/2021-03-31-scala3-rc2.md b/docs/blog/_posts/2021-03-31-scala3-rc2.md index a8c49e10a419..8e4439037be5 100644 --- a/docs/blog/_posts/2021-03-31-scala3-rc2.md +++ b/docs/blog/_posts/2021-03-31-scala3-rc2.md @@ -3,7 +3,7 @@ layout: blog-page title: Scala 3.0.0-RC2 – getting ready for 3.0.0 author: Anatolii Kmetiuk authorImg: /images/anatolii.png -date: 2020-03-31 +date: 2021-03-31 --- Hello! We are happy to announce Scala 3.0.0-RC2. With this release, we are getting ready for 3.0.0. The significance of it is to give the community the chance and time to test all the changes meant for 3.0.0 final. A lot of bug fixes found their way into this release to ensure stability for 3.0.0 – more than [250 PRs](https://github.com/lampepfl/dotty/pulls?q=is%3Apr+is%3Aclosed+closed%3A%3E2021-02-16) were merged after the 3.0.0-RC1 release and until today! From beb5aa5a7f94dd41503d3d45c45c4f0b52e8ec14 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 1 Apr 2021 13:44:25 +0200 Subject: [PATCH 21/40] Fix sourcelinks to scala/scala in scaladoc --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 85d0e0458a3b..6b98804180ee 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1632,7 +1632,7 @@ object Build { val scala3version = stdlibVersion(Bootstrapped) // TODO add versions etc. val srcManaged = s"out/bootstrap/stdlib-bootstrapped/scala-$baseVersion/src_managed/main/scala-library-src" - val sourceLinks = s"-source-links:$srcManaged=github://scala/scala/v${stdlibVersion(Bootstrapped)}}#src/library" + val sourceLinks = s"-source-links:$srcManaged=github://scala/scala/v${stdlibVersion(Bootstrapped)}#src/library" val revision = Seq("-revision", ref, "-project-version", projectVersion) val cmd = Seq("-d", outDir, "-project", name, sourceLinks) ++ scalacOptionsDocSettings ++ revision ++ params ++ targets import _root_.scala.sys.process._ From 2f3eb3c5f2176a483f551e1e182a96423e559da3 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Thu, 1 Apr 2021 12:55:00 +0200 Subject: [PATCH 22/40] Fix bug with doubling comments in Scaladoc --- .../scaladoc/renderers/MemberRenderer.scala | 33 ++++++++++--------- .../dotty/tools/scaladoc/BaseHtmlTest.scala | 4 +-- .../tasty/comments/IntegrationTest.scala | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index e8e88c3f6ef9..7b7627afc71f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -92,24 +92,26 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext Seq(dt("Deprecated"), dd(content:_*)) } - def memberInfo(m: Member): Seq[AppliedTag] = + def memberInfo(m: Member, withBrief: Boolean = false): Seq[AppliedTag] = val comment = m.docs val bodyContents = m.docs.fold(Nil)(e => renderDocPart(e.body) :: Nil) Seq( - div(cls := "documentableBrief doc")(comment.flatMap(_.short).fold("")(renderDocPart)), - div(cls := "cover")( - div(cls := "doc")(bodyContents), - dl(cls := "attributes")( - docAttributes(m), - companion(m), - deprecation(m), - defintionClasses(m), - inheritedFrom(m), - source(m), + Option.when(withBrief)(div(cls := "documentableBrief doc")(comment.flatMap(_.short).fold("")(renderDocPart))), + Some( + div(cls := "cover")( + div(cls := "doc")(bodyContents), + dl(cls := "attributes")( + docAttributes(m), + companion(m), + deprecation(m), + defintionClasses(m), + inheritedFrom(m), + source(m), + ) ) ) - ) + ).flatten private def originInfo(m: Member): Seq[TagArg] = m.origin match { case Origin.ImplicitlyAddedBy(name, dri) => @@ -170,7 +172,7 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext span(cls := "modifiers"), // just to have padding on left div( div(cls := "originInfo")(originInfo(member):_*), - div(cls := "memberDocumentation")(memberInfo(member)), + div(cls := "memberDocumentation")(memberInfo(member, withBrief = true)), ) ) ) @@ -382,7 +384,8 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext div( intro, - memberInfo(m), + memberInfo(m, withBrief = false), classLikeParts(m), buildDocumentableFilter, // TODO Need to make it work in JS :( - buildMembers(m)) + buildMembers(m) + ) diff --git a/scaladoc/test/dotty/tools/scaladoc/BaseHtmlTest.scala b/scaladoc/test/dotty/tools/scaladoc/BaseHtmlTest.scala index 2432fe984751..6bb1f1a20010 100644 --- a/scaladoc/test/dotty/tools/scaladoc/BaseHtmlTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/BaseHtmlTest.scala @@ -12,7 +12,7 @@ import dotty.tools.scaladoc.test.BuildInfo import util.IO class BaseHtmlTest: - val unresolvedLinkSelector = ".documentableBrief span[data-unresolved-link]" + val unresolvedLinkSelector = ".documentableBrief span[data-unresolved-link], .cover span[data-unresolved-link]" def projectName = "Test Project Name" def projectVersion = "1.0.1-M1" @@ -66,4 +66,4 @@ class BaseHtmlTest: val path = summon[ProjectContext].path.resolve(pathStr) assertTrue(s"File at $path does not exisits!", Files.exists(path)) val document = Jsoup.parse(IO.read(path)) - op(DocumentContext(document, path)) \ No newline at end of file + op(DocumentContext(document, path)) diff --git a/scaladoc/test/dotty/tools/scaladoc/tasty/comments/IntegrationTest.scala b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/IntegrationTest.scala index d3b0aac5099d..0a5329cf6333 100644 --- a/scaladoc/test/dotty/tools/scaladoc/tasty/comments/IntegrationTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/IntegrationTest.scala @@ -8,7 +8,7 @@ abstract class BaseIntegrationTest(pck: String) extends BaseHtmlTest: @Test def testLinks: Unit = withGeneratedDoc(pcks = Seq(pck, "commonlinks")) { def checkDocLinks(links: String*)(ctx: DocumentContext): Unit = - ctx.assertAttr(".documentableBrief a", "href", links:_*) + ctx.assertAttr(".documentableBrief a, .cover a", "href", links:_*) ctx.assertNotExists("unresolvedLinkSelector") def checkUnresolved(ctx: DocumentContext): Unit = From 2ad4bed6949bf38bdce0130c749bfec8cb7096ff Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Tue, 30 Mar 2021 12:01:48 +0200 Subject: [PATCH 23/40] Add handling for scaladoc 2 flags: private, groups, author, canonical doc url, project footer. --- .../tools/dotc/config/ScalaSettings.scala | 30 --------------- project/Build.scala | 7 +++- .../src/example/Documentation2.scala | 2 +- .../src/dotty/tools/scaladoc/Scaladoc.scala | 13 ++++++- .../tools/scaladoc/ScaladocSettings.scala | 38 +++++++++++++++++-- .../scaladoc/renderers/HtmlRenderer.scala | 19 +++++++++- .../scaladoc/renderers/MemberRenderer.scala | 12 +++++- .../dotty/tools/scaladoc/tasty/SymOps.scala | 6 +-- 8 files changed, 85 insertions(+), 42 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index ad1e0ad7151a..9f346f97e742 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -52,36 +52,6 @@ trait CommonScalaSettings { self: Settings.SettingGroup => val showPlugins: Setting[Boolean] = BooleanSetting ("-Xplugin-list", "Print a synopsis of loaded plugins.") val pluginsDir: Setting[String] = StringSetting ("-Xpluginsdir", "path", "Path to search for plugin archives.", Defaults.scalaPluginPath) val pluginOptions: Setting[List[String]] = MultiStringSetting ("-P", "plugin:opt", "Pass an option to a plugin, e.g. -P::") - - /** Doctool specific settings */ - val siteRoot: Setting[String] = StringSetting( - "-siteroot", - "site root", - "A directory containing static files from which to generate documentation.", - "./docs" - ) - - - val projectName: Setting[String] = StringSetting ( - "-project", - "project title", - "The name of the project.", - "" - ) - - val projectVersion: Setting[String] = StringSetting ( - "-project-version", - "project version", - "The current version of your project.", - "" - ) - - val projectLogo: Setting[String] = StringSetting( - "-project-logo", - "project logo filename", - "The file that contains the project's logo (in /images).", - "" - ) } class ScalaSettings extends Settings.SettingGroup with CommonScalaSettings { diff --git a/project/Build.scala b/project/Build.scala index 402bc133125f..bcf6d0deff4f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -291,6 +291,8 @@ object Build { disableDocSetting ) + private lazy val currentYear: String = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR).toString + lazy val scalacOptionsDocSettings = Seq( "-external-mappings:" + ".*scala.*::scaladoc3::http://dotty.epfl.ch/api/," + @@ -309,6 +311,9 @@ object Build { // Reflect doesn't expect to see it as a standalone definition // and therefore it's easier just not to document it "-skip-by-id:scala.runtime.MatchCase", + "-project-footer", s"Copyright (c) 2002-$currentYear, LAMP/EPFL", + "-author", + "-groups" ) // Settings used when compiling dotty with a non-bootstrapped dotty @@ -426,7 +431,7 @@ object Build { s"""version.number=${version.value} |maven.version.number=${version.value} |git.hash=${VersionUtil.gitHash} - |copyright.string=Copyright 2002-${Calendar.getInstance().get(Calendar.YEAR)}, LAMP/EPFL + |copyright.string=Copyright 2002-$currentYear, LAMP/EPFL """.stripMargin if (!(file.exists && IO.read(file) == contents)) { diff --git a/scaladoc-testcases/src/example/Documentation2.scala b/scaladoc-testcases/src/example/Documentation2.scala index 52eb51f348cc..78b23f099bc1 100644 --- a/scaladoc-testcases/src/example/Documentation2.scala +++ b/scaladoc-testcases/src/example/Documentation2.scala @@ -9,4 +9,4 @@ class UserDocLinkingClass { object ReturnObjectWithType { type returnType = Int -} \ No newline at end of file +} diff --git a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala index ec7c56881e39..4365e7b06198 100644 --- a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala +++ b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala @@ -35,6 +35,7 @@ object Scaladoc: docsRoot: Option[String] = None, projectVersion: Option[String] = None, projectLogo: Option[String] = None, + projectFooter: Option[String] = None, defaultSyntax: CommentSyntax = CommentSyntax.Markdown, sourceLinks: List[String] = Nil, revision: Option[String] = None, @@ -43,6 +44,10 @@ object Scaladoc: identifiersToSkip: List[String] = Nil, regexesToSkip: List[String] = Nil, rootDocPath: Option[String] = None, + includeAuthors: Boolean = false, + includeGroups: Boolean = false, + includePrivateAPI: Boolean = false, + docCanonicalBaseUrl: String = "", documentSyntheticTypes: Boolean = false, ) @@ -94,7 +99,8 @@ object Scaladoc: )(s => newContext.setSetting(s.asInstanceOf[Setting[T]], newValue)) } - allSettings.filterNot(scaladocSpecificSettings.contains).foreach(setInGlobal) + val commonScalaSettings = (new SettingGroup with CommonScalaSettings).allSettings + allSettings.filter(commonScalaSettings.contains).foreach(setInGlobal) def parseTastyRoots(roots: String) = roots.split(File.pathSeparatorChar).toList.map(new File(_)) @@ -162,6 +168,7 @@ object Scaladoc: siteRoot.nonDefault, projectVersion.nonDefault, projectLogo.nonDefault, + projectFooter.nonDefault, parseSyntax, sourceLinks.get, revision.nonDefault, @@ -170,6 +177,10 @@ object Scaladoc: skipById.get ++ deprecatedSkipPackages.get, skipByRegex.get, docRootContent.nonDefault, + author.get, + groups.get, + visibilityPrivate.get, + docCanonicalBaseUrl.get, YdocumentSyntheticTypes.get ) (Some(docArgs), newContext) diff --git a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala index 538292929dad..119305a68ad2 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala @@ -26,6 +26,17 @@ class ScaladocSettings extends SettingGroup with CommonScalaSettings: sourcepath, sourceroot ) + val projectName: Setting[String] = + StringSetting("-project", "project title", "The name of the project.", "", aliases = List("-doc-title")) + + val projectVersion: Setting[String] = + StringSetting("-project-version", "project version", "The current version of your project.", "", aliases = List("-doc-version")) + + val projectLogo: Setting[String] = + StringSetting("-project-logo", "project logo filename", "The file that contains the project's logo (in /images).", "", aliases = List("-doc-logo")) + + val projectFooter: Setting[String] = StringSetting("-project-footer", "project footer", "A footer on every Scaladoc page.", "", aliases = List("-doc-footer")) + val sourceLinks: Setting[List[String]] = MultiStringSetting("-source-links", "sources", SourceLinks.usage) @@ -57,8 +68,29 @@ class ScaladocSettings extends SettingGroup with CommonScalaSettings: val docRootContent: Setting[String] = StringSetting("-doc-root-content", "path", "The file from which the root package documentation should be imported.", "") + val author: Setting[Boolean] = + BooleanSetting("-author", "Include authors.", false) + + val groups: Setting[Boolean] = + BooleanSetting("-groups", "Group similar functions together (based on the @group annotation)", false) + + val visibilityPrivate: Setting[Boolean] = + BooleanSetting("-private", "Show all types and members. Unless specified, show only public and protected types and members.", false) + + val docCanonicalBaseUrl: Setting[String] = + StringSetting( + "-doc-canonical-base-url", + "url", + s"A base URL to use as prefix and add `canonical` URLs to all pages. The canonical URL may be used by search engines to choose the URL that you want people to see in search results. If unset no canonical URLs are generated.", + "" + ) + + val siteRoot: Setting[String] = StringSetting( + "-siteroot", + "site root", + "A directory containing static files from which to generate documentation.", + "./docs" + ) + val YdocumentSyntheticTypes: Setting[Boolean] = BooleanSetting("-Ydocument-synthetic-types", "Documents intrinsic types e. g. Any, Nothing. Setting is useful only for stdlib", false) - - def scaladocSpecificSettings: Set[Setting[_]] = - Set(sourceLinks, syntax, revision, externalDocumentationMappings, socialLinks, skipById, skipByRegex, deprecatedSkipPackages, docRootContent) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala index aeb1948373b7..b3bcb9b55ac4 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala @@ -131,6 +131,7 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx meta(charset := "utf-8"), meta(util.HTML.name := "viewport", content := "width=device-width, initial-scale=1"), title(page.link.name), + canonicalUrl(absolutePath(page.link.dri)), link( rel := "shortcut icon", `type` := "image/x-icon", @@ -170,6 +171,14 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx ) renderNested(navigablePage, toplevel = true)._2 + private def canonicalUrl(l: String): AppliedTag | String = + val canon = args.docCanonicalBaseUrl + if !canon.isEmpty then + val canonicalUrl = if canon.endsWith("/") then canon else canon + "/" + link(rel := "canonical", href := canonicalUrl + l) + else + "" // return empty tag + private def hasSocialLinks = !args.socialLinks.isEmpty private def socialLinks(whiteIcon: Boolean = true) = @@ -194,6 +203,13 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx )).dropRight(1) div(cls := "breadcrumbs")(innerTags:_*) + def textFooter: String | AppliedTag = + args.projectFooter.fold("") { f => + div(id := "footer-text")( + raw(f) + ) + } + div(id := "container")( div(id := "leftColumn")( div(id := "logo")( @@ -244,7 +260,8 @@ class HtmlRenderer(rootPackage: Member, val members: Map[DRI, Member])(using ctx cls := "scaladoc_logo" ) ) + ), + textFooter ) ) ) - ) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index e8e88c3f6ef9..a36ceefab328 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -46,13 +46,15 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext def opt(name: String, on: Option[DocPart]): Seq[AppliedTag] = if on.isEmpty then Nil else tableRow(name, renderDocPart(on.get)) + def authors(authors: List[DocPart]) = if summon[DocContext].args.includeAuthors then list("Authors", authors) else Nil + m.docs.fold(Nil)(d => nested("Type Params", d.typeParams) ++ nested("Value Params", d.valueParams) ++ opt("Returns", d.result) ++ nested("Throws", d.throws) ++ opt("Constructor", d.constructor) ++ - list("Authors", d.authors) ++ + authors(d.authors) ++ list("See also", d.see) ++ opt("Version", d.version) ++ opt("Since", d.since) ++ @@ -263,7 +265,13 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext Tab("Grouped members", "custom_groups", content, "selected") def buildMembers(s: Member): AppliedTag = - val (membersInGroups, rest) = s.members.partition(_.docs.exists(_.group.nonEmpty)) + def partitionIntoGroups(members: Seq[Member]) = + if summon[DocContext].args.includeGroups then + members.partition(_.docs.exists(_.group.nonEmpty)) + else + (Nil, members) + + val (membersInGroups, rest) = partitionIntoGroups(s.members) val extensions = rest.groupBy{ _.kind match diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index ec1b9c743a05..12738943c1fe 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -83,15 +83,15 @@ class SymOps[Q <: Quotes](val q: Q) extends JavadocAnchorCreator with Scaladoc2A Flags.Case -> Modifier.Case, ).collect { case (flag, mod) if sym.flags.is(flag) => mod } - def isHiddenByVisibility: Boolean = + def isHiddenByVisibility(using dctx: DocContext): Boolean = import VisibilityScope._ - getVisibility() match + !summon[DocContext].args.includePrivateAPI && getVisibility().match case Visibility.Private(_) => true case Visibility.Protected(ThisScope | ImplicitModuleScope | _: ExplicitModuleScope) => true case _ => false - def shouldDocumentClasslike: Boolean = !isHiddenByVisibility + def shouldDocumentClasslike(using dctx: DocContext): Boolean = !isHiddenByVisibility && !sym.flags.is(Flags.Synthetic) && (!sym.flags.is(Flags.Case) || !sym.flags.is(Flags.Enum)) && !(sym.companionModule.flags.is(Flags.Given)) From 88fa00e3bd5b841a0d6fc949ab25df5d4d2b2985 Mon Sep 17 00:00:00 2001 From: Tom Grigg Date: Mon, 1 Feb 2021 16:14:16 -0800 Subject: [PATCH 24/40] CI: automatically open an issue if nightly build fails --- .github/workflows/ci.yaml | 20 ++++++++++++++++++++ .github/workflows/issue_nightly_failed.md | 5 +++++ 2 files changed, 25 insertions(+) create mode 100644 .github/workflows/issue_nightly_failed.md diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index eb333c59d8c5..1ea336c4c0d1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -672,3 +672,23 @@ jobs: - name: Publish Dotty SBT Plugin Release run: | ./project/scripts/sbtPublish ";project sbt-dotty ;publishSigned ;sonatypeBundleRelease" + + open_issue_on_failure: + runs-on: [self-hosted, Linux] + container: + image: lampepfl/dotty:2021-03-22 + needs: [nightly_documentation, test_windows_full] + # The `failure()` expression is true iff at least one of the dependencies + # of this job (including transitive dependencies) has failed. + if: "failure() && github.event_name == 'schedule'" + steps: + - name: Checkout issue template + uses: actions/checkout@v2 + + - name: Open an issue + uses: JasonEtco/create-an-issue@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + filename: .github/workflows/issue_nightly_failed.md diff --git a/.github/workflows/issue_nightly_failed.md b/.github/workflows/issue_nightly_failed.md new file mode 100644 index 000000000000..8399513578f2 --- /dev/null +++ b/.github/workflows/issue_nightly_failed.md @@ -0,0 +1,5 @@ +--- +title: Nightly {{ workflow }} workflow of {{ date | date('YYYY-MM-DD') }} failed +labels: itype:bug, prio:blocker +--- +See {{ env.WORKFLOW_RUN_URL }} From b2a5ae534e2048289b48270e21ddab0b0e5c9a9c Mon Sep 17 00:00:00 2001 From: noti0na1 Date: Fri, 2 Apr 2021 22:01:38 -0400 Subject: [PATCH 25/40] Fix type namedArg when pt is nullable --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 3 ++- tests/explicit-nulls/pos/annotaions-args/JAnnots.java | 7 +++++++ tests/explicit-nulls/pos/annotaions-args/SAnnots.scala | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/explicit-nulls/pos/annotaions-args/JAnnots.java create mode 100644 tests/explicit-nulls/pos/annotaions-args/SAnnots.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 49b91a37c43f..aad4f127e9f3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -905,8 +905,9 @@ class Typer extends Namer * of annotation defined as `@interface Annot { int[] value() }` * We assume that calling `typedNamedArg` in context of Java implies that we are dealing * with annotation contructor, as named arguments are not allowed anywhere else in Java. + * Under explicit nulls, the pt could be nullable. We need to strip `Null` type first. */ - val arg1 = pt match { + val arg1 = pt.stripNull match { case AppliedType(a, typ :: Nil) if ctx.isJava && a.isRef(defn.ArrayClass) => tryAlternatively { typed(tree.arg, pt) } { val elemTp = untpd.TypedSplice(TypeTree(typ)) diff --git a/tests/explicit-nulls/pos/annotaions-args/JAnnots.java b/tests/explicit-nulls/pos/annotaions-args/JAnnots.java new file mode 100644 index 000000000000..4c7a31a333bb --- /dev/null +++ b/tests/explicit-nulls/pos/annotaions-args/JAnnots.java @@ -0,0 +1,7 @@ +public class JAnnots { + @SuppressWarnings("unused") + public void f1() {} + + @SuppressWarnings({"unused"}) + public void f2() {} +} diff --git a/tests/explicit-nulls/pos/annotaions-args/SAnnots.scala b/tests/explicit-nulls/pos/annotaions-args/SAnnots.scala new file mode 100644 index 000000000000..2e6ef7a9bb41 --- /dev/null +++ b/tests/explicit-nulls/pos/annotaions-args/SAnnots.scala @@ -0,0 +1,4 @@ +class SAnnots { + @SuppressWarnings(Array("unused")) + def f() = {} +} \ No newline at end of file From e65305dd2268ec245b246f90265d6db825820f0c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 3 Apr 2021 18:55:15 +0200 Subject: [PATCH 26/40] Fix treatment of bottom types in OrType#join and baseType computations --- .../dotty/tools/dotc/core/SymDenotations.scala | 16 +++++++++------- compiler/src/dotty/tools/dotc/core/Types.scala | 5 ++++- tests/pos/i11968.scala | 10 ++++++++++ 3 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 tests/pos/i11968.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index f170304d20d4..c65bf49f40b0 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2098,25 +2098,27 @@ object SymDenotations { computeTypeProxy case tp: AndOrType => - def computeAndOrType = { + def computeAndOrType: Type = val tp1 = tp.tp1 val tp2 = tp.tp2 + if !tp.isAnd then + if tp1.isBottomType && (tp1 frozen_<:< tp2) then return recur(tp2) + if tp2.isBottomType && (tp2 frozen_<:< tp1) then return recur(tp1) val baseTp = - if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty) + if symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty then symbol.typeRef - else { + else val baseTp1 = recur(tp1) val baseTp2 = recur(tp2) val combined = if (tp.isAnd) baseTp1 & baseTp2 else baseTp1 | baseTp2 - combined match { + combined match case combined: AndOrType if (combined.tp1 eq tp1) && (combined.tp2 eq tp2) && (combined.isAnd == tp.isAnd) => tp case _ => combined - } - } + if (baseTp.exists && inCache(tp1) && inCache(tp2)) record(tp, baseTp) baseTp - } + computeAndOrType case JavaArrayType(_) if symbol == defn.ObjectClass => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 24537ac288cd..4ac311729216 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3148,7 +3148,10 @@ object Types { /** Replace or type by the closest non-or type above it */ def join(using Context): Type = { if (myJoinPeriod != ctx.period) { - myJoin = TypeOps.orDominator(this) + myJoin = + if tp1 frozen_<:< tp2 then tp2 + else if tp2 frozen_<:< tp1 then tp1 + else TypeOps.orDominator(this) core.println(i"join of $this == $myJoin") assert(myJoin != this) myJoinPeriod = ctx.period diff --git a/tests/pos/i11968.scala b/tests/pos/i11968.scala new file mode 100644 index 000000000000..2cdffda07455 --- /dev/null +++ b/tests/pos/i11968.scala @@ -0,0 +1,10 @@ +class C { + def get(): Int = 0 +} + +def g = { + val s: String | Null = ??? + val l = s.length // ok + val c: C | Null = ??? + c.get() // error: value get is not a member of C | Null +} \ No newline at end of file From 230edd17abf6260b7482cb833bdb7806124b316c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 4 Apr 2021 12:49:56 +0200 Subject: [PATCH 27/40] Better fix: baseclasses intersection that takes bottom types into account --- .../src/dotty/tools/dotc/core/TypeOps.scala | 16 ++++--- .../src/dotty/tools/dotc/core/Types.scala | 5 +- tests/pos/i11968a.scala | 48 +++++++++++++++++++ tests/pos/i11981.scala | 21 ++++++++ 4 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 tests/pos/i11968a.scala create mode 100644 tests/pos/i11981.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 3c4db42b293b..c3b0d35f2b1d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -194,12 +194,16 @@ object TypeOps: */ def orDominator(tp: Type)(using Context): Type = { - /** a faster version of cs1 intersect cs2 */ - def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = { - val cs2AsSet = new util.HashSet[ClassSymbol](128) - cs2.foreach(cs2AsSet += _) - cs1.filter(cs2AsSet.contains) - } + /** a faster version of cs1 intersect cs2 that treats bottom types correctly */ + def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = + if cs1.head == defn.NothingClass then cs2 + else if cs2.head == defn.NothingClass then cs1 + else if cs1.head == defn.NullClass && !ctx.explicitNulls && cs2.head.derivesFrom(defn.ObjectClass) then cs2 + else if cs2.head == defn.NullClass && !ctx.explicitNulls && cs1.head.derivesFrom(defn.ObjectClass) then cs1 + else + val cs2AsSet = new util.HashSet[ClassSymbol](128) + cs2.foreach(cs2AsSet += _) + cs1.filter(cs2AsSet.contains) /** The minimal set of classes in `cs` which derive all other classes in `cs` */ def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match { diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 4ac311729216..24537ac288cd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3148,10 +3148,7 @@ object Types { /** Replace or type by the closest non-or type above it */ def join(using Context): Type = { if (myJoinPeriod != ctx.period) { - myJoin = - if tp1 frozen_<:< tp2 then tp2 - else if tp2 frozen_<:< tp1 then tp1 - else TypeOps.orDominator(this) + myJoin = TypeOps.orDominator(this) core.println(i"join of $this == $myJoin") assert(myJoin != this) myJoinPeriod = ctx.period diff --git a/tests/pos/i11968a.scala b/tests/pos/i11968a.scala new file mode 100644 index 000000000000..1db07a2f6658 --- /dev/null +++ b/tests/pos/i11968a.scala @@ -0,0 +1,48 @@ + +class A { + def get(): Int = 0 +} + +class B extends A {} + +class C extends A {} + +def test1 = { + val s: String | Null = ??? + val l = s.length + + val a: A | Null = new A + a.get() + + val bc: B | C = new B + bc.get() + + val bcn: B | (C | Null) = new C + bcn.get() + + val bnc: (B | Null) | C = null + bnc.get() + + val abcn: A | B | C | Null = new A + abcn.get() +} + +def test2 = { + val s: String | Nothing = ??? + val l = s.length + + val a: A | Nothing = new A + a.get() + + val bc: B | C = new B + bc.get() + + val bcn: B | (C | Nothing) = new C + bcn.get() + + val bnc: (B | Nothing) | C = new B + bnc.get() + + val abcn: A | B | C | Nothing = new A + abcn.get() +} \ No newline at end of file diff --git a/tests/pos/i11981.scala b/tests/pos/i11981.scala new file mode 100644 index 000000000000..abdf468bd9a6 --- /dev/null +++ b/tests/pos/i11981.scala @@ -0,0 +1,21 @@ +object Main: + class Null + type Optional[A] = A | Null + + val maybeInt: Optional[Int] = 1 + + // simplest typeclass + trait TC[F[_]] + + // given instances for our Optional and standard Option[_] + given g1: TC[Optional] = ??? + given g2: TC[Option] = ??? + + def summonTC[F[_], A](f: F[A])(using TC[F]): Unit = ??? + + summonTC(Option(42)) // OK + + summonTC[Optional, Int](maybeInt) // OK + + summonTC(maybeInt) + From cbaac3f2a1e2890f06120b063807e1b5be4ff48e Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 4 Apr 2021 23:14:48 +0200 Subject: [PATCH 28/40] Update tests/pos/i11968.scala --- tests/pos/i11968.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pos/i11968.scala b/tests/pos/i11968.scala index 2cdffda07455..27fdbb9e89a4 100644 --- a/tests/pos/i11968.scala +++ b/tests/pos/i11968.scala @@ -6,5 +6,5 @@ def g = { val s: String | Null = ??? val l = s.length // ok val c: C | Null = ??? - c.get() // error: value get is not a member of C | Null -} \ No newline at end of file + c.get() +} From 67960b9dd2c32d38e7f40ed1f41c5d6a253b7fea Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Apr 2021 10:19:54 +0200 Subject: [PATCH 29/40] Revert "Recursively check nonvariant arguments of base types for realizability" This reverts commit 60e39653d1a73044056c5c257d02565c5ee3899f. --- .../tools/dotc/core/CheckRealizable.scala | 33 +++++------------ tests/neg/i11545.scala | 37 ------------------- 2 files changed, 10 insertions(+), 60 deletions(-) delete mode 100644 tests/neg/i11545.scala diff --git a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala index 7f15bd6271f1..460647cdaf7c 100644 --- a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala +++ b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala @@ -32,8 +32,8 @@ object CheckRealizable { class HasProblemBaseArg(typ: Type, argBounds: TypeBounds)(using Context) extends Realizability(i" has a base type $typ with possibly conflicting parameter bounds ${argBounds.lo} <: ... <: ${argBounds.hi}") - class HasProblemBase(base1: Type, base2: Type, argStr: String)(using Context) - extends Realizability(i" has conflicting base type${argStr}s $base1 and $base2") + class HasProblemBase(base1: Type, base2: Type)(using Context) + extends Realizability(i" has conflicting base types $base1 and $base2") class HasProblemField(fld: SingleDenotation, problem: Realizability)(using Context) extends Realizability(i" has a member $fld which is not a legal path\nsince ${fld.symbol.name}: ${fld.info}${problem.msg}") @@ -167,30 +167,17 @@ class CheckRealizable(using Context) { new HasProblemBounds(name, mbr.info) } - def baseTypeProblems(base: Type, argStr: String): List[Realizability] = base match { - case base: AndType => - def factors(tp: Type): List[Type] = tp match - case AndType(tp1, tp2) => factors(tp1) ++ factors(tp2) - case _ => tp :: Nil - for case AndType(base1, base2) <- - factors(base).groupBy(_.classSymbol).values.map(_.reduce(_ & _)).toList - // try to merge factors with common class symbols - // if we cannot, it's a conflict - yield HasProblemBase(base1, base2, argStr) - case base: AppliedType => - base.argInfos.lazyZip(base.tycon.typeParams).flatMap { (arg, tparam) => - arg match - case bounds @ TypeBounds(lo, hi) if !(lo <:< hi) => - new HasProblemBaseArg(base, bounds) :: Nil - case arg if tparam.paramVarianceSign == 0 => - baseTypeProblems(arg, " argument") - case _ => - Nil + def baseTypeProblems(base: Type) = base match { + case AndType(base1, base2) => + new HasProblemBase(base1, base2) :: Nil + case base => + base.argInfos.collect { + case bounds @ TypeBounds(lo, hi) if !(lo <:< hi) => + new HasProblemBaseArg(base, bounds) } - case _ => Nil } val baseProblems = - tp.baseClasses.map(_.baseTypeOf(tp)).flatMap(baseTypeProblems(_, "")) + tp.baseClasses.map(_.baseTypeOf(tp)).flatMap(baseTypeProblems) baseProblems.foldLeft( refinementProblems.foldLeft( diff --git a/tests/neg/i11545.scala b/tests/neg/i11545.scala deleted file mode 100644 index 380027b7db41..000000000000 --- a/tests/neg/i11545.scala +++ /dev/null @@ -1,37 +0,0 @@ -@main def test: Unit = { - trait S[A] - trait Inv[A] - - locally { - class P[X] extends S[Inv[X] & Inv[String]] // error: cannot be instantiated - - def patmat[A, Y](s: S[Inv[A] & Y]): A = s match { - case p: P[x] => - "Hello" - } - - val got: Int = patmat[Int, Inv[String]](new P) - } - - locally { - class P[X] extends S[S[Inv[X] & Inv[String]]] // error: cannot be instantiated - - def patmat[A, Y](s: S[S[Inv[A] & Y]]): A = s match { - case p: P[x] => - "Hello" - } - - val got: Int = patmat[Int, Inv[String]](new P) - } - - locally { - abstract class P[X] extends S[Inv[X] & Inv[String]] - - def patmat[A, Y](s: S[Inv[A] & Y]): A = s match { - case p: P[x] => - "Hello" - } - - val got: Int = patmat[Int, Inv[String]](new P {}) // error: cannot be instantiated - } -} \ No newline at end of file From 65498067b4d94840edd45d000adbde08dc8261f7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Apr 2021 10:21:57 +0200 Subject: [PATCH 30/40] Add test --- tests/pos/i11995.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/pos/i11995.scala diff --git a/tests/pos/i11995.scala b/tests/pos/i11995.scala new file mode 100644 index 000000000000..d5a089c1bb35 --- /dev/null +++ b/tests/pos/i11995.scala @@ -0,0 +1,18 @@ +trait MyBase[A]{ + def foo: String +} + +case class BothThing[L, R]() extends MyBase[L & R]: + def foo: String = "blather" + +trait Has[A] + +trait Console +trait Clock + +type ConsoleWithClock = Has[Console] with Has[Clock] + +class Spec[R <: Has[_]] + +object MySpec1 extends Spec[Has[Console] with Has[Clock]] // does not compile +object MySpec2 extends Spec[ConsoleWithClock] // okay From 3e2483d659d3980de8a97f1afb6ab775e7d11d8a Mon Sep 17 00:00:00 2001 From: Aleksander Boruch-Gruszecki Date: Tue, 6 Apr 2021 10:38:28 +0200 Subject: [PATCH 31/40] Community build: tag scas as not requiring experimental The change was done accidentally. --- community-build/src/scala/dotty/communitybuild/projects.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index 4ee17cb535cc..0f59a529546b 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -247,7 +247,6 @@ object projects: lazy val scas = MillCommunityProject( project = "scas", baseCommand = "scas.application", - requiresExperimental = true ) lazy val intent = SbtCommunityProject( From d8b8fc1af183ec9e074640179a1b97c343f0c14e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Apr 2021 10:42:32 +0200 Subject: [PATCH 32/40] Allow by-name types in using clauses Fixes #11997 --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 14 +++++++------- docs/docs/internals/syntax.md | 11 ++++++----- docs/docs/reference/syntax.md | 11 ++++++----- tests/pos/i11997.scala | 3 +++ 4 files changed, 22 insertions(+), 17 deletions(-) create mode 100644 tests/pos/i11997.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index cec916a4b826..f5db937f8684 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1324,9 +1324,9 @@ object Parsers { * | MatchType * | InfixType * FunType ::= (MonoFunType | PolyFunType) - * MonoFunType ::= FunArgTypes (β€˜=>’ | β€˜?=>’) Type + * MonoFunType ::= FunTypeArgs (β€˜=>’ | β€˜?=>’) Type * PolyFunType ::= HKTypeParamClause '=>' Type - * FunArgTypes ::= InfixType + * FunTypeArgs ::= InfixType * | `(' [ [ β€˜[using]’ β€˜['erased'] FunArgType {`,' FunArgType } ] `)' * | '(' [ β€˜[using]’ β€˜['erased'] TypedFunParam {',' TypedFunParam } ')' */ @@ -1364,7 +1364,7 @@ object Parsers { else Function(params, t) } - def funArgTypesRest(first: Tree, following: () => Tree) = { + def funTypeArgsRest(first: Tree, following: () => Tree) = { val buf = new ListBuffer[Tree] += first while (in.token == COMMA) { in.nextToken() @@ -1387,11 +1387,11 @@ object Parsers { val ts = funArgType() match { case Ident(name) if name != tpnme.WILDCARD && in.isColon() => isValParamList = true - funArgTypesRest( + funTypeArgsRest( typedFunParam(paramStart, name.toTermName, imods), () => typedFunParam(in.offset, ident(), imods)) case t => - funArgTypesRest(t, funArgType) + funTypeArgsRest(t, funArgType) } accept(RPAREN) if isValParamList || in.isArrow then @@ -2907,10 +2907,10 @@ object Parsers { def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] = if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil - /** ContextTypes ::= Type {β€˜,’ Type} + /** ContextTypes ::= FunArgType {β€˜,’ FunArgType} */ def contextTypes(ofClass: Boolean, nparams: Int, impliedMods: Modifiers): List[ValDef] = - val tps = commaSeparated(typ) + val tps = commaSeparated(funArgType) var counter = nparams def nextIdx = { counter += 1; counter } val paramFlags = if ofClass then Private | Local | ParamAccessor else Param diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 388d0ed6971f..4ecefb4bdb83 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -162,10 +162,10 @@ Type ::= FunType | FunParamClause β€˜=>>’ Type TermLambdaTypeTree(ps, t) | MatchType | InfixType -FunType ::= FunArgTypes (β€˜=>’ | β€˜?=>’) Type Function(ts, t) +FunType ::= FunTypeArgs (β€˜=>’ | β€˜?=>’) Type Function(ts, t) | HKTypeParamClause '=>' Type PolyFunction(ps, t) -FunArgTypes ::= InfixType - | β€˜(’ [ FunArgType {β€˜,’ FunArgType } ] β€˜)’ +FunTypeArgs ::= InfixType + | β€˜(’ [ FunArgTypes ] β€˜)’ | FunParamClause FunParamClause ::= β€˜(’ TypedFunParam {β€˜,’ TypedFunParam } β€˜)’ TypedFunParam ::= id β€˜:’ Type @@ -191,6 +191,7 @@ Singleton ::= SimpleRef Singletons ::= Singleton { β€˜,’ Singleton } FunArgType ::= Type | β€˜=>’ Type PrefixOp(=>, t) +FunArgTypes ::= FunArgType { β€˜,’ FunArgType } ParamType ::= [β€˜=>’] ParamValueType ParamValueType ::= Type [β€˜*’] PostfixOp(t, "*") TypeArgs ::= β€˜[’ Types β€˜]’ ts @@ -323,7 +324,7 @@ HkTypeParam ::= {Annotation} [β€˜+’ | β€˜-’] (id [HkTypeParamClause] ClsParamClauses ::= {ClsParamClause} [[nl] β€˜(’ [β€˜implicit’] ClsParams β€˜)’] ClsParamClause ::= [nl] β€˜(’ ClsParams β€˜)’ - | [nl] β€˜(’ β€˜using’ (ClsParams | Types) β€˜)’ + | [nl] β€˜(’ β€˜using’ (ClsParams | FunArgTypes) β€˜)’ ClsParams ::= ClsParam {β€˜,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var [{Modifier} (β€˜val’ | β€˜var’) | β€˜inline’] Param @@ -331,7 +332,7 @@ Param ::= id β€˜:’ ParamType [β€˜=’ Expr] DefParamClauses ::= {DefParamClause} [[nl] β€˜(’ [β€˜implicit’] DefParams β€˜)’] DefParamClause ::= [nl] β€˜(’ DefParams β€˜)’ | UsingParamClause -UsingParamClause ::= [nl] β€˜(’ β€˜using’ (DefParams | Types) β€˜)’ +UsingParamClause ::= [nl] β€˜(’ β€˜using’ (DefParams | FunArgTypes) β€˜)’ DefParams ::= DefParam {β€˜,’ DefParam} DefParam ::= {Annotation} [β€˜inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. ``` diff --git a/docs/docs/reference/syntax.md b/docs/docs/reference/syntax.md index c3915a1676fc..ddb523efd9ea 100644 --- a/docs/docs/reference/syntax.md +++ b/docs/docs/reference/syntax.md @@ -161,10 +161,10 @@ Type ::= FunType | FunParamClause β€˜=>>’ Type | MatchType | InfixType -FunType ::= FunArgTypes (β€˜=>’ | β€˜?=>’) Type +FunType ::= FunTypeArgs (β€˜=>’ | β€˜?=>’) Type | HKTypeParamClause '=>' Type -FunArgTypes ::= InfixType - | β€˜(’ [ FunArgType {β€˜,’ FunArgType } ] β€˜)’ +FunTypeArgs ::= InfixType + | β€˜(’ [ FunArgTypes ] β€˜)’ | FunParamClause FunParamClause ::= β€˜(’ TypedFunParam {β€˜,’ TypedFunParam } β€˜)’ TypedFunParam ::= id β€˜:’ Type @@ -189,6 +189,7 @@ Singleton ::= SimpleRef FunArgType ::= Type | β€˜=>’ Type +FunArgTypes ::= FunArgType { β€˜,’ FunArgType } ParamType ::= [β€˜=>’] ParamValueType ParamValueType ::= Type [β€˜*’] TypeArgs ::= β€˜[’ Types β€˜]’ @@ -311,14 +312,14 @@ HkTypeParam ::= {Annotation} [β€˜+’ | β€˜-’] (id [HkTypeParamClause] ClsParamClauses ::= {ClsParamClause} [[nl] β€˜(’ [β€˜implicit’] ClsParams β€˜)’] ClsParamClause ::= [nl] β€˜(’ ClsParams β€˜)’ - | [nl] β€˜(’ β€˜using’ (ClsParams | Types) β€˜)’ + | [nl] β€˜(’ β€˜using’ (ClsParams | FunArgTypes) β€˜)’ ClsParams ::= ClsParam {β€˜,’ ClsParam} ClsParam ::= {Annotation} [{Modifier} (β€˜val’ | β€˜var’) | β€˜inline’] Param Param ::= id β€˜:’ ParamType [β€˜=’ Expr] DefParamClauses ::= {DefParamClause} [[nl] β€˜(’ [β€˜implicit’] DefParams β€˜)’] DefParamClause ::= [nl] β€˜(’ DefParams β€˜)’ | UsingParamClause -UsingParamClause ::= [nl] β€˜(’ β€˜using’ (DefParams | Types) β€˜)’ +UsingParamClause ::= [nl] β€˜(’ β€˜using’ (DefParams | FunArgTypes) β€˜)’ DefParams ::= DefParam {β€˜,’ DefParam} DefParam ::= {Annotation} [β€˜inline’] Param ``` diff --git a/tests/pos/i11997.scala b/tests/pos/i11997.scala new file mode 100644 index 000000000000..e6941affedff --- /dev/null +++ b/tests/pos/i11997.scala @@ -0,0 +1,3 @@ +def a(using i: => Int ) = () // ok +def b(using => Int ) = () // ok + From b2e62b6a273b8afddd28780490985378f8bedade Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Apr 2021 11:42:04 +0200 Subject: [PATCH 33/40] Better error message for errors arising from implicit completions Fixes #11994 --- .../src/dotty/tools/dotc/typer/Typer.scala | 42 +++++++++---------- tests/neg/i11994.check | 14 +++++++ tests/neg/i11994.scala | 2 + 3 files changed, 37 insertions(+), 21 deletions(-) create mode 100644 tests/neg/i11994.check create mode 100644 tests/neg/i11994.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 49b91a37c43f..8cb7ce3ee61d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3065,30 +3065,30 @@ class Typer extends Namer for err <- nestedCtx.reporter.allErrors.take(1) do rememberSearchFailure(qual, SearchFailure(app.withType(FailedExtension(app, selectionProto, err.msg)))) + + // try an implicit conversion or given extension + if ctx.mode.is(Mode.ImplicitsEnabled) && !tree.name.isConstructorName && qual.tpe.isValueType then + trace(i"try insert impl on qualifier $tree $pt") { + val selProto = selectionProto + inferView(qual, selProto) match + case SearchSuccess(found, _, _, isExtension) => + if isExtension then return found + else + checkImplicitConversionUseOK(found) + return typedSelect(tree, pt, found) + case failure: SearchFailure => + if failure.isAmbiguous then + return ( + if canDefineFurther(qual.tpe.widen) then + tryExtensionOrConversion(tree, pt, mbrProto, qual, locked, compat, privateOK) + else + err.typeMismatch(qual, selProto, failure.reason) // TODO: report NotAMember instead, but need to be aware of failure + ) + rememberSearchFailure(qual, failure) + } catch case ex: TypeError => rememberSearchFailure(qual, SearchFailure(qual.withType(NestedFailure(ex.toMessage, selectionProto)))) - - // try an implicit conversion or given extension - if ctx.mode.is(Mode.ImplicitsEnabled) && !tree.name.isConstructorName && qual.tpe.isValueType then - trace(i"try insert impl on qualifier $tree $pt") { - val selProto = selectionProto - inferView(qual, selProto) match - case SearchSuccess(found, _, _, isExtension) => - if isExtension then return found - else - checkImplicitConversionUseOK(found) - return typedSelect(tree, pt, found) - case failure: SearchFailure => - if failure.isAmbiguous then - return ( - if canDefineFurther(qual.tpe.widen) then - tryExtensionOrConversion(tree, pt, mbrProto, qual, locked, compat, privateOK) - else - err.typeMismatch(qual, selProto, failure.reason) // TODO: report NotAMember instead, but need to be aware of failure - ) - rememberSearchFailure(qual, failure) - } EmptyTree end tryExtensionOrConversion diff --git a/tests/neg/i11994.check b/tests/neg/i11994.check new file mode 100644 index 000000000000..24d18257ed9f --- /dev/null +++ b/tests/neg/i11994.check @@ -0,0 +1,14 @@ +-- [E008] Not Found Error: tests/neg/i11994.scala:1:28 ----------------------------------------------------------------- +1 |implicit def foo[T <: Tuple.meow]: Unit = ??? // error + | ^^^^^^^^^^ + | type meow is not a member of object Tuple. + | Extension methods were tried, but the search failed with: + | + | Cyclic reference involving method foo +-- [E008] Not Found Error: tests/neg/i11994.scala:2:18 ----------------------------------------------------------------- +2 |given [T <: Tuple.meow]: Unit = ??? // error + | ^^^^^^^^^^ + | type meow is not a member of object Tuple. + | Extension methods were tried, but the search failed with: + | + | Cyclic reference involving method given_Unit diff --git a/tests/neg/i11994.scala b/tests/neg/i11994.scala new file mode 100644 index 000000000000..373de300dd42 --- /dev/null +++ b/tests/neg/i11994.scala @@ -0,0 +1,2 @@ +implicit def foo[T <: Tuple.meow]: Unit = ??? // error +given [T <: Tuple.meow]: Unit = ??? // error From 9c1647bd393f65e5e5a0f6aa2dd5d24175843ce7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Apr 2021 13:22:45 +0200 Subject: [PATCH 34/40] Refine previous fix We cannot pull the try that catches the type error over both parts of `tryExtensionOrConversion` since a failed extension should still cause a conversion to be tried. We need to try both parts separately instead. --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 15 ++++++++++----- tests/pos/i11994.scala | 6 ++++++ 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i11994.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 8cb7ce3ee61d..04cba60052ea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3055,6 +3055,10 @@ class Typer extends Namer case _ => EmptyTree + def nestedFailure(ex: TypeError) = + rememberSearchFailure(qual, + SearchFailure(qual.withType(NestedFailure(ex.toMessage, selectionProto)))) + // try an extension method in scope try val nestedCtx = ctx.fresh.setNewTyperState() @@ -3065,9 +3069,11 @@ class Typer extends Namer for err <- nestedCtx.reporter.allErrors.take(1) do rememberSearchFailure(qual, SearchFailure(app.withType(FailedExtension(app, selectionProto, err.msg)))) + catch case ex: TypeError => nestedFailure(ex) - // try an implicit conversion or given extension - if ctx.mode.is(Mode.ImplicitsEnabled) && !tree.name.isConstructorName && qual.tpe.isValueType then + // try an implicit conversion or given extension + if ctx.mode.is(Mode.ImplicitsEnabled) && !tree.name.isConstructorName && qual.tpe.isValueType then + try trace(i"try insert impl on qualifier $tree $pt") { val selProto = selectionProto inferView(qual, selProto) match @@ -3086,9 +3092,8 @@ class Typer extends Namer ) rememberSearchFailure(qual, failure) } - catch case ex: TypeError => - rememberSearchFailure(qual, - SearchFailure(qual.withType(NestedFailure(ex.toMessage, selectionProto)))) + catch case ex: TypeError => nestedFailure(ex) + EmptyTree end tryExtensionOrConversion diff --git a/tests/pos/i11994.scala b/tests/pos/i11994.scala new file mode 100644 index 000000000000..3ae8d311051f --- /dev/null +++ b/tests/pos/i11994.scala @@ -0,0 +1,6 @@ +class WeirdNumber(v: Double) extends java.lang.Number { + override def doubleValue = v + override def intValue = v.intValue + override def longValue = v.longValue + override def floatValue = v.floatValue +} From 58d76653bb58a4a72eb93c48a78fdc07681968be Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 6 Apr 2021 21:05:37 +0200 Subject: [PATCH 35/40] Streamline flag checking - Concentrate everything in Checking -- it's too easy to add flag checking in several places otherwise. - Simplify some operations --- .../src/dotty/tools/dotc/ast/Desugar.scala | 13 +------ .../tools/dotc/reporting/ErrorMessageID.scala | 4 +- .../dotty/tools/dotc/reporting/messages.scala | 36 ++++-------------- .../src/dotty/tools/dotc/typer/Checking.scala | 38 +++++++++++++------ tests/neg/AbstractObject.check | 6 --- tests/neg/AbstractObject.scala | 1 - tests/neg/SealedObject.check | 6 --- tests/neg/SealedObject.scala | 1 - tests/neg/modifier-not-allowed.check | 10 ++++- tests/neg/modifier-not-allowed.scala | 2 + 10 files changed, 49 insertions(+), 68 deletions(-) delete mode 100644 tests/neg/AbstractObject.check delete mode 100644 tests/neg/AbstractObject.scala delete mode 100644 tests/neg/SealedObject.check delete mode 100644 tests/neg/SealedObject.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index efbe5878828c..27362bff418f 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -7,7 +7,7 @@ import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags import Symbols._, StdNames._, Trees._, Phases._, ContextOps._ import Decorators._, transform.SymUtils._ import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName} -import typer.{FrontEnd, Namer} +import typer.{FrontEnd, Namer, Checking} import util.{Property, SourceFile, SourcePosition} import config.Feature.{sourceVersion, migrateTo3, enabled} import config.SourceVersion._ @@ -859,16 +859,7 @@ object desugar { val mods = mdef.mods val moduleName = normalizeName(mdef, impl).asTermName def isEnumCase = mods.isEnumCase - - def flagSourcePos(flag: FlagSet) = mods.mods.find(_.flags == flag).fold(mdef.sourcePos)(_.sourcePos) - - if (mods.is(Abstract)) - report.error(AbstractCannotBeUsedForObjects(mdef), flagSourcePos(Abstract)) - if (mods.is(Sealed)) - report.error(ModifierRedundantForObjects(mdef, "sealed"), flagSourcePos(Sealed)) - // Maybe this should be an error; see https://github.com/scala/bug/issues/11094. - if (mods.is(Final) && !mods.is(Synthetic)) - report.warning(ModifierRedundantForObjects(mdef, "final"), flagSourcePos(Final)) + Checking.checkWellFormedModule(mdef) if (mods.is(Package)) packageModuleDef(mdef) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 518be160763e..1cc0f2afc1a7 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -154,8 +154,8 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] { ErasedTypesCanOnlyBeFunctionTypesID, CaseClassMissingNonImplicitParamListID, EnumerationsShouldNotBeEmptyID, - AbstractCannotBeUsedForObjectsID, - ModifierRedundantForObjectsID, + UNUSED_1, + RedundantModifierID, TypedCaseDoesNotExplicitlyExtendTypedEnumID, IllegalRedefinitionOfStandardKindID, NoExtensionMethodAllowedID, diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index eb3e2b22781c..3ef9d8d076a2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2364,32 +2364,6 @@ import transform.SymUtils._ |""".stripMargin } - class AbstractCannotBeUsedForObjects(mdef: untpd.ModuleDef)(using Context) - extends SyntaxMsg(AbstractCannotBeUsedForObjectsID) { - def msg = em"${hl("abstract")} modifier cannot be used for objects" - - def explain = - em"""|Objects are final and cannot be extended, thus cannot have the ${hl("abstract")} modifier - | - |You may want to define an abstract class: - | ${hl("abstract")} ${hl("class")} Abstract${mdef.name} { } - | - |And extend it in an object: - | ${hl("object")} ${mdef.name} ${hl("extends")} Abstract${mdef.name} { } - |""".stripMargin - } - - class ModifierRedundantForObjects(mdef: untpd.ModuleDef, modifier: String)(using Context) - extends SyntaxMsg(ModifierRedundantForObjectsID) { - def msg = em"${hl(modifier)} modifier is redundant for objects" - - def explain = - em"""|Objects cannot be extended making the ${hl(modifier)} modifier redundant. - |You may want to define the object without it: - | ${hl("object")} ${mdef.name} { } - |""".stripMargin - } - class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd.TypeDef)(using Context) extends SyntaxMsg(TypedCaseDoesNotExplicitlyExtendTypedEnumID) { def msg = i"explicit extends clause needed because both enum case and enum class have type parameters" @@ -2481,9 +2455,15 @@ import transform.SymUtils._ |""".stripMargin } - class ModifierNotAllowedForDefinition(flag: FlagSet)(using Context) + class ModifierNotAllowedForDefinition(flag: Flag)(using Context) extends SyntaxMsg(ModifierNotAllowedForDefinitionID) { - def msg = s"Modifier `${flag.flagsString}` is not allowed for this definition" + def msg = em"Modifier ${hl(flag.flagsString)} is not allowed for this definition" + def explain = "" + } + + class RedundantModifier(flag: Flag)(using Context) + extends SyntaxMsg(RedundantModifierID) { + def msg = em"Modifier ${hl(flag.flagsString)} is redundant for this definition" def explain = "" } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index cd0effedafcd..9f48913d1f67 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -433,9 +433,10 @@ object Checking { if (sym.isAllOf(flag1 | flag2)) fail(msg) def checkCombination(flag1: FlagSet, flag2: FlagSet) = if sym.isAllOf(flag1 | flag2) then fail(i"illegal combination of modifiers: `${flag1.flagsString}` and `${flag2.flagsString}` for: $sym") - def checkApplicable(flag: FlagSet, ok: Boolean) = - if (!ok && !sym.is(Synthetic)) + def checkApplicable(flag: Flag, ok: Boolean) = + if sym.is(flag, butNot = Synthetic) && !ok then fail(ModifierNotAllowedForDefinition(flag)) + sym.resetFlag(flag) if (sym.is(Inline) && ( sym.is(ParamAccessor) && sym.owner.isClass @@ -485,6 +486,15 @@ object Checking { if (sym.isConstructor && !sym.isPrimaryConstructor && sym.owner.is(Trait, butNot = JavaDefined)) val addendum = if ctx.settings.Ydebug.value then s" ${sym.owner.flagsString}" else "" fail("Traits cannot have secondary constructors" + addendum) + checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) + checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) + if (sym.isType && !sym.is(Deferred)) + for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) { + fail(CannotHaveSameNameAs(sym, cls, CannotHaveSameNameAs.CannotBeOverridden)) + sym.setFlag(Private) // break the overriding relationship by making sym Private + } + checkApplicable(Erased, + !sym.isOneOf(MutableOrLazy, butNot = Given) && !sym.isType || sym.isClass) checkCombination(Final, Open) checkCombination(Sealed, Open) checkCombination(Final, Sealed) @@ -493,18 +503,22 @@ object Checking { checkCombination(Private, Override) checkCombination(Lazy, Inline) checkNoConflict(Lazy, ParamAccessor, s"parameter may not be `lazy`") - if (sym.is(Inline)) checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) - if (sym.is(Lazy)) checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) - if (sym.isType && !sym.is(Deferred)) - for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) { - fail(CannotHaveSameNameAs(sym, cls, CannotHaveSameNameAs.CannotBeOverridden)) - sym.setFlag(Private) // break the overriding relationship by making sym Private - } - if (sym.is(Erased)) - checkApplicable(Erased, - !sym.isOneOf(MutableOrLazy, butNot = Given) && !sym.isType || sym.isClass) } + /** Check for illegal or redundant modifiers on modules. This is done separately + * from checkWellformed, since the original module modifiers don't surivive desugaring + */ + def checkWellFormedModule(mdef: untpd.ModuleDef)(using Context) = + val mods = mdef.mods + def flagSourcePos(flag: FlagSet) = + mods.mods.find(_.flags == flag).getOrElse(mdef).srcPos + if mods.is(Abstract) then + report.error(ModifierNotAllowedForDefinition(Abstract), flagSourcePos(Abstract)) + if mods.is(Sealed) then + report.error(ModifierNotAllowedForDefinition(Sealed), flagSourcePos(Sealed)) + if mods.is(Final, butNot = Synthetic) then + report.warning(RedundantModifier(Final), flagSourcePos(Final)) + /** Check the type signature of the symbol `M` defined by `tree` does not refer * to a private type or value which is invisible at a point where `M` is still * visible. diff --git a/tests/neg/AbstractObject.check b/tests/neg/AbstractObject.check deleted file mode 100644 index 4f3200ba2439..000000000000 --- a/tests/neg/AbstractObject.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E146] Syntax Error: tests/neg/AbstractObject.scala:1:0 ------------------------------------------------------------- -1 |abstract object A {} // error - |^^^^^^^^ - |abstract modifier cannot be used for objects - -longer explanation available when compiling with `-explain` diff --git a/tests/neg/AbstractObject.scala b/tests/neg/AbstractObject.scala deleted file mode 100644 index 0abd82d2a7b5..000000000000 --- a/tests/neg/AbstractObject.scala +++ /dev/null @@ -1 +0,0 @@ -abstract object A {} // error diff --git a/tests/neg/SealedObject.check b/tests/neg/SealedObject.check deleted file mode 100644 index d63079f07a24..000000000000 --- a/tests/neg/SealedObject.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E147] Syntax Error: tests/neg/SealedObject.scala:1:0 --------------------------------------------------------------- -1 |sealed object A {} // error - |^^^^^^ - |sealed modifier is redundant for objects - -longer explanation available when compiling with `-explain` diff --git a/tests/neg/SealedObject.scala b/tests/neg/SealedObject.scala deleted file mode 100644 index ecee2ea4857a..000000000000 --- a/tests/neg/SealedObject.scala +++ /dev/null @@ -1 +0,0 @@ -sealed object A {} // error \ No newline at end of file diff --git a/tests/neg/modifier-not-allowed.check b/tests/neg/modifier-not-allowed.check index f80ffb85495b..75d0dc8b616e 100644 --- a/tests/neg/modifier-not-allowed.check +++ b/tests/neg/modifier-not-allowed.check @@ -1,4 +1,12 @@ -- [E156] Syntax Error: tests/neg/modifier-not-allowed.scala:2:13 ------------------------------------------------------ 2 | opaque def o: Int = 3 // error | ^^^^^^^^^^^^^^^^^^^^^ - | Modifier `opaque` is not allowed for this definition + | Modifier opaque is not allowed for this definition +-- [E156] Syntax Error: tests/neg/modifier-not-allowed.scala:3:2 ------------------------------------------------------- +3 | abstract object A {} // error + | ^^^^^^^^ + | Modifier abstract is not allowed for this definition +-- [E156] Syntax Error: tests/neg/modifier-not-allowed.scala:4:2 ------------------------------------------------------- +4 | sealed object A {} // error + | ^^^^^^ + | Modifier sealed is not allowed for this definition diff --git a/tests/neg/modifier-not-allowed.scala b/tests/neg/modifier-not-allowed.scala index 2838dbb8f3eb..a8d4522eac60 100644 --- a/tests/neg/modifier-not-allowed.scala +++ b/tests/neg/modifier-not-allowed.scala @@ -1,3 +1,5 @@ object Test { opaque def o: Int = 3 // error + abstract object A {} // error + sealed object A {} // error } \ No newline at end of file From 1ed90a58b604d4c39b7297d8c7789ac64b042971 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Mon, 22 Mar 2021 16:11:31 +0100 Subject: [PATCH 36/40] bump sbt to 1.5.0 --- .github/workflows/ci.yaml | 5 +---- .../src/scala/dotty/communitybuild/projects.scala | 4 ++-- project/Build.scala | 3 +++ project/build.properties | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1ea336c4c0d1..20b59eb56aa6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -360,12 +360,9 @@ jobs: - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - name: Test sbt 1.4.x + - name: Test sbt run: ./project/scripts/sbt "sbt-dotty/scripted; sbt-community-build/scripted" - - name: Test sbt 1.5.x - run: ./project/scripts/sbt "set \`sbt-dotty\`/scriptedSbt := \"1.5.0-RC1\"; sbt-dotty/scripted sbt-dotty/*" - test_java8: runs-on: [self-hosted, Linux] container: diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index a44b44d6085f..fa063832fc52 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -124,7 +124,7 @@ final case class SbtCommunityProject( case Some(ivyHome) => List(s"-Dsbt.ivy.home=$ivyHome") case _ => Nil extraSbtArgs ++ sbtProps ++ List( - "-sbt-version", "1.4.9", + "-sbt-version", "1.5.0", "-Dsbt.supershell=false", s"-Ddotty.communitybuild.dir=$communitybuildDir", s"--addPluginSbtFile=$sbtPluginFilePath" @@ -140,7 +140,7 @@ object projects: private def forceDoc(projects: String*) = projects.map(project => - s""";set $project/Compile/doc/sources ++= ($project/Compile/doc/tastyFiles).value ;$project/doc""" + s""";set $project/Compile/doc/sources ++= ($project/Compile/doc/dotty.tools.sbtplugin.DottyPlugin.autoImport.tastyFiles).value ;$project/doc""" ).mkString(" ") private def aggregateDoc(in: String)(projects: String*) = diff --git a/project/Build.scala b/project/Build.scala index e06f260a581b..f8c2866a6acb 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1248,6 +1248,9 @@ object Build { baseDirectory.value / "../language-server/src/dotty/tools/languageserver/config", sbtTestDirectory := baseDirectory.value / "sbt-test", + // ensure that sbt-dotty is built on sbt 1.4 + pluginCrossBuild / sbtVersion := "1.4.9", + // The batch mode accidentally became the default with no way to disable // it in sbt 1.4 (https://github.com/sbt/sbt/issues/5913#issuecomment-716003195). // We enable it explicitly here to make it clear that we're using it. diff --git a/project/build.properties b/project/build.properties index dbae93bcfd51..e67343ae796c 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.4.9 +sbt.version=1.5.0 From 69a1a41d7f404afe2bf9e70af7b7caeb420f1ba5 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Wed, 7 Apr 2021 12:01:27 +0200 Subject: [PATCH 37/40] Use sbt slash syntax --- .../src/scala/dotty/communitybuild/projects.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index fa063832fc52..95fa4e9ece59 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -102,12 +102,12 @@ final case class SbtCommunityProject( scalacOptions.map("\"" + _ + "\"").mkString("List(", ",", ")") private val baseCommand = - "clean; set logLevel in Global := Level.Error; set updateOptions in Global ~= (_.withLatestSnapshots(false)); " - ++ (if scalacOptions.isEmpty then "" else s"""set scalacOptions in Global ++= $scalacOptionsString;""") + "clean; set Global/logLevel := Level.Error; set Global/updateOptions ~= (_.withLatestSnapshots(false)); " + ++ (if scalacOptions.isEmpty then "" else s"""set Global/scalacOptions ++= $scalacOptionsString;""") ++ s"++$compilerVersion!; " override val testCommand = - """set testOptions in Global += Tests.Argument(TestFramework("munit.Framework"), "+l"); """ + """set Global/testOptions += Tests.Argument(TestFramework("munit.Framework"), "+l"); """ ++ s"$baseCommand$sbtTestCommand" override val publishCommand = @@ -512,7 +512,7 @@ object projects: lazy val cats = SbtCommunityProject( project = "cats", - sbtTestCommand = "set scalaJSStage in Global := FastOptStage;buildJVM;validateAllJS", + sbtTestCommand = "set Global/scalaJSStage := FastOptStage;buildJVM;validateAllJS", sbtPublishCommand = "catsJVM/publishLocal;catsJS/publishLocal", dependencies = List(discipline, disciplineMunit, scalacheck, simulacrumScalafixAnnotations), scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Ysafe-init") // disable -Ysafe-init, due to -Xfatal-warning From dd95969010a9d60c82827b0851b22de7045830d7 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Wed, 7 Apr 2021 14:10:41 +0200 Subject: [PATCH 38/40] Fix minitest doc command --- community-build/src/scala/dotty/communitybuild/projects.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index 95fa4e9ece59..b1433492a00c 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -145,7 +145,7 @@ object projects: private def aggregateDoc(in: String)(projects: String*) = val tastyFiles = - (in +: projects).map(p => s"($p/Compile/doc/tastyFiles).value").mkString(" ++ ") + (in +: projects).map(p => s"($p/Compile/doc/dotty.tools.sbtplugin.DottyPlugin.autoImport.tastyFiles).value").mkString(" ++ ") s""";set $in/Compile/doc/sources ++= file("a.scala") +: ($tastyFiles) ;$in/doc""" lazy val utest = MillCommunityProject( From 7229749205429db724ad5730f84940d3df75ec77 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Wed, 7 Apr 2021 14:34:18 +0200 Subject: [PATCH 39/40] Update stdLib213 --- community-build/community-projects/stdLib213 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/stdLib213 b/community-build/community-projects/stdLib213 index d39b0169e2e1..891f92f01cfb 160000 --- a/community-build/community-projects/stdLib213 +++ b/community-build/community-projects/stdLib213 @@ -1 +1 @@ -Subproject commit d39b0169e2e1fa434d05669fe4abc720ac879e6e +Subproject commit 891f92f01cfbc900a1a1efada73530246babd075 From 8c2656f19efc16d82cea49083d0e1356eb30ffc3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 9 Apr 2021 13:34:44 +0200 Subject: [PATCH 40/40] Fix type test for trait parameter arguments Fixes #11993 --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 6 +-- .../tools/dotc/core/SymDenotations.scala | 4 ++ .../tools/dotc/reporting/ErrorMessageID.scala | 2 +- .../dotty/tools/dotc/reporting/messages.scala | 8 +++ .../tools/dotc/transform/Constructors.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 50 ++++++++----------- .../derivedNames.scala | 2 +- tests/neg/i3989f.scala | 2 +- tests/pos/i11993.scala | 15 ++++++ 9 files changed, 56 insertions(+), 35 deletions(-) create mode 100644 tests/pos/i11993.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 2c1353250db2..73f1585427ac 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -652,7 +652,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } } - /** The type arguemnts of a possibly curried call */ + /** The type arguments of a possibly curried call */ def typeArgss(tree: Tree): List[List[Tree]] = @tailrec def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match @@ -661,7 +661,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case _ => argss loop(tree, Nil) - /** The term arguemnts of a possibly curried call */ + /** The term arguments of a possibly curried call */ def termArgss(tree: Tree): List[List[Tree]] = @tailrec def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match @@ -670,7 +670,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case _ => argss loop(tree, Nil) - /** The type and term arguemnts of a possibly curried call, in the order they are given */ + /** The type and term arguments of a possibly curried call, in the order they are given */ def allArgss(tree: Tree): List[List[Tree]] = @tailrec def loop(tree: Tree, argss: List[List[Tree]]): List[List[Tree]] = tree match diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index c65bf49f40b0..2d71bd350b3b 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2199,6 +2199,10 @@ object SymDenotations { def paramAccessors(using Context): List[Symbol] = unforcedDecls.filter(_.is(ParamAccessor)) + /** The term parameter getters of this class. */ + def paramGetters(using Context): List[Symbol] = + paramAccessors.filterNot(_.isSetter) + /** If this class has the same `decls` scope reference in `phase` and * `phase.next`, install a new denotation with a cloned scope in `phase.next`. */ diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 1cc0f2afc1a7..d73e799ddf3c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -154,7 +154,7 @@ enum ErrorMessageID extends java.lang.Enum[ErrorMessageID] { ErasedTypesCanOnlyBeFunctionTypesID, CaseClassMissingNonImplicitParamListID, EnumerationsShouldNotBeEmptyID, - UNUSED_1, + IllegalParameterInitID, RedundantModifierID, TypedCaseDoesNotExplicitlyExtendTypedEnumID, IllegalRedefinitionOfStandardKindID, diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 3ef9d8d076a2..b32f00dde083 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1467,6 +1467,14 @@ import transform.SymUtils._ def msg = em"""$tp does not conform to its self type $selfType; cannot be instantiated""" } + class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symbol)(using Context) + extends TypeMismatchMsg(found, expected)(IllegalParameterInitID): + def msg = + em"""illegal parameter initialization of $param. + | + | The argument passed for $param has type: $found + | but $cls expects $param to have type: $expected""" + class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)( implicit ctx: Context) extends SyntaxMsg(AbstractMemberMayNotHaveModifierID) { diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index 16666795ab77..024261746265 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -132,7 +132,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = // Produce aligned accessors and constructor parameters. We have to adjust // for any outer parameters, which are last in the sequence of original // parameter accessors but come first in the constructor parameter list. - val accessors = cls.paramAccessors.filterNot(x => x.isSetter) + val accessors = cls.paramGetters val vparamsWithOuterLast = vparams match { case vparam :: rest if vparam.name == nme.OUTER => rest ::: vparam :: Nil case _ => vparams diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 7ff65d5c36e3..7d9e25c33b2d 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -26,7 +26,7 @@ import Constants.Constant import NullOpsDecorator._ object RefChecks { - import tpd.{Tree, MemberDef, NamedArg, Literal, Template, DefDef} + import tpd._ val name: String = "refchecks" @@ -133,6 +133,27 @@ object RefChecks { false } + /** Check that arguments passed to trait parameters conform to the parameter types + * in the current class. This is necessary since parameter types might be narrowed + * through intersection with other parent traits. See neg/i11018.scala. + */ + def checkParamInits(app: Apply): Unit = + val parentCls = app.tpe.classSymbol + if parentCls.is(Trait) then + val params = parentCls.asClass.paramGetters + val args = termArgss(app).flatten + for (param, arg) <- params.lazyZip(args) do + if !param.is(Private) then // its type can be narrowed through intersection -> a check is needed + val paramType = cls.thisType.memberInfo(param) + if !(arg.tpe <:< paramType) then + val argTypes = args.tpes + // it could still be OK but we might need to substitute arguments for parameters + // to account for dependent parameters. See pos/i11993.scala + if !(arg.tpe.subst(params, argTypes) <:< paramType.subst(params, argTypes)) + then + report.error(IllegalParameterInit(arg.tpe, paramType, param, cls), arg.srcPos) + + for case app: Apply <- parentTrees do checkParamInits(app) case _ => } @@ -801,34 +822,7 @@ object RefChecks { report.error(problem(), clazz.srcPos) } - // check that basetype and subtype agree on types of trait parameters - // - // I.e. trait and class parameters not only need to conform to the expected - // type of the corresponding base-trait, but also to the type as seen by the - // inheriting subtype. - def checkTraitParametersOK() = for { - parent <- clazz.info.parents - parentSym = parent.classSymbol - if parentSym.isClass - cls = parentSym.asClass - if cls.paramAccessors.nonEmpty - param <- cls.paramAccessors - } { - val tpeFromParent = parent.memberInfo(param) - val tpeFromClazz = clazz.thisType.memberInfo(param) - if (!(tpeFromParent <:< tpeFromClazz)) { - val msg = - em"""illegal parameter: The types of $param do not match. - | - | $param in $cls has type: $tpeFromParent - | but $clazz expects $param to have type: $tpeFromClazz""" - - report.error(msg, clazz.srcPos) - } - } - checkParameterizedTraitsOK() - checkTraitParametersOK() } /** Check that `site` does not inherit conflicting generic instances of `baseCls`, diff --git a/tests/generic-java-signatures/derivedNames.scala b/tests/generic-java-signatures/derivedNames.scala index f42c6ebab6a2..23df59ff561d 100644 --- a/tests/generic-java-signatures/derivedNames.scala +++ b/tests/generic-java-signatures/derivedNames.scala @@ -9,7 +9,7 @@ object Test { if (scala.util.Properties.isWin) assert(returnType.toString == out1 || returnType.toString == out2) else if (scala.util.Properties.isMac) - assert(returnType.toString == out1) + assert(returnType.toString == out1, s"$returnType != $out1") else assert(returnType.toString == out2) } diff --git a/tests/neg/i3989f.scala b/tests/neg/i3989f.scala index 12738a83d282..7387518c7362 100644 --- a/tests/neg/i3989f.scala +++ b/tests/neg/i3989f.scala @@ -3,7 +3,7 @@ object Test extends App { class B[+X](val y: X) extends A[X](y) class C extends B(5) with A[String] // error: illegal inheritance - class D extends B(5) with A[Any] // error: illegal parameter + class D extends B(5) with A[Any] def f(a: A[Int]): String = a match { case c: C => c.x diff --git a/tests/pos/i11993.scala b/tests/pos/i11993.scala new file mode 100644 index 000000000000..630d4cd826d9 --- /dev/null +++ b/tests/pos/i11993.scala @@ -0,0 +1,15 @@ +object test1: + class Foo( + val x: String, + val y: Option[x.type] + ) + + class Bar extends Foo("bar", Some("bar")) + +object test2: + trait Foo( + val x: String, + val y: Option[x.type] + ) + + class Bar extends Foo("bar", Some("bar"))