diff --git a/.gitattributes b/.gitattributes index da4421cb78ed..99eca173f23e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -21,6 +21,9 @@ text eol=lf *.txt eol=lf *.xml eol=lf +# Some sbt launcher scripts can't handle CR in .jvmopts +.jvmopts eol=lf + # Windows-specific files get windows endings *.bat eol=crlf *.cmd eol=crlf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000000..70647980f2e2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +name: Scala Merge CI + +on: + push: + branches: ['2.*.x'] + +defaults: + run: + shell: bash + +jobs: + build_and_test: + name: Windows + runs-on: windows-latest + strategy: + fail-fast: false + steps: + - run: git config --global core.autocrlf false + - name: Checkout + uses: actions/checkout@v2 + + # Note that we don't use olafurpg/setup-scala; it wouldn't buy us anything + # over setup-java. (We don't want csbt or xsbt; we prefer the standard + # sbt launch script, which comes preinstalled on Windows (and Ubuntu).) + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: adopt + java-version: 8 + + - name: Cache + uses: actions/cache@v2 + with: + path: | + ~/.sbt + ~/.ivy2/cache + ~/.cache/coursier + key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + + - name: Build + run: | + sbt setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal + + - name: Test + run: | + STARR=`cat buildcharacter.properties | grep ^maven.version.number | cut -d= -f2` && echo $STARR + sbt -Dstarr.version=$STARR setupValidateTest test:compile info testAll diff --git a/.gitignore b/.gitignore index b49d07b1e72f..082750115d52 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,10 @@ local.sbt jitwatch.out +# Used by the restarr/restarrFull commands as target directories +/build-restarr/ +/target-restarr/ + # metals .metals .bloop diff --git a/.mailmap b/.mailmap index 04ae00e8cce7..815b48ad3c20 100644 --- a/.mailmap +++ b/.mailmap @@ -25,6 +25,7 @@ Damien Obristi Daniel C. Sobral Daniel C. Sobral Daniel Lorch +Darcy Shen Diego E. Alonso Blas Diego E. Alonso Blas Erik Stenman diff --git a/.travis.yml b/.travis.yml index ac1272ff59f4..cfb5e32e83d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,126 +1,155 @@ version: ~> 1.0 # needed for imports import: scala/scala-dev:travis/default.yml -dist: bionic # for generating spec PDF; see #7432 - +dist: xenial # GPG stuff breaks on bionic; scala/scala-dev#764 language: scala stages: - build - test +templates: # this has no effect on travis, it's just a place to put our templates + pr-jdk8: &pr-jdk8 + if: type = pull_request OR repo != scala/scala + + cron-jdk16: &cron-jdk16 + if: type = cron AND repo = scala/scala + env: ADOPTOPENJDK=16 + + build-for-testing: &build-for-testing + # pull request validation (w/ bootstrap) + # differs from the build that publishes releases / integration builds: + # - not using bash script setup, but just the underlying sbt calls + # - publishing locally rather than to Artifactory + # the bootstrap above is older historically; this way of doing it is newer + # and also simpler. we should aim to reduce/eliminate the duplication. + stage: build + name: build, publishLocal, build again + script: + - set -e + - sbt setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal + - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR + - sbt -Dstarr.version=$STARR setupValidateTest compile + workspaces: + create: + name: bootstrapped + paths: + # so new STARR will be available + - "buildcharacter.properties" + - "$HOME/.ivy2/local/org.scala-lang" + # so build products built using new STARR are kept + - "target" + - "project/target" + - "project/project/target" + - "project/project/project/target" + - "dist" + - "build" + + test1: &test1 + stage: test + name: tests (junit, scalacheck, et al) + workspaces: + use: bootstrapped + script: + - set -e + - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR + - sbt -Dstarr.version=$STARR setupValidateTest Test/compile testAll1 + + test2: &test2 + stage: test + name: tests (partest) + workspaces: + use: bootstrapped + script: + - set -e + - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR + - sbt -Dstarr.version=$STARR setupValidateTest testAll2 + jobs: - include: - - stage: build - if: type != pull_request AND repo = scala/scala - name: bootstrap and publish - script: - # see comment in `bootstrap_fun` for details on the procedure - # env available in each stage - # - by travis config (see below): secret env vars - # - by `common` script: WORKSPACE, IVY2_DIR, SBT_CMD, integrationRepoUrl - # - by `bootstrap_fun`: publishPrivateTask, ... - - set -e - - (cd admin && ./init.sh) - - source scripts/common - - source scripts/bootstrap_fun - - determineScalaVersion - - removeExistingBuilds $integrationRepoUrl - - if [ ! -z "$STARR_REF" ]; then buildStarr; fi - - buildLocker - - buildQuick - - triggerScalaDist - - # pull request validation (w/ bootstrap) - # differs from the bootstrap above by: - # - not using bash script setup, but just the underlying sbt calls - # - publishing locally rather than to Artifactory - # the bootstrap above is older historically; this way of doing it is newer - # and also simpler. we should aim to reduce/eliminate the duplication. - - stage: build - name: build, publishLocal, build again - if: type = pull_request OR repo != scala/scala - script: - - set -e - - sbt setupPublishCore generateBuildCharacterPropertiesFile headerCheck publishLocal - - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt -Dstarr.version=$STARR setupValidateTest compile - workspaces: - create: - name: bootstrapped - paths: - # so new STARR will be available - - "buildcharacter.properties" - - "$HOME/.ivy2/local/org.scala-lang" - # so build products built using new STARR are kept - - "target" - - "project/target" - - "project/project/target" - - "project/project/project/target" - - "dist" - - "build" - - - stage: test - name: tests (junit, scalacheck, et al) - if: type = pull_request OR repo != scala/scala - workspaces: - use: bootstrapped - script: - - set -e - - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt -Dstarr.version=$STARR setupValidateTest Test/compile testAll1 - - - name: tests (partest) - if: type = pull_request OR repo != scala/scala - workspaces: - use: bootstrapped - script: - - set -e - - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt -Dstarr.version=$STARR setupValidateTest testAll2 - - - name: ensure standard library is buildable by Scala 3 - if: type = pull_request OR repo != scala/scala - workspaces: - use: bootstrapped - script: - - set -e - - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt -Dscala.build.compileWithDotty=true library/compile - - - stage: test - name: build benchmarks (bootstrapped) - if: type = pull_request OR repo != scala/scala - workspaces: - use: bootstrapped - script: - - set -e - - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR - - sbt bench/Jmh/compile - - - stage: build - name: language spec (Jekyll) - language: ruby - install: - - ruby -v - - gem install bundler - - bundler --version - - bundle install - # cribbed from https://github.com/SebastiaanKlippert/go-wkhtmltopdf/blob/master/.travis.yml - - sudo apt-get update - - sudo apt-get install -y build-essential xorg xfonts-75dpi libpng16-16 libssl1.1 - - wget --quiet "https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.bionic_amd64.deb" - - sudo dpkg -i "wkhtmltox_0.12.6-1.bionic_amd64.deb" - - rm "wkhtmltox_0.12.6-1.bionic_amd64.deb" - script: - - set -e - - (cd admin && ./init.sh) - - bundle exec jekyll build -s spec/ -d build/spec - - export JEKYLL_ENV=spec-pdf - - bundle exec jekyll build -s spec/ -d build/spec-pdf - - ./scripts/generate-spec-pdf.sh - after_success: - - ./scripts/travis-publish-spec.sh + include: + - stage: build + if: (type = push OR type = api) AND repo = scala/scala # api for manually triggered release builds + name: publish (bootstrapped) to scala-integration or sonatype + script: + # see comment in `bootstrap_fun` for details on the procedure + # env available in each stage + # - by travis config (see below): secret env vars + # - by `common` script: WORKSPACE, IVY2_DIR, SBT_CMD, integrationRepoUrl + # - by `bootstrap_fun`: publishPrivateTask, ... + - set -e + - (cd admin && ./init.sh) + - source scripts/common + - source scripts/bootstrap_fun + - determineScalaVersion + - removeExistingBuilds $integrationRepoUrl + - if [ ! -z "$STARR_REF" ]; then buildStarr; fi + - buildLocker + - buildQuick + - triggerScalaDist + + - <<: *build-for-testing + <<: *pr-jdk8 + + - <<: *test1 + <<: *pr-jdk8 + + - <<: *test2 + <<: *pr-jdk8 + + - <<: *build-for-testing + <<: *cron-jdk16 + + - <<: *test1 + <<: *cron-jdk16 + + - <<: *test2 + <<: *cron-jdk16 + + - stage: test + name: build library with Scala 3 + if: type = pull_request OR repo != scala/scala + workspaces: + use: bootstrapped + script: + - set -e + - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR + - sbt -Dscala.build.compileWithDotty=true library/compile + + - name: build benchmarks + if: type = pull_request OR repo != scala/scala + workspaces: + use: bootstrapped + script: + - set -e + - STARR=$(sed -n 's/^maven\.version\.number=//p' buildcharacter.properties) && echo $STARR + - sbt bench/Jmh/compile + + - stage: build + if: type = pull_request OR type = push + name: language spec + # wkhtmltopdf requires libssl1.1, which we can't install on xenial + dist: bionic + language: ruby + install: + - ruby -v + - gem install bundler + - bundler --version + - bundle install + # cribbed from https://github.com/SebastiaanKlippert/go-wkhtmltopdf/blob/master/.travis.yml + - sudo apt-get update + - sudo apt-get install -y build-essential xorg xfonts-75dpi libpng16-16 libssl1.1 + - wget --quiet "https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.bionic_amd64.deb" + - sudo dpkg -i "wkhtmltox_0.12.6-1.bionic_amd64.deb" + - rm "wkhtmltox_0.12.6-1.bionic_amd64.deb" + script: + - set -e + - (cd admin && ./init.sh) + - bundle exec jekyll build -s spec/ -d build/spec + - export JEKYLL_ENV=spec-pdf + - bundle exec jekyll build -s spec/ -d build/spec-pdf + - ./scripts/generate-spec-pdf.sh + after_success: + - ./scripts/travis-publish-spec.sh env: global: @@ -138,4 +167,10 @@ cache: - $HOME/.rvm notifications: + slack: + rooms: + - typesafe:WoewGgHil2FkdGzJyV3phAhj + if: (type = cron OR type = push) AND repo = scala/scala + on_success: never + on_failure: change webhooks: https://scala-ci.typesafe.com/benchq/webhooks/travis diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78db0a59d6d8..59c9675e690d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -115,8 +115,25 @@ To run a single negative test from sbt shell: root> partest --verbose test/files/neg/delayed-init-ref.scala ``` -To specify compiler flags such as `-Werror -Xlint`, you can add a comment -at the top of your source file of the form: `// scalac: -Werror -Xlint`. +A test can be either a single `.scala` file or a directory containing multiple `.scala` and `.java` files. +For testing separate compilation, files can be grouped using `_N` suffixes in the filename. For example, a test +with files (`A.scala`, `B_1.scala`, `C_1.java`, `Test_2.scala`) does: +``` +scalac A.scala -d out +scalac -cp out B_1.scala C_1.java -d out +javac -cp out C_1.java -d out +scalac -cp out Test_2.scala -d out +scala -cp out Test +``` + +**Flags** + - To specify compiler flags such as `-Werror -Xlint`, you can add a comment at the top of your source file of the form: `// scalac: -Werror -Xlint`. + - Similarly, a `// javac: ` comment in a Java source file passes flags to the Java compiler. + - A `// filter: ` comment eliminates output lines that match the filter before comparing to the `.check` file. + - A `// java: ` comment makes a `run` test execute in a separate JVM and passes the additional flags to the `java` command. + - A `// javaVersion ` comment makes partest skip the test if the java version is outside the requested range (e.g. `8`, `15+`, `9 - 11`) + +**Common Usage** To test that no warnings are emitted while compiling a `pos` test, use `-Werror`. That will fail a `pos` test if there are warnings. Note that `pos` tests do not have `.check` files. @@ -171,7 +188,7 @@ See `--help` for more info: root> partest --help ``` -Partests are compiled by the `quick` compiler (and `run` partests executed with the `quick` library), +Partests are compiled by the bootstrapped `quick` compiler (and `run` partests executed with the `quick` library), and therefore: * if you're working on the compiler, you must write a partest, or a `BytecodeTesting` JUnit test which invokes the compiler programmatically; however diff --git a/NOTICE b/NOTICE index ac3a26b40f48..ba6f890b920f 100644 --- a/NOTICE +++ b/NOTICE @@ -1,6 +1,6 @@ Scala -Copyright (c) 2002-2020 EPFL -Copyright (c) 2011-2020 Lightbend, Inc. +Copyright (c) 2002-2021 EPFL +Copyright (c) 2011-2021 Lightbend, Inc. Scala includes software developed at LAMP/EPFL (https://lamp.epfl.ch/) and diff --git a/README.md b/README.md index 63f3edafc6bd..1fefc3f11305 100644 --- a/README.md +++ b/README.md @@ -152,8 +152,12 @@ distribution to your local artifact repository and then switch sbt to use that version as its new `scalaVersion`. You may then revert back with `reload`. Note `restarrFull` will also write the STARR version to `buildcharacter.properties` so you can switch back to it with -`restarr` without republishing (though incremental compilation will -recompile from scratch, sadly.) +`restarr` without republishing. This will switch the sbt session to +use the `build-restarr` and `target-restarr` directories instead of +`build` and `target`, which avoids wiping out classfiles and +incremental metadata. IntelliJ will continue to be configured to +compile and run tests using the starr version in +`versions.properties`. For history on how the current scheme was arrived at, see https://groups.google.com/d/topic/scala-internals/gp5JsM1E0Fo/discussion. diff --git a/build.sbt b/build.sbt index 2a50ba4111d6..ff6183de1c8b 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ * * What you see below is very much work-in-progress. The following features are implemented: * - Compiling all classes for the compiler and library ("compile" in the respective subprojects) - * - Running JUnit ("junit/test"), ScalaCheck ("scalacheck/test"), and partest ("test/it:test") tests + * - Running JUnit ("junit/test"), ScalaCheck ("scalacheck/test"), and partest ("test/IntegrationTest/test") tests * - Creating build/quick with all compiled classes and launcher scripts ("dist/mkQuick") * - Creating build/pack with all JARs and launcher scripts ("dist/mkPack") * - Building all scaladoc sets ("doc") @@ -37,7 +37,7 @@ import scala.build._, VersionUtil._ // Non-Scala dependencies: val junitDep = "junit" % "junit" % "4.13.2" val junitInterfaceDep = "com.novocode" % "junit-interface" % "0.11" % Test -val scalacheckDep = "org.scalacheck" %% "scalacheck" % "1.15.3" % Test +val scalacheckDep = "org.scalacheck" %% "scalacheck" % "1.15.4" % Test val jolDep = "org.openjdk.jol" % "jol-core" % "0.13" val asmDep = "org.scala-lang.modules" % "scala-asm" % versionProps("scala-asm.version") val jlineDep = "org.jline" % "jline" % versionProps("jline.version") @@ -70,7 +70,7 @@ lazy val publishSettings : Seq[Setting[_]] = Seq( // should not be set directly. It is the same as the Maven version and derived automatically from `baseVersion` and // `baseVersionSuffix`. globalVersionSettings -Global / baseVersion := "2.13.5" +Global / baseVersion := "2.13.7" Global / baseVersionSuffix := "SNAPSHOT" ThisBuild / organization := "org.scala-lang" ThisBuild / homepage := Some(url("https://www.scala-lang.org")) @@ -112,7 +112,7 @@ lazy val instanceSettings = Seq[Setting[_]]( // We create a managed copy to prevent sbt from putting it on the classpath where we don't want it if(s.isManagedVersion) s else { import sbt.internal.inc.ScalaInstance - val s2 = new ScalaInstance(s.version, s.loader, s.loaderLibraryOnly, s.libraryJars, s.compilerJar, s.allJars, Some(s.actualVersion)) + val s2 = new ScalaInstance(s.version, s.loader, s.loaderCompilerOnly, s.loaderLibraryOnly, s.libraryJars, s.compilerJars, s.allJars, Some(s.actualVersion)) assert(s2.isManagedVersion) s2 } @@ -141,7 +141,7 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories Global / excludeLintKeys ++= Set(scalaSource), // each subproject has to ask specifically for files they want to include Compile / unmanagedResources / includeFilter := NothingFilter, - target := (ThisBuild / baseDirectory).value / "target" / thisProject.value.id, + target := (ThisBuild / target).value / thisProject.value.id, Compile / classDirectory := buildDirectory.value / "quick/classes" / thisProject.value.id, Compile / doc / target := buildDirectory.value / "scaladoc" / thisProject.value.id, // given that classDirectory and doc target are overridden to be _outside_ of target directory, we have @@ -157,6 +157,7 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories // we don't want optimizer warnings to interfere with `-Werror`. we have hundreds of such warnings // when the optimizer is enabled (as it is in CI and release builds, though not in local development) Compile / scalacOptions += "-Wconf:cat=optimizer:is", + Compile / scalacOptions ++= Seq("-deprecation", "-feature"), Compile / doc / scalacOptions ++= Seq( "-doc-footer", "epfl", "-diagrams", @@ -487,6 +488,7 @@ lazy val compiler = configureAsSubproject(project) |org.jline.terminal.impl.jna.*;resolution:=optional |org.jline.terminal.spi;resolution:=optional |org.jline.utils;resolution:=optional + |org.jline.builtins;resolution:=optional |scala.*;version="$${range;[==,=+);$${ver}}" |*""".stripMargin.linesIterator.mkString(","), "Class-Path" -> "scala-reflect.jar scala-library.jar" @@ -615,7 +617,7 @@ lazy val tastytest = configureAsSubproject(project) .settings( name := "scala-tastytest", description := "Scala TASTy Integration Testing Tool", - libraryDependencies ++= List(diffUtilsDep, TastySupport.scala3Compiler), + libraryDependencies += diffUtilsDep, Compile / scalacOptions ++= Seq("-feature", "-Xlint"), ) @@ -663,12 +665,13 @@ lazy val bench = project.in(file("test") / "benchmarks") name := "test-benchmarks", autoScalaLibrary := false, crossPaths := true, // needed to enable per-scala-version source directories (https://github.com/sbt/sbt/pull/1799) + compileOrder := CompileOrder.JavaThenScala, // to allow inlining from Java ("... is defined in a Java source (mixed compilation), no bytecode is available") libraryDependencies += "org.openjdk.jol" % "jol-core" % "0.10", libraryDependencies ++= { if (benchmarkScalaVersion == "") Nil else "org.scala-lang" % "scala-compiler" % benchmarkScalaVersion :: Nil }, - scalacOptions ++= Seq("-feature", "-opt:l:inline", "-opt-inline-from:scala.**") + scalacOptions ++= Seq("-feature", "-opt:l:inline", "-opt-inline-from:scala/**", "-opt-warnings"), ).settings(inConfig(JmhPlugin.JmhKeys.Jmh)(scalabuild.JitWatchFilePlugin.jitwatchSettings)) @@ -690,6 +693,12 @@ lazy val testkit = configureAsSubproject(project) ) ) +// Jigsaw: reflective access between modules (`setAccessible(true)`) requires an `opens` directive. +// This is enforced by error (not just by warning) since JDK 16. In our tests we use reflective access +// from the unnamed package (the classpath) to JDK modules in testing utilities like `assertNotReachable`. +// `add-exports=jdk.jdeps/com.sun.tools.javap` is tests that use `:javap` in the REPL, see scala/bug#12378 +val addOpensForTesting = "-XX:+IgnoreUnrecognizedVMOptions" +: "--add-exports=jdk.jdeps/com.sun.tools.javap=ALL-UNNAMED" +: + Seq("java.util.concurrent.atomic", "java.lang", "java.lang.reflect", "java.net").map(p => s"--add-opens=java.base/$p=ALL-UNNAMED") lazy val junit = project.in(file("test") / "junit") .dependsOn(testkit, compiler, replFrontend, scaladoc) @@ -699,7 +708,7 @@ lazy val junit = project.in(file("test") / "junit") .settings(publish / skip := true) .settings( Test / fork := true, - Test / javaOptions += "-Xss1M", + Test / javaOptions ++= "-Xss1M" +: addOpensForTesting, (Test / forkOptions) := (Test / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), (Test / testOnly / forkOptions) := (Test / testOnly / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), Compile / scalacOptions ++= Seq( @@ -710,7 +719,7 @@ lazy val junit = project.in(file("test") / "junit") ), Compile / javacOptions ++= Seq("-Xlint"), libraryDependencies ++= Seq(junitInterfaceDep, jolDep, diffUtilsDep), - testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"), + testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v", "-s"), Compile / unmanagedSourceDirectories := Nil, Test / unmanagedSourceDirectories := List(baseDirectory.value) ) @@ -722,7 +731,7 @@ lazy val tasty = project.in(file("test") / "tasty") .settings(publish / skip := true) .settings( Test / fork := true, - libraryDependencies += junitInterfaceDep, + libraryDependencies ++= Seq(junitInterfaceDep, TastySupport.scala3Library), testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"), Test / testOptions += Tests.Argument( s"-Dtastytest.src=${baseDirectory.value}", @@ -731,10 +740,32 @@ lazy val tasty = project.in(file("test") / "tasty") Compile / unmanagedSourceDirectories := Nil, Test / unmanagedSourceDirectories := List(baseDirectory.value/"test"), ) + .configs(TastySupport.CompilerClasspath, TastySupport.LibraryClasspath) + .settings( + inConfig(TastySupport.CompilerClasspath)(Defaults.configSettings), + inConfig(TastySupport.LibraryClasspath)(Defaults.configSettings), + libraryDependencies ++= Seq( + TastySupport.scala3Compiler % TastySupport.CompilerClasspath, + TastySupport.scala3Library % TastySupport.LibraryClasspath, + ), + javaOptions ++= { + import java.io.File.pathSeparator + val scalaLibrary = (library / Compile / classDirectory).value.getAbsoluteFile() + val scalaReflect = (reflect / Compile / classDirectory).value.getAbsoluteFile() + val dottyCompiler = (TastySupport.CompilerClasspath / managedClasspath).value.seq.map(_.data) :+ scalaLibrary + val dottyLibrary = (TastySupport.LibraryClasspath / managedClasspath).value.seq.map(_.data) :+ scalaLibrary + Seq( + s"-Dtastytest.classpaths.dottyCompiler=${dottyCompiler.mkString(pathSeparator)}", + s"-Dtastytest.classpaths.dottyLibrary=${dottyLibrary.mkString(pathSeparator)}", + s"-Dtastytest.classpaths.scalaReflect=$scalaReflect", + ) + }, + ) lazy val scalacheck = project.in(file("test") / "scalacheck") .dependsOn(library, reflect, compiler, scaladoc) .settings(commonSettings) + .settings(fatalWarningsSettings) .settings(disableDocs) .settings(publish / skip := true) .settings( @@ -742,12 +773,12 @@ lazy val scalacheck = project.in(file("test") / "scalacheck") Test / fork := true, // Instead of forking above, it should be possible to set: // Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat, - Test / javaOptions += "-Xss1M", + Test / javaOptions ++= "-Xss1M" +: addOpensForTesting, Test / testOptions += Tests.Argument( // Full stack trace on failure: "-verbosity", "2" ), - libraryDependencies ++= Seq(scalacheckDep), + libraryDependencies ++= Seq(scalacheckDep, junitDep), Compile / unmanagedSourceDirectories := Nil, Test / unmanagedSourceDirectories := List(baseDirectory.value) ) @@ -787,7 +818,7 @@ def osgiTestProject(p: Project, framework: ModuleID) = p Test / Keys.test := (Test / Keys.test).dependsOn(Compile / packageBin).value, Test / Keys.testOnly := (Test / Keys.testOnly).dependsOn(Compile / packageBin).evaluated, testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v", "-q"), - Test / javaOptions += "-Dscala.bundle.dir=" + (ThisBuild / buildDirectory).value / "osgi", + Test / javaOptions ++= ("-Dscala.bundle.dir=" + (ThisBuild / buildDirectory).value / "osgi") +: addOpensForTesting, Test / Keys.test / forkOptions := (Test / Keys.test / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), Test / unmanagedSourceDirectories := List((ThisBuild / baseDirectory).value / "test" / "osgi" / "src"), Compile / unmanagedResourceDirectories := (Test / unmanagedSourceDirectories).value, @@ -805,6 +836,7 @@ def osgiTestProject(p: Project, framework: ModuleID) = p lazy val partestJavaAgent = Project("partestJavaAgent", file(".") / "src" / "partest-javaagent") .settings(commonSettings) + .settings(fatalWarningsSettings) .settings(generatePropertiesFileSettings) .settings(disableDocs) .settings( @@ -838,10 +870,10 @@ lazy val test = project IntegrationTest / sources := Nil, IntegrationTest / fork := true, Compile / scalacOptions += "-Yvalidate-pos:parser,typer", - IntegrationTest / javaOptions ++= List("-Xmx2G", "-Dpartest.exec.in.process=true", "-Dfile.encoding=UTF-8", "-Duser.language=en", "-Duser.country=US"), + IntegrationTest / javaOptions ++= List("-Xmx2G", "-Dpartest.exec.in.process=true", "-Dfile.encoding=UTF-8", "-Duser.language=en", "-Duser.country=US") ++ addOpensForTesting, IntegrationTest / testOptions += Tests.Argument("-Dfile.encoding=UTF-8", "-Duser.language=en", "-Duser.country=US"), testFrameworks += new TestFramework("scala.tools.partest.sbt.Framework"), - IntegrationTest / testOptions += Tests.Argument("-Dpartest.java_opts=-Xmx1024M -Xms64M"), + IntegrationTest / testOptions += Tests.Argument(s"-Dpartest.java_opts=-Xmx1024M -Xms64M ${addOpensForTesting.mkString(" ")}"), IntegrationTest / testOptions += Tests.Argument("-Dpartest.scalac_opts=" + (Compile / scalacOptions).value.mkString(" ")), (IntegrationTest / forkOptions) := (IntegrationTest / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), IntegrationTest / testOptions += { @@ -879,6 +911,7 @@ lazy val test = project lazy val manual = configureAsSubproject(project) .settings(disableDocs) .settings(publish / skip := true) + .settings(fatalWarningsSettings) .settings( libraryDependencies += "org.scala-lang" % "scala-library" % scalaVersion.value, Compile / classDirectory := (Compile / target).value / "classes" @@ -1091,7 +1124,7 @@ lazy val dist = (project in file("dist")) (ThisBuild / buildDirectory).value / "quick" }.dependsOn((distDependencies.map(_ / Runtime / products) :+ mkBin): _*).value, mkPack := Def.task { (ThisBuild / buildDirectory).value / "pack" }.dependsOn(Compile / packageBin / packagedArtifact, mkBin).value, - target := (ThisBuild / baseDirectory).value / "target" / thisProject.value.id, + target := (ThisBuild / target).value / thisProject.value.id, Compile / packageBin := { val targetDir = (ThisBuild / buildDirectory).value / "pack" / "lib" val jlineJAR = findJar((Compile / dependencyClasspath).value, jlineDep).get.data @@ -1130,7 +1163,6 @@ def configureAsSubproject(project: Project, srcdir: Option[String] = None): Proj .settings(generatePropertiesFileSettings) } -lazy val buildDirectory = settingKey[File]("The directory where all build products go. By default ./build") lazy val mkBin = taskKey[Seq[File]]("Generate shell script (bash or Windows batch).") lazy val mkQuick = taskKey[File]("Generate a full build, including scripts, in build/quick") lazy val mkPack = taskKey[File]("Generate a full build, including scripts, in build/pack") @@ -1198,11 +1230,9 @@ def generateServiceProviderResources(services: (String, String)*): Setting[_] = } }.taskValue -ThisBuild / buildDirectory := (ThisBuild / baseDirectory).value / "build" - // Add tab completion to partest commands += Command("partest")(_ => PartestUtil.partestParser((ThisBuild / baseDirectory).value, (ThisBuild / baseDirectory).value / "test")) { (state, parsed) => - ("test/it:testOnly -- " + parsed) :: state + ("test/IntegrationTest/testOnly -- " + parsed) :: state } // Watch the test files also so ~partest triggers on test case changes diff --git a/doc/LICENSE.md b/doc/LICENSE.md index 83ef781d15f1..8a8e78738ff2 100644 --- a/doc/LICENSE.md +++ b/doc/LICENSE.md @@ -2,9 +2,9 @@ Scala is licensed under the [Apache License Version 2.0](https://www.apache.org/ ## Scala License -Copyright (c) 2002-2020 EPFL +Copyright (c) 2002-2021 EPFL -Copyright (c) 2011-2020 Lightbend, Inc. +Copyright (c) 2011-2021 Lightbend, Inc. All rights reserved. diff --git a/doc/License.rtf b/doc/License.rtf index 376ec02cb530..8f266ee71b4b 100644 --- a/doc/License.rtf +++ b/doc/License.rtf @@ -23,8 +23,8 @@ Scala is licensed under the\'a0{\field{\*\fldinst{HYPERLINK "https://www.apache. \fs48 \cf2 Scala License\ \pard\pardeftab720\sl360\sa320\partightenfactor0 -\f0\b0\fs28 \cf2 Copyright (c) 2002-2020 EPFL\ -Copyright (c) 2011-2020 Lightbend, Inc.\ +\f0\b0\fs28 \cf2 Copyright (c) 2002-2021 EPFL\ +Copyright (c) 2011-2021 Lightbend, Inc.\ All rights reserved.\ \pard\pardeftab720\sl360\sa320\partightenfactor0 \cf2 \cb4 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at {\field{\*\fldinst{HYPERLINK "http://www.apache.org/licenses/LICENSE-2.0"}}{\fldrslt http://www.apache.org/licenses/LICENSE-2.0}}.\ diff --git a/doc/README b/doc/README index 3361044f73d4..f7d3d44ab721 100644 --- a/doc/README +++ b/doc/README @@ -9,7 +9,7 @@ We welcome contributions at https://github.com/scala/scala! Scala Tools ----------- -- scala Scala interactive interpreter +- scala Scala REPL (interactive shell) - scalac Scala compiler - fsc Scala resident compiler - scaladoc Scala API documentation generator diff --git a/project/BuildSettings.scala b/project/BuildSettings.scala index 3cec68215323..5d4418a6fe0a 100644 --- a/project/BuildSettings.scala +++ b/project/BuildSettings.scala @@ -1,11 +1,20 @@ package scala.build -import sbt._ +import sbt._, Keys._ /** This object defines keys that should be visible with an unqualified name in all .sbt files and the command line */ object BuildSettings extends AutoPlugin { + override def trigger = allRequirements + object autoImport { lazy val baseVersion = settingKey[String]("The base version number from which all others are derived") lazy val baseVersionSuffix = settingKey[String]("Identifies the kind of version to build") + lazy val buildDirectory = settingKey[File]("The directory where all build products go. By default ./build") } + import autoImport._ + + override def buildSettings = Def.settings( + ThisBuild / target := (ThisBuild / baseDirectory).value / "target", + ThisBuild / buildDirectory := (ThisBuild / baseDirectory).value / "build", + ) } diff --git a/project/DottySupport.scala b/project/DottySupport.scala index d234470addbc..05f805f739cd 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,8 +12,12 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.0.0-RC1" // TASTy version 28.0.1 - val scala3Compiler = "org.scala-lang" % "scala3-compiler_3.0.0-RC1" % supportedTASTyRelease + val supportedTASTyRelease = "3.0.0" // TASTy version 28.0-0 + val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease + val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease + + val CompilerClasspath = Configuration.of("TastySupport.CompilerClasspath", "TastySupport.CompilerClasspath") + val LibraryClasspath = Configuration.of("TastySupport.LibraryClasspath", "TastySupport.LibraryClasspath") } /** Settings needed to compile with Dotty, @@ -22,7 +26,7 @@ object TastySupport { * Dotty in .travis.yml. */ object DottySupport { - val dottyVersion = "3.0.0-RC1" + val dottyVersion = "3.0.0" val compileWithDotty: Boolean = Option(System.getProperty("scala.build.compileWithDotty")).map(_.toBoolean).getOrElse(false) lazy val commonSettings = Seq( diff --git a/project/JitWatch.scala b/project/JitWatch.scala index 8bd483cc618f..d14c43765102 100644 --- a/project/JitWatch.scala +++ b/project/JitWatch.scala @@ -34,14 +34,14 @@ object JitWatchFilePlugin extends AutoPlugin { // Transitive sources from the projects that contribute to this classpath. val projects: Seq[ProjectRef] = buildDependencies.value.classpathTransitiveRefs(thisProjectRef.value) :+ thisProjectRef.value - val projectArtifacts: Map[ProjectRef, Seq[Artifact]] = projects.map(project => (project -> (Keys.artifacts in project get settingsData.value).getOrElse(Nil))).toMap - val artifactNameToProject: Map[String, Seq[ProjectRef]] = projects.groupBy(project => (Keys.name in project get settingsData.value).getOrElse("")) + val projectArtifacts: Map[ProjectRef, Seq[Artifact]] = projects.map(project => (project -> (project / Keys.artifacts get settingsData.value).getOrElse(Nil))).toMap + val artifactNameToProject: Map[String, Seq[ProjectRef]] = projects.groupBy(project => (project / Keys.name get settingsData.value).getOrElse("")) val transitiveSourceDirectories = projects.flatMap { project => - val projectArtifacts: Seq[Artifact] = (Keys.artifacts in project get settingsData.value).getOrElse(Nil) + val projectArtifacts: Seq[Artifact] = (project / Keys.artifacts get settingsData.value).getOrElse(Nil) val matching = projectArtifacts.filter(artifacts.contains(_)) val configs = matching.flatMap(artifact => artifact.configurations).distinct val sourceDirectories: Seq[File] = configs.flatMap { configRef => - (Keys.sourceDirectories in project in sbt.Configuration.of(configRef.name.capitalize, configRef.name)).get(settingsData.value).toList.flatten + (project / sbt.Configuration.of(configRef.name.capitalize, configRef.name) / Keys.sourceDirectories).get(settingsData.value).toList.flatten } sourceDirectories }.distinct @@ -50,7 +50,7 @@ object JitWatchFilePlugin extends AutoPlugin { projects.flatMap { project: ProjectRef => val configs = artifact.configurations val sourceDirectories: Seq[File] = configs.toList.flatMap { configRef => - (Keys.sourceDirectories in project in sbt.Configuration.of(configRef.name.capitalize, configRef.name)).get(settingsData.value).toList.flatten + (project / sbt.Configuration.of(configRef.name.capitalize, configRef.name) / Keys.sourceDirectories).get(settingsData.value).toList.flatten } sourceDirectories } diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index 722642074c1b..ad847e7b0a31 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -13,11 +13,11 @@ object MimaFilters extends AutoPlugin { import autoImport._ override val globalSettings = Seq( - mimaReferenceVersion := Some("2.13.4"), + mimaReferenceVersion := Some("2.13.6"), ) val mimaFilters: Seq[ProblemFilter] = Seq[ProblemFilter]( - // KEEP: we don't the reflect internal API isn't public API + // KEEP: the reflect internal API isn't public API ProblemFilters.exclude[Problem]("scala.reflect.internal.*"), // KEEP: java.util.Enumeration.asIterator only exists in later JDK versions (11 at least). If you build @@ -25,11 +25,14 @@ object MimaFilters extends AutoPlugin { // don't publish the artifact built with JDK 11 anyways ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.convert.JavaCollectionWrappers#IteratorWrapper.asIterator"), - // #9425 Node is private[collection] - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.mutable.HashMap#Node.foreachEntry"), + // KEEP: when building on a recent JDK, classes implementing `CharSequence` get a mixin forwarder for + // the `isEmpty` default method that was added in JDK 15 + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.Predef#SeqCharSequence.isEmpty"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.Predef#ArrayCharSequence.isEmpty"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.ArrayCharSequence.isEmpty"), - // #9487 - ProblemFilters.exclude[MissingClassProblem]("scala.reflect.ClassTag$cache$"), + // #8835 + ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.reflect.runtime.SynchronizedOps#SynchronizedBaseTypeSeq.scala$reflect$runtime$SynchronizedOps$SynchronizedBaseTypeSeq$$super$maxDepthOfElems"), ) override val buildSettings = Seq( diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index 7243fa9f631d..e3149a39c048 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -82,7 +82,7 @@ object ScalaOptionParser { } // TODO retrieve these data programmatically, ala https://github.com/scala/scala-tool-support/blob/master/bash-completion/src/main/scala/BashCompletion.scala - private def booleanSettingNames = List("-X", "-Xasync", "-Xcheckinit", "-Xdev", "-Xdisable-assertions", "-Xexperimental", "-Xfatal-warnings", "-Xlog-free-terms", "-Xlog-free-types", "-Xlog-implicit-conversions", "-Xlog-implicits", "-Xlog-reflective-calls", + private def booleanSettingNames = List("-X", "-Xasync", "-Xcheckinit", "-Xdev", "-Xdisable-assertions", "-Xexperimental", "-Xfatal-warnings", "-Xlog-free-terms", "-Xlog-free-types", "-Xlog-implicit-conversions", "-Xlog-reflective-calls", "-Xno-forwarders", "-Xno-patmat-analysis", "-Xnon-strict-patmat-analysis", "-Xprint-pos", "-Xprint-types", "-Xprompt", "-Xresident", "-Xshow-phases", "-Xverify", "-Y", "-Ybreak-cycles", "-Ydebug", "-Ycompact-trees", "-YdisableFlatCpCaching", "-Ydoc-debug", "-Yide-debug", @@ -97,7 +97,7 @@ object ScalaOptionParser { "-Vhot-statistics", "-Vide", "-Vimplicit-conversions", "-Vimplicits", "-Vissue", "-Vmacro", "-Vmacro-lite", "-Vpatmat", "-Vphases", "-Vpos", "-Vprint-pos", "-Vprint-types", "-Vquasiquote", "-Vreflective-calls", "-Vreify", - "-Vshow-member-pos", "-Vshow-symkinds", "-Vshow-symowners", "-Vsymbols", "-Vtyper", + "-Vshow-member-pos", "-Vshow-symkinds", "-Vshow-symowners", "-Vsymbols", "-Vtype-diffs", "-Vtyper", "-W", "-Wdead-code", "-Werror", "-Wextra-implicit", "-Wnumeric-widen", "-Woctal-literal", "-Wvalue-discard", "-Wself-implicit", @@ -105,8 +105,8 @@ object ScalaOptionParser { private def stringSettingNames = List("-Xjline", "-Xgenerate-phase-graph", "-Xmain-class", "-Xpluginsdir", "-Xshow-class", "-Xshow-object", "-Vshow-object", "-Xsource-reader", "-Ydump-classes", "-Ygen-asmp", "-Ypresentation-log", "-Ypresentation-replay", "-Yrepl-outdir", "-d", "-dependencyfile", "-encoding", "-Xscript", "-Vinline", "-Vopt", "-Vshow-class", "-Vshow-member-pos") private def pathSettingNames = List("-bootclasspath", "-classpath", "-extdirs", "-javabootclasspath", "-javaextdirs", "-sourcepath", "-toolcp", "-Vprint-args") - private val phases = List("all", "parser", "namer", "packageobjects", "typer", "patmat", "superaccessors", "extmethods", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "posterasure", "fields", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "delambdafy", "icode", "jvm", "terminal") - private val phaseSettings = List("-Xprint-icode", "-Ystop-after", "-Yskip", "-Yshow", "-Ystop-before", + private val phases = List("all", "parser", "namer", "packageobjects", "typer", "patmat", "superaccessors", "extmethods", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "posterasure", "fields", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "delambdafy", "jvm", "terminal") + private val phaseSettings = List("-Ystop-after", "-Yskip", "-Yshow", "-Ystop-before", "-Ybrowse", "-Ylog", "-Ycheck", "-Xprint", "-Yvalidate-pos", "-Vbrowse", "-Vlog", "-Vprint", "-Vshow") private def multiStringSettingNames = List("-Xmacro-settings", "-Xplugin", "-Xplugin-disable", "-Xplugin-require", "-Ywarn-unused", "-opt-inline-from") private def intSettingNames = List("-Xelide-below", "-Ypatmat-exhaust-depth", "-Ypresentation-delay", "-Yrecursion") diff --git a/project/ScriptCommands.scala b/project/ScriptCommands.scala index 156a40dbd72a..82cc51f38561 100644 --- a/project/ScriptCommands.scala +++ b/project/ScriptCommands.scala @@ -4,6 +4,7 @@ import java.nio.file.Paths import sbt._ import Keys._ +import sbt.complete.Parser._ import sbt.complete.Parsers._ import BuildSettings.autoImport._ @@ -26,7 +27,7 @@ object ScriptCommands { * The optional argument is the Artifactory snapshot repository URL. */ def setupPublishCoreNonOpt = setup("setupPublishCoreNonOpt") { args => Seq( - baseVersionSuffix in Global := "SHA-SNAPSHOT" + Global / baseVersionSuffix := "SHA-SNAPSHOT" ) ++ (args match { case Seq(url) => publishTarget(url) case Nil => Nil @@ -37,7 +38,7 @@ object ScriptCommands { * The optional argument is the Artifactory snapshot repository URL. */ def setupPublishCore = setup("setupPublishCore") { args => Seq( - baseVersionSuffix in Global := "SHA-SNAPSHOT" + Global / baseVersionSuffix := "SHA-SNAPSHOT" ) ++ (args match { case Seq(url) => publishTarget(url) case Nil => Nil @@ -48,9 +49,9 @@ object ScriptCommands { * The optional argument is the Artifactory snapshot repository URL. */ def setupValidateTest = setup("setupValidateTest") { args => Seq( - testOptions in IntegrationTest in LocalProject("test") ++= Seq(Tests.Argument("--show-log"), Tests.Argument("--show-diff")) + LocalProject("test") / IntegrationTest / testOptions ++= Seq(Tests.Argument("--show-log"), Tests.Argument("--show-diff")) ) ++ (args match { - case Seq(url) => Seq(resolvers in Global += "scala-pr" at url) + case Seq(url) => Seq(Global / resolvers += "scala-pr" at url) case Nil => Nil }) ++ enableOptimizer } @@ -61,8 +62,8 @@ object ScriptCommands { def setupBootstrapStarr = setup("setupBootstrapStarr") { case Seq(fileOrUrl, ver) => val url = fileToUrl(fileOrUrl) Seq( - baseVersion in Global := ver, - baseVersionSuffix in Global := "SPLIT" + Global / baseVersion := ver, + Global / baseVersionSuffix := "SPLIT" ) ++ publishTarget(url) ++ noDocs ++ enableOptimizer } @@ -72,9 +73,9 @@ object ScriptCommands { def setupBootstrapLocker = setup("setupBootstrapLocker") { case Seq(fileOrUrl, ver) => val url = fileToUrl(fileOrUrl) Seq( - baseVersion in Global := ver, - baseVersionSuffix in Global := "SPLIT", - resolvers in Global += "scala-pr" at url + Global / baseVersion := ver, + Global / baseVersionSuffix := "SPLIT", + Global / resolvers += "scala-pr" at url ) ++ publishTarget(url) ++ noDocs ++ enableOptimizer } @@ -88,10 +89,10 @@ object ScriptCommands { val targetUrl = fileToUrl(targetFileOrUrl) val resolverUrl = fileToUrl(resolverFileOrUrl) Seq( - baseVersion in Global := ver, - baseVersionSuffix in Global := "SPLIT", - resolvers in Global += "scala-pr" at resolverUrl, - testOptions in IntegrationTest in LocalProject("test") ++= Seq(Tests.Argument("--show-log"), Tests.Argument("--show-diff")) + Global / baseVersion := ver, + Global / baseVersionSuffix := "SPLIT", + Global / resolvers += "scala-pr" at resolverUrl, + LocalProject("test") / IntegrationTest / testOptions ++= Seq(Tests.Argument("--show-log"), Tests.Argument("--show-diff")) ) ++ publishTarget(targetUrl) ++ enableOptimizer } @@ -102,11 +103,11 @@ object ScriptCommands { def setupBootstrapPublish = setup("setupBootstrapPublish") { case Seq(fileOrUrl, ver) => val url = fileToUrl(fileOrUrl) Seq( - baseVersion in Global := ver, - baseVersionSuffix in Global := "SPLIT", - resolvers in Global += "scala-pr" at url, - publishTo in Global := Some("sonatype-releases" at "https://oss.sonatype.org/service/local/staging/deploy/maven2"), - credentials in Global += Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", env("SONA_USER"), env("SONA_PASS")) + Global / baseVersion := ver, + Global / baseVersionSuffix := "SPLIT", + Global / resolvers += "scala-pr" at url, + Global / publishTo := Some("sonatype-releases" at "https://oss.sonatype.org/service/local/staging/deploy/maven2"), + Global / credentials += Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", env("SONA_USER"), env("SONA_PASS")) // pgpSigningKey and pgpPassphrase are set externally by travis / the bootstrap script, as the sbt-pgp plugin is not enabled by default ) ++ enableOptimizer } @@ -115,12 +116,17 @@ object ScriptCommands { /** For local dev: sets `scalaVersion` to the version in `/buildcharacter.properties` or the given arg. * Running `reload` will re-read the build files, resetting `scalaVersion`. */ - def restarr = Command("restarr")(_ => (Space ~> StringBasic).?) { (state, s) => - val newVersion = s.getOrElse(readVersionFromPropsFile(state)) - val x = Project.extract(state) - val sv = x.get(Global / scalaVersion) - state.log.info(s"Re-STARR'ing: setting scalaVersion from $sv to $newVersion (`reload` to undo)") - x.appendWithSession(Global / scalaVersion := newVersion, state) // don't use version.value or it'll be a wrong, new value + def restarr = Command("restarr")(_ => (Space ~> token(StringBasic, "scalaVersion")).?) { (state, argSv) => + val x = Project.extract(state) + val oldSv = x.get(Global / scalaVersion) + val newSv = argSv.getOrElse(readVersionFromPropsFile(state)) + state.log.info(s"Re-STARR'ing: setting scalaVersion from $oldSv to $newSv (`reload` to undo; IntelliJ still uses $oldSv)") + val settings = Def.settings( + Global / scalaVersion := newSv, // don't use version.value or it'll be a wrong, new value + ThisBuild / target := (ThisBuild / baseDirectory).value / "target-restarr", + ThisBuild / buildDirectory := (ThisBuild / baseDirectory).value / "build-restarr", + ) + x.appendWithSession(settings, state) } /** For local dev: publishes locally (without optimizing) & then sets the new `scalaVersion`. @@ -134,7 +140,10 @@ object ScriptCommands { } private def readVersionFromPropsFile(state: State): String = { - val props = readProps(file("buildcharacter.properties")) + val propsFile = file("buildcharacter.properties") + if (!propsFile.exists()) + throw new MessageOnlyException("No buildcharacter.properties found - try restarrFull") + val props = readProps(propsFile) val newVersion = props("maven.version.number") val fullVersion = props("version.number") state.log.info(s"Read STARR version from buildcharacter.properties: $newVersion (full version: $fullVersion)") diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index 6b4e659cc7a2..ac5ea30b20cc 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -30,7 +30,7 @@ object VersionUtil { ) lazy val generatePropertiesFileSettings = Seq[Setting[_]]( - copyrightString := "Copyright 2002-2020, LAMP/EPFL and Lightbend, Inc.", + copyrightString := "Copyright 2002-2021, LAMP/EPFL and Lightbend, Inc.", shellBannerString := """ | ________ ___ / / ___ | / __/ __// _ | / / / _ | diff --git a/project/build.properties b/project/build.properties index 0b2e09c5ac99..67d27a1dfe00 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.4.7 +sbt.version=1.5.3 diff --git a/project/plugins.sbt b/project/plugins.sbt index 9294ca79ba70..f049bdae5c79 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -17,7 +17,7 @@ buildInfoKeys := Seq[BuildInfoKey](buildClasspath) buildInfoPackage := "scalabuild" -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.9.0") libraryDependencies ++= Seq( "org.eclipse.jgit" % "org.eclipse.jgit" % "4.6.0.201612231935-r", @@ -33,10 +33,4 @@ addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.4") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.0.0") -// See DottySupport.scala -if (Option(System.getProperty("scala.build.compileWithDotty")).map(_.toBoolean).getOrElse(false)) - Seq(addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.5.2")) -else - Seq() - addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.18") diff --git a/scripts/common b/scripts/common index a0c1e9af137e..8f6c3aa3bef8 100644 --- a/scripts/common +++ b/scripts/common @@ -11,14 +11,13 @@ else fi SBT_CMD=${SBT_CMD-sbt} -SBT_CMD="$SBT_CMD -sbt-version 1.4.7" +SBT_CMD="$SBT_CMD -sbt-version 1.5.3" # repo to publish builds integrationRepoUrl=${integrationRepoUrl-"https://scala-ci.typesafe.com/artifactory/scala-integration/"} # only used on jenkins sbtRepositoryConfig="$WORKSPACE/scripts/sbt-repositories-config" -jcenterCacheUrl=${jcenterCacheUrl-"https://scala-ci.typesafe.com/artifactory/jcenter/"} # used by `checkAvailability` TMP_ROOT_DIR=$(mktemp -d -t pr-scala.XXXX) @@ -92,11 +91,9 @@ function generateRepositoriesConfig() { fi cat >> "$sbtRepositoryConfig" << EOF - jcenter-cache: $jcenterCacheUrl local maven-central - typesafe-ivy-releases-boot: https://repo.lightbend.com/typesafe/ivy-releases/, [organisation]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext], bootOnly - typesafe-ivy-releases: https://dl.bintray.com/typesafe/ivy-releases/, [organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] + typesafe-ivy-releases: https://repo.lightbend.com/typesafe/ivy-releases/, [organisation]/[module]/[revision]/[type]s/[artifact](-[classifier]).[ext] sbt-plugin-releases: https://repo.scala-sbt.org/scalasbt/sbt-plugin-releases/, [organisation]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] EOF } diff --git a/scripts/jobs/integrate/ide b/scripts/jobs/integrate/ide deleted file mode 100755 index 1dc7b43139e8..000000000000 --- a/scripts/jobs/integrate/ide +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -e -# requires checkout: root is a scala checkout with which to integrate (actually, only required file is versions.properties, as documented below) -# requires env: scalaVersion (specifies binary already built from above checkout), WORKSPACE (provided by jenkins), repo_ref (HEAD of the scala checkout), -# requires files: $WORKSPACE/versions.properties (from checkout -- defines version numbers for modules used to build scala for dbuild...) - -echo "IDE integration not yet available on 2.12.x. Punting." -exit 0 - -# TODO: remove when integration is up and running -if [ "woele$_scabot_last" != "woele1" ]; then echo "Scabot didn't mark this as last commit -- skipping."; exit 0; fi - -baseDir=${WORKSPACE-`pwd`} -uberBuildUrl=${uberBuildUrl-"https://github.com/scala-ide/uber-build.git"} -uberBuildConfig=${uberBuildConfig-"validator.conf"} # TODO: backport to 2.10.x: uberBuildConfig="validator-2.10.conf" - -uberBuildDir="$WORKSPACE/uber-build/" - -cd $WORKSPACE -if [[ -d $uberBuildDir ]]; then - ( cd $uberBuildDir && git fetch $uberBuildUrl HEAD && git checkout -f FETCH_HEAD && git clean -fxd ) -else - git clone $uberBuildUrl -fi - -echo "maven.version.number=$scalaVersion" >> versions.properties - -# pass prRepoUrl in, which uber-build passes along to dbuild (in sbt-builds-for-ide) -# the "-P pr-scala" maven arg accomplishes the same thing for maven (directly used in uber-build) -BASEDIR="$WORKSPACE" prRepoUrl="$prRepoUrl" IDE_M2_REPO="$prRepoUrl" MAVEN_ARGS="-P pr-scala"\ - $uberBuildDir/uber-build.sh $uberBuildDir/config/$uberBuildConfig $repo_ref $scalaVersion - -# uber-build puts its local repo under target/m2repo -# wipe the org/scala-lang part, which otherwise just keeps -# growing and growing due to the -$sha-SNAPSHOT approach -[[ -d $WORKSPACE/target/m2repo/org/scala-lang ]] && rm -rf $WORKSPACE/target/m2repo/org/scala-lang diff --git a/scripts/jobs/integrate/windows b/scripts/jobs/integrate/windows deleted file mode 100755 index 964b70383c09..000000000000 --- a/scripts/jobs/integrate/windows +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -source scripts/common - -java -version -javac -version - -generateRepositoriesConfig - -# it may not be necessary to set both COURSIER_HOME and sbt.coursier.home, -# but at least for now, doing it just in case; see discussion at -# https://github.com/scala/scala-dev/issues/666 -export COURSIER_HOME=$WORKSPACE/.coursier - -SBT="java $JAVA_OPTS -Dsbt.ivy.home=$WORKSPACE/.ivy2 -Dsbt.coursier.home=$WORKSPACE/.coursier -jar $sbtLauncher -Dsbt.override.build.repos=true -Dsbt.repository.config=$sbtRepositoryConfig" - -# Build locker with STARR -$SBT -warn "setupPublishCore" generateBuildCharacterPropertiesFile publishLocal - -# Build quick and run the tests -parseScalaProperties buildcharacter.properties -$SBT -Dstarr.version=$maven_version_number -warn "setupValidateTest" testAll diff --git a/spec/01-lexical-syntax.md b/spec/01-lexical-syntax.md index e240ef372ffa..718950b171a1 100644 --- a/spec/01-lexical-syntax.md +++ b/spec/01-lexical-syntax.md @@ -463,7 +463,7 @@ arbitrary, except that it may contain three or more consecutive quote characters only at the very end. Characters must not necessarily be printable; newlines or other control characters are also permitted. [Escape sequences](#escape-sequences) are -not processed, except for Unicode escapes. +not processed, except for Unicode escapes (this is deprecated since 2.13.2). > ```scala > """the present string @@ -503,9 +503,11 @@ not processed, except for Unicode escapes. #### Interpolated string ```ebnf -interpolatedString ::= alphaid ‘"’ {printableChar \ (‘"’ | ‘$’) | escape} ‘"’ - | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ -escape ::= ‘$$’ +interpolatedString ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ +interpolatedStringPart ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape +escape ::= ‘$$’ + | ‘$"’ | ‘$’ id | ‘$’ BlockExpr alphaid ::= upper idrest @@ -513,22 +515,24 @@ alphaid ::= upper idrest ``` -Interpolated string consist of an identifier starting with a letter immediately +An interpolated string consists of an identifier starting with a letter immediately followed by a string literal. There may be no whitespace characters or comments -between the leading identifier and the opening quote ‘”’ of the string. -The string literal in a interpolated string can be standard (single quote) +between the leading identifier and the opening quote `"` of the string. +The string literal in an interpolated string can be standard (single quote) or multi-line (triple quote). -Inside a interpolated string none of the usual escape characters are interpreted -(except for unicode escapes) no matter whether the string literal is normal -(enclosed in single quotes) or multi-line (enclosed in triple quotes). -Instead, there are two new forms of dollar sign escape. +Inside an interpolated string none of the usual escape characters are interpreted +no matter whether the string literal is normal (enclosed in single quotes) or +multi-line (enclosed in triple quotes). Note that the sequence `\"` does not +close a normal string literal (enclosed in single quotes). + +There are three forms of dollar sign escape. The most general form encloses an expression in `${` and `}`, i.e. `${expr}`. The expression enclosed in the braces that follow the leading `$` character is of syntactical category BlockExpr. Hence, it can contain multiple statements, and newlines are significant. Single ‘$’-signs are not permitted in isolation -in a interpolated string. A single ‘$’-sign can still be obtained by doubling the ‘$’ -character: ‘$$’. +in an interpolated string. A single ‘$’-sign can still be obtained by doubling the ‘$’ +character: ‘$$’. A single ‘"’-sign can be obtained by the sequence ‘\$"’. The simpler form consists of a ‘$’-sign followed by an identifier starting with a letter and followed only by letters, digits, and underscore characters, diff --git a/spec/05-classes-and-objects.md b/spec/05-classes-and-objects.md index b297712c17ff..5c3a74e608a2 100644 --- a/spec/05-classes-and-objects.md +++ b/spec/05-classes-and-objects.md @@ -502,11 +502,14 @@ definition apply to all constituent definitions. The rules governing the validity and meaning of a modifier are as follows. ### `private` -The `private` modifier can be used with any definition or -declaration in a template. Such members can be accessed only from -within the directly enclosing template and its companion module or +The `private` modifier can be used with any definition or declaration in a +template. Private members of a template can be accessed only from within the +directly enclosing template and its companion module or [companion class](#object-definitions). +The `private` modifier is also valid for +[top-level](09-top-level-definitions.html#packagings) templates. + A `private` modifier can be _qualified_ with an identifier ´C´ (e.g. `private[´C´]`) that must denote a class or package enclosing the definition. Members labeled with such a modifier are accessible respectively only from code diff --git a/spec/06-expressions.md b/spec/06-expressions.md index 905fa5bf4925..da88cbaa5ae5 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -261,13 +261,13 @@ If ´f´ has some value type, the application is taken to be equivalent to `´f i.e. the application of an `apply` method defined by ´f´. The value `´f´` is applicable to the given arguments if `´f´.apply` is applicable. -Evaluation of `´f´(´e_1 , \ldots , e_n´)` usually entails evaluation of -´f´ and ´e_1 , \ldots , e_n´ in that order. Each argument expression -is converted to the type of its corresponding formal parameter. After -that, the application is rewritten to the function's right hand side, -with actual arguments substituted for formal parameters. The result -of evaluating the rewritten right-hand side is finally converted to -the function's declared result type, if one is given. +The application `´f´(´e_1 , \ldots , e_n´)` evaluates ´f´ and then each argument +´e_1 , \ldots , e_n´ from left to right, except for arguments that correspond to +a by-name parameter (see below). Each argument expression is converted to the +type of its corresponding formal parameter. After that, the application is +rewritten to the function's right hand side, with actual arguments substituted +for formal parameters. The result of evaluating the rewritten right-hand side +is finally converted to the function's declared result type, if one is given. The case of a formal parameter with a parameterless method type `=> ´T´` is treated specially. In this case, the @@ -295,6 +295,11 @@ must be the same). Furthermore, the type of ´e´ must conform to sequence ´e´ with its elements. When the application uses named arguments, the vararg parameter has to be specified exactly once. +If only a single argument is supplied, it may be supplied as a block expression +and parentheses can be omitted, in the form `´f´ { block }`. This is valid when +`f` has a single formal parameter or when all other formal parameters have +default values. + A function application usually allocates a new frame on the program's run-time stack. However, if a local method or a final method calls itself as its last action, the call is executed using the stack-frame @@ -400,13 +405,17 @@ The final result of the transformation is a block of the form For invocations of signature polymorphic methods of the target platform `´f´(´e_1 , \ldots , e_m´)`, the invoked method has a different method type `(´p_1´:´T_1 , \ldots , p_n´:´T_n´)´U´` at each call site. The parameter types `´T_ , \ldots , T_n´` are the types of the argument expressions -`´e_1 , \ldots , e_m´` and `´U´` is the expected type at the call site. If the expected type is -undefined then `´U´` is `scala.AnyRef`. The parameter names `´p_1 , \ldots , p_n´` are fresh. +`´e_1 , \ldots , e_m´`. If the declared return type `´R´` of the signature polymorphic method is +any type other than `scala.AnyRef`, then the return type `´U´` is `´R´`. +Otherwise, `´U´` is the expected type at the call site. If the expected type is undefined then +`´U´` is `scala.AnyRef`. The parameter names `´p_1 , \ldots , p_n´` are fresh. ###### Note -On the Java platform version 7 and later, the methods `invoke` and `invokeExact` in class -`java.lang.invoke.MethodHandle` are signature polymorphic. +On the Java platform version 11 and later, signature polymorphic methods are native, +members of `java.lang.invoke.MethodHandle` or `java.lang.invoke.VarHandle`, and have a single +repeated parameter of type `java.lang.Object*`. + ## Method Values @@ -586,6 +595,9 @@ Evaluation of the block entails evaluation of its statement sequence, followed by an evaluation of the final expression ´e´, which defines the result of the block. +A block expression `{´c_1´; ´\ldots´; ´c_n´; ´}` where ´s_1 , \ldots , s_n´ are +case clauses forms a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions). + ###### Example Assuming a class `Ref[T](x: T)`, the block @@ -1106,7 +1118,7 @@ Expr1 ::= ‘try’ Expr [‘catch’ Expr] [‘finally’ Expr] ``` A _try expression_ is of the form `try { ´b´ } catch ´h´` -where the handler ´h´ is a +where the handler ´h´ is usually a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions) ```scala @@ -1120,11 +1132,12 @@ handler ´h´ is applied to the thrown exception. If the handler contains a case matching the thrown exception, the first such case is invoked. If the handler contains no case matching the thrown exception, the exception is -re-thrown. +re-thrown. More generally, if the handler is a `PartialFunction`, +it is applied only if it is defined at the given exception. Let ´\mathit{pt}´ be the expected type of the try expression. The block ´b´ is expected to conform to ´\mathit{pt}´. The handler ´h´ -is expected conform to type `scala.PartialFunction[scala.Throwable, ´\mathit{pt}\,´]`. +is expected conform to type `scala.Function[scala.Throwable, ´\mathit{pt}\,´]`. The type of the try expression is the [weak least upper bound](03-types.html#weak-conformance) of the type of ´b´ and the result type of ´h´. diff --git a/spec/08-pattern-matching.md b/spec/08-pattern-matching.md index 0a2b222a3b06..7607b0db85e0 100644 --- a/spec/08-pattern-matching.md +++ b/spec/08-pattern-matching.md @@ -240,33 +240,38 @@ the pattern. An extractor pattern cannot match the value `null`. The implementation ensures that the `unapply`/`unapplySeq` method is not applied to `null`. +A type is said to be an _extractor type_ for some type `T` if it has a +method `get` with return type `T`, and a method `isEmpty` with a return type +that conforms to `Boolean`. `Option[T]` is an extractor type for type `T`. + An `unapply` method in an object ´x´ _matches_ the pattern ´x(p_1 , \ldots , p_n)´ if it has a single parameter (and, optionally, an implicit parameter list) and one of the following applies: -* ´n=0´ and `unapply`'s result type is `Boolean`. In this case +* ´n=0´ and `unapply`'s result type conforms to `Boolean`. In this case the extractor pattern matches all values ´v´ for which `´x´.unapply(´v´)` yields `true`. -* ´n=1´ and `unapply`'s result type is `Option[´T´]`, for some +* ´n=1´ and `unapply`'s result type is an extractor type for some type ´T´. In this case, the (only) argument pattern ´p_1´ is typed in turn with expected type ´T´. The extractor pattern matches then all - values ´v´ for which `´x´.unapply(´v´)` yields a value of form - `Some(´v_1´)`, and ´p_1´ matches ´v_1´. + values ´v´ for which `´x´.unapply(´v´)` yields a value ´u´ for which `´u´.isEmpty` yields + `false`, `´u´.get` yields a value ´v_1´, and ´p_1´ matches ´v_1´. * ´n>1´ and `unapply`'s result type is - `Option[(´T_1 , \ldots , T_n´)]`, for some + an extractor type for some type ´T´ with members ´\_1 , \ldots , \_n´ returning types ´T_1 , \ldots , T_n´. In this case, the argument patterns ´p_1 , \ldots , p_n´ are typed in turn with expected types ´T_1 , \ldots , T_n´. The extractor pattern matches then all values ´v´ for which - `´x´.unapply(´v´)` yields a value of form - `Some((´v_1 , \ldots , v_n´))`, and each pattern - ´p_i´ matches the corresponding value ´v_i´. + `´x´.unapply(´v´)` yields a value ´u´ for which + `´u´.isEmpty` yields `false`, `´u´.get` yields some value ´t´, and each pattern + ´p_i´ matches the corresponding value ´t._1´ from + ´t._1 , \ldots , t._n´. An `unapplySeq` method in an object ´x´ matches the pattern ´x(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´ if it takes exactly one argument and its result type is of the form `Option[(´T_1 , \ldots , T_m´, Seq[S])]` (if `m = 0`, the type `Option[Seq[S]]` is also accepted). This case is further discussed [below](#pattern-sequences). -###### Example +###### Example 1 If we define an extractor object `Pair`: @@ -288,6 +293,37 @@ val y = x match { } ``` +###### Example 2 + +If we define a class `NameBased` + +```scala +class NameBased[A, B](a: A, b: B) { + def isEmpty = false + def get = this + def _1 = a + def _2 = b +} +``` + +Then `NameBased` is an extractor type for `NameBased` itself, since it has a +member `isEmpty` returning a value of type Boolean, and it has a member `get` +returning a value of type `NameBased`. + +Since it also has members `_1` and `_2`, it can be used in an extractor pattern +with n = 2 as follows: + +```scala +object Extractor { + def unapply(x: Any) = new NameBased(1, "two") +} + +"anything" match { + case Extractor(a, b) => println(s"\$a, \$b") //prints "1, two" +} +``` + + ### Pattern Sequences ```ebnf @@ -297,7 +333,7 @@ SimplePattern ::= StableId ‘(’ [Patterns ‘,’] [varid ‘@’] ‘_’ A _pattern sequence_ ´p_1 , \ldots , p_n´ appears in two contexts. First, in a constructor pattern ´c(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´, where ´c´ is a case class which has ´m+1´ primary constructor parameters, ending in a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type `S*`. Second, in an extractor pattern ´x(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´ if the extractor object ´x´ does not have an `unapply` method, -but it does define an `unapplySeq` method with a result type conforming to `Option[(T_1, ... , T_m, Seq[S])]` (if `m = 0`, the type `Option[Seq[S]]` is also accepted). The expected type for the patterns ´p_i´ is ´S´. +but it does define an `unapplySeq` method with a result type that is an extractor type for type `(T_1, ... , T_m, Seq[S])` (if `m = 0`, an extractor type for the type `Seq[S]` is also accepted). The expected type for the patterns ´p_i´ is ´S´. The last pattern in a pattern sequence may be a _sequence wildcard_ `_*`. Each element pattern ´p_i´ is type-checked with @@ -366,6 +402,12 @@ A pattern ´p´ is _irrefutable_ for a type ´T´, if one of the following appli is an instance of class ´c´, the [primary constructor](05-classes-and-objects.html#class-definitions) of type ´T´ has argument types ´T_1 , \ldots , T_n´, and each ´p_i´ is irrefutable for ´T_i´. +1. ´p´ is an extractor pattern for which the extractor type is `Some[´T´]` for + some type ´T´ +1. ´p´ is an extractor pattern for which the extractor types `isEmpty` method + is the singleton type `false` +1. ´p´ is an extractor pattern for which the return type is the singleton type + `true` ## Type Patterns diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md index 837054f5a772..aec631beb45f 100644 --- a/spec/13-syntax-summary.md +++ b/spec/13-syntax-summary.md @@ -60,11 +60,14 @@ stringElement ::= charNoDoubleQuoteOrNewline | escapeSeq multiLineChars ::= {[‘"’] [‘"’] charNoDoubleQuote} {‘"’} -interpolatedString - ::= alphaid ‘"’ {printableChar \ (‘"’ | ‘\$’) | escape} ‘"’ +interpolatedString + ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘\$’) | escape} {‘"’} ‘"""’ -escape ::= ‘\$\$’ - | ‘\$’ id +interpolatedStringPart + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape +escape ::= ‘\$\$’ + | ‘\$"’ + | ‘\$’ id | ‘\$’ BlockExpr alphaid ::= upper idrest | varid diff --git a/src/compiler/scala/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/reflect/quasiquotes/Parsers.scala index c4b9d8f78bf8..eb1310ed0d80 100644 --- a/src/compiler/scala/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/reflect/quasiquotes/Parsers.scala @@ -99,9 +99,9 @@ trait Parsers { self: Quasiquotes => override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = FunctionTypePlaceholder(argtpes, restpe) // make q"val (x: T) = rhs" be equivalent to q"val x: T = rhs" for sake of bug compatibility (scala/bug#8211) - override def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = pat match { - case TuplePlaceholder(inParensPat :: Nil) => super.makePatDef(mods, inParensPat, rhs) - case _ => super.makePatDef(mods, pat, rhs) + override def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree, rhsPos: Position) = pat match { + case TuplePlaceholder(inParensPat :: Nil) => super.makePatDef(mods, inParensPat, rhs, rhsPos) + case _ => super.makePatDef(mods, pat, rhs, rhsPos) } } import treeBuilder.{global => _, unit => _} diff --git a/src/compiler/scala/reflect/reify/Taggers.scala b/src/compiler/scala/reflect/reify/Taggers.scala index 41eddd796c85..adff7a293503 100644 --- a/src/compiler/scala/reflect/reify/Taggers.scala +++ b/src/compiler/scala/reflect/reify/Taggers.scala @@ -102,8 +102,6 @@ abstract class Taggers { val tpe = tpeTree.tpe val PolyType(_, MethodType(_, tagTpe)) = fun.tpe: @unchecked val tagModule = tagTpe.typeSymbol.companionSymbol - if (c.compilerSettings.contains("-Xlog-implicits")) - c.echo(c.enclosingPosition, s"cannot materialize ${tagModule.name}[$tpe] as $result because:\n$reason") c.abort(c.enclosingPosition, "No %s available for %s".format(tagModule.name, tpe)) } diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 5601f96459cc..44c107f55dad 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -113,7 +113,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { else if (Yhelp) yusageMsg else if (showPlugins) global.pluginDescriptions else if (showPhases) global.phaseDescriptions + ( - if (debug) "\n" + global.phaseFlagDescriptions else "" + if (settings.isDebug) "\n" + global.phaseFlagDescriptions else "" ) else if (genPhaseGraph.isSetByUser) { val components = global.phaseNames // global.phaseDescriptors // one initializes diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index a80c5dbf4d51..bea3b0678099 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -282,7 +282,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) // ------------------ Debugging ------------------------------------- @inline final def ifDebug(body: => Unit): Unit = { - if (settings.debug) + if (settings.isDebug) body } @@ -313,7 +313,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) } @inline final override def debuglog(msg: => String): Unit = { - if (settings.debug) + if (settings.isDebug) log(msg) } @@ -417,7 +417,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) if ((unit ne null) && unit.exists) lastSeenSourceFile = unit.source - if (settings.debug && (settings.verbose || currentRun.size < 5)) + if (settings.isDebug && (settings.verbose || currentRun.size < 5)) inform("[running phase " + name + " on " + unit + "]") } @@ -713,7 +713,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) protected def computePhaseDescriptors: List[SubComponent] = { /* Allow phases to opt out of the phase assembly. */ def cullPhases(phases: List[SubComponent]) = { - val enabled = if (settings.debug && settings.isInfo) phases else phases filter (_.enabled) + val enabled = if (settings.isDebug && settings.isInfo) phases else phases filter (_.enabled) def isEnabled(q: String) = enabled exists (_.phaseName == q) val (satisfied, unhappy) = enabled partition (_.requires forall isEnabled) unhappy foreach (u => globalError(s"Phase '${u.phaseName}' requires: ${u.requires filterNot isEnabled}")) @@ -744,7 +744,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) } /** A description of the phases that will run in this configuration, or all if -Vdebug. */ - def phaseDescriptions: String = phaseHelp("description", elliptically = !settings.debug, phasesDescMap) + def phaseDescriptions: String = phaseHelp("description", elliptically = !settings.isDebug, phasesDescMap) /** Summary of the per-phase values of nextFlags and newFlags, shown under -Vphases -Vdebug. */ def phaseFlagDescriptions: String = { @@ -755,7 +755,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) else if (ph.phaseNewFlags != 0L && ph.phaseNextFlags != 0L) fstr1 + " " + fstr2 else fstr1 + fstr2 } - phaseHelp("new flags", elliptically = !settings.debug, fmt) + phaseHelp("new flags", elliptically = !settings.isDebug, fmt) } /** Emit a verbose phase table. @@ -1113,7 +1113,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) def echoPhaseSummary(ph: Phase) = { /* Only output a summary message under debug if we aren't echoing each file. */ - if (settings.debug && !(settings.verbose || currentRun.size < 5)) + if (settings.isDebug && !(settings.verbose || currentRun.size < 5)) inform("[running phase " + ph.name + " on " + currentRun.size + " compilation units]") } @@ -1154,9 +1154,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) keepPhaseStack = settings.log.isSetByUser // We hit these checks regularly. They shouldn't change inside the same run, so cache the comparisons here. - val isScala212: Boolean = settings.isScala212 - val isScala213: Boolean = settings.isScala213 - val isScala3: Boolean = settings.isScala3 + val isScala3 = settings.isScala3 // used in sbt def uncheckedWarnings: List[(Position, String)] = reporting.uncheckedWarnings @@ -1283,11 +1281,8 @@ class Global(var currentSettings: Settings, reporter0: Reporter) checkPhaseSettings(including = true, inclusions.toSeq: _*) checkPhaseSettings(including = false, exclusions map (_.value): _*) - // Enable or disable depending on the current setting -- useful for interactive behaviour - statistics.initFromSettings(settings) - // Report the overhead of statistics measurements per every run - if (statistics.areStatisticsLocallyEnabled) + if (settings.areStatisticsEnabled) statistics.reportStatisticsOverhead(reporter) phase = first //parserPhase @@ -1512,7 +1507,7 @@ class Global(var currentSettings: Settings, reporter0: Reporter) warnDeprecatedAndConflictingSettings() globalPhase = fromPhase - val timePhases = statistics.areStatisticsLocallyEnabled + val timePhases = settings.areStatisticsEnabled val startTotal = if (timePhases) statistics.startTimer(totalCompileTime) else null while (globalPhase.hasNext && !reporter.hasErrors) { diff --git a/src/compiler/scala/tools/nsc/MainBench.scala b/src/compiler/scala/tools/nsc/MainBench.scala index ca78db7e2dfe..84b3b6e603e7 100644 --- a/src/compiler/scala/tools/nsc/MainBench.scala +++ b/src/compiler/scala/tools/nsc/MainBench.scala @@ -29,9 +29,8 @@ object MainBench extends Driver with EvalLoop { var start = System.nanoTime() for (i <- 0 until NIter) { if (i == NIter-1) { - theCompiler.settings.Ystatistics.value = List("all") - theCompiler.statistics.enabled = true - theCompiler.statistics.hotEnabled = true + theCompiler.settings.Ystatistics.value = List("all") + theCompiler.settings.YhotStatisticsEnabled.value = true } process(args) val end = System.nanoTime() diff --git a/src/compiler/scala/tools/nsc/MainTokenMetric.scala b/src/compiler/scala/tools/nsc/MainTokenMetric.scala index c43683118028..ff8fcfa5c24f 100644 --- a/src/compiler/scala/tools/nsc/MainTokenMetric.scala +++ b/src/compiler/scala/tools/nsc/MainTokenMetric.scala @@ -50,7 +50,7 @@ object MainTokenMetric { tokenMetric(compiler, command.files) } catch { case ex @ FatalError(msg) => - if (command.settings.debug) + if (command.settings.isDebug) ex.printStackTrace() reporter.error(null, "fatal error: " + msg) } diff --git a/src/compiler/scala/tools/nsc/ObjectRunner.scala b/src/compiler/scala/tools/nsc/ObjectRunner.scala index 756822931d21..82b50330b1a0 100644 --- a/src/compiler/scala/tools/nsc/ObjectRunner.scala +++ b/src/compiler/scala/tools/nsc/ObjectRunner.scala @@ -25,8 +25,10 @@ trait CommonRunner { * @throws java.lang.NoSuchMethodException * @throws java.lang.reflect.InvocationTargetException */ - def run(urls: Seq[URL], objectName: String, arguments: Seq[String]): Unit = - ScalaClassLoader.fromURLs(urls).run(objectName, arguments) + def run(urls: Seq[URL], objectName: String, arguments: Seq[String]): Unit = { + import scala.reflect.internal.util.RichClassLoader._ + ScalaClassLoader.fromURLsParallelCapable(urls).run(objectName, arguments) + } /** Catches any non-fatal exception thrown by run (in the case of InvocationTargetException, * unwrapping it) and returns it in an Option. diff --git a/src/compiler/scala/tools/nsc/Parsing.scala b/src/compiler/scala/tools/nsc/Parsing.scala index 673d30bf237c..7d48e27678d8 100644 --- a/src/compiler/scala/tools/nsc/Parsing.scala +++ b/src/compiler/scala/tools/nsc/Parsing.scala @@ -22,7 +22,6 @@ trait Parsing { self : Positions with Reporting => trait RunParsing { val parsing: PerRunParsing = new PerRunParsing - def isScala213: Boolean } class PerRunParsing { diff --git a/src/compiler/scala/tools/nsc/PipelineMain.scala b/src/compiler/scala/tools/nsc/PipelineMain.scala index 1978abf2555f..7300d6996f33 100644 --- a/src/compiler/scala/tools/nsc/PipelineMain.scala +++ b/src/compiler/scala/tools/nsc/PipelineMain.scala @@ -55,10 +55,19 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe /** Forward errors to the (current) reporter. */ protected def scalacError(msg: String): Unit = { - reporter.error(FakePos("scalac"), msg + "\n scalac -help gives more information") + reporterError(FakePos("scalac"), msg + "\n scalac -help gives more information") } private var reporter: Reporter = _ + private def reporterEcho(pos: Position, msg: String): Unit = synchronized { + reporter.echo(pos, msg) + } + private def reporterEcho(msg: String): Unit = synchronized { + reporter.echo(NoPosition, msg) + } + private def reporterError(pos: Position, msg: String): Unit = synchronized { + reporter.echo(msg) + } private object handler extends UncaughtExceptionHandler { override def uncaughtException(t: Thread, e: Throwable): Unit = { @@ -93,14 +102,14 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe builder.append("}\n") val path = logDir.resolve("projects.dot") Files.write(path, builder.toString.getBytes(java.nio.charset.StandardCharsets.UTF_8)) - reporter.echo("Wrote project dependency graph to: " + path.toAbsolutePath) + reporterEcho("Wrote project dependency graph to: " + path.toAbsolutePath) } private case class Dependency(t: Task, isMacro: Boolean, isPlugin: Boolean) def process(): Boolean = { reporter = createReporter(new Settings(scalacError)) - reporter.echo(s"parallelism = $parallelism, strategy = $strategy") + reporterEcho(s"parallelism = $parallelism, strategy = $strategy") def commandFor(argFileArg: Path): Task = { val ss = new Settings(scalacError) @@ -137,13 +146,13 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe } else { PickleExtractor.process(entry, extracted) Files.setLastModifiedTime(extracted, sourceTimeStamp) - reporter.echo(s"Exported pickles from $entry to $extracted") + reporterEcho(s"Exported pickles from $entry to $extracted") Files.setLastModifiedTime(extracted, sourceTimeStamp) } strippedAndExportedClassPath(entry) = extracted } exportTimer.stop() - reporter.echo(f"Exported external classpath in ${exportTimer.durationMs}%.0f ms") + reporterEcho(f"Exported external classpath in ${exportTimer.durationMs}%.0f ms") } } @@ -169,14 +178,14 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe Await.result(awaitAllFutures, Duration(60, "s")) timer.stop() val numCompleted = allFutures.count(_.isCompleted) - reporter.echo(s"PROGRESS: $numCompleted / $numAllFutures") + reporterEcho(s"PROGRESS: $numCompleted / $numAllFutures") return } catch { case _: TimeoutException => val numCompleted = allFutures.count(_.isCompleted) if (numCompleted == lastNumCompleted) { - reporter.echo(s"STALLED: $numCompleted / $numAllFutures") - reporter.echo("Outline/Scala/Javac") + reporterEcho(s"STALLED: $numCompleted / $numAllFutures") + reporterEcho("Outline/Scala/Javac") projects.map { p => def toX(b: Future[_]): String = b.value match { case None => "-"; case Some(Success(_)) => "x"; case Some(Failure(_)) => "!" } @@ -184,7 +193,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe reporter.echo(s + " " + p.label) } } else { - reporter.echo(s"PROGRESS: $numCompleted / $numAllFutures") + reporterEcho(s"PROGRESS: $numCompleted / $numAllFutures") lastNumCompleted = numCompleted } } @@ -225,9 +234,9 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe if (parallelism == 1) { val criticalPath = projects.maxBy(_.regularCriticalPathMs) - reporter.echo(f"Critical path: ${criticalPath.regularCriticalPathMs}%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f"Critical path: ${criticalPath.regularCriticalPathMs}%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") } else - reporter.echo(f" Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f" Wall Clock: ${timer.durationMs}%.0f ms") case Pipeline => projects.foreach { p => val depsReady = Future.traverse(dependsOn.getOrElse(p, Nil))(task => p.dependencyReadyFuture(task)) @@ -264,9 +273,9 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe if (parallelism == 1) { val criticalPath = projects.maxBy(_.regularCriticalPathMs) - reporter.echo(f"Critical path: ${criticalPath.regularCriticalPathMs}%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f"Critical path: ${criticalPath.regularCriticalPathMs}%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") } else - reporter.echo(f" Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f" Wall Clock: ${timer.durationMs}%.0f ms") case Traditional => projects.foreach { p => val f1 = Future.traverse(dependsOn.getOrElse(p, Nil))(_.t.javaDone.future) @@ -289,9 +298,9 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe } if (parallelism == 1) { val maxFullCriticalPath: Double = projects.map(_.fullCriticalPathMs).max - reporter.echo(f"Critical path: $maxFullCriticalPath%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f"Critical path: $maxFullCriticalPath%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") } else { - reporter.echo(f"Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f"Wall Clock: ${timer.durationMs}%.0f ms") } } @@ -340,7 +349,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe trace.append("]}") val traceFile = logDir.resolve(s"build-${label}.trace") Files.write(traceFile, trace.toString.getBytes()) - reporter.echo("Chrome trace written to " + traceFile.toAbsolutePath) + reporterEcho("Chrome trace written to " + traceFile.toAbsolutePath) } case class Group(files: List[String]) { @@ -423,7 +432,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe if (reporter.hasErrors) reporter.flush() else if (command.shouldStopWithInfo) - reporter.echo(command.getInfoMessage(result)) + reporterEcho(command.getInfoMessage(result)) result.reporter = createReporter(result.settings) result } catch { @@ -556,9 +565,9 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe Position.range(sourceFile, diagnostic.getStartPosition.toInt, diagnostic.getPosition.toInt, diagnostic.getEndPosition.toInt) } diagnostic.getKind match { - case Kind.ERROR => reporter.error(position, msg) + case Kind.ERROR => reporterError(position, msg) case Kind.WARNING | Kind.MANDATORY_WARNING => Task.this.compiler.runReporting.warning(position, msg, WarningCategory.JavaSource, site = "") - case Kind.NOTE | Kind.OTHER => reporter.echo(position, msg) + case Kind.NOTE | Kind.OTHER => reporterEcho(position, msg) } } } @@ -577,7 +586,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe javaDone.complete(Success(())) } } - def log(msg: String): Unit = reporter.echo(this.label + ": " + msg) + def log(msg: String): Unit = reporterEcho(this.label + ": " + msg) } final class Timer() { diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala index a6e155f08a3f..cd26e72a7cfd 100644 --- a/src/compiler/scala/tools/nsc/Reporting.scala +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -55,9 +55,14 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio private val summarizedWarnings: mutable.Map[WarningCategory, mutable.LinkedHashMap[Position, Message]] = mutable.HashMap.empty private val summarizedInfos: mutable.Map[WarningCategory, mutable.LinkedHashMap[Position, Message]] = mutable.HashMap.empty - private var suppressionsComplete = false private val suppressions: mutable.LinkedHashMap[SourceFile, mutable.ListBuffer[Suppression]] = mutable.LinkedHashMap.empty - private val suspendedMessages: mutable.LinkedHashSet[Message] = mutable.LinkedHashSet.empty + private val suppressionsComplete: mutable.Set[SourceFile] = mutable.Set.empty + private val suspendedMessages: mutable.LinkedHashMap[SourceFile, mutable.LinkedHashSet[Message]] = mutable.LinkedHashMap.empty + + // Used in REPL. The old run is used for parsing. Don't discard its suspended warnings. + def initFrom(old: PerRunReporting): Unit = { + suspendedMessages ++= old.suspendedMessages + } private def isSuppressed(warning: Message): Boolean = suppressions.getOrElse(warning.pos.source, Nil).find(_.matches(warning)) match { @@ -65,6 +70,8 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio case _ => false } + def clearSuppressionsComplete(sourceFile: SourceFile): Unit = suppressionsComplete -= sourceFile + def addSuppression(sup: Suppression): Unit = { val source = sup.annotPos.source suppressions.getOrElseUpdate(source, mutable.ListBuffer.empty) += sup @@ -75,7 +82,7 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio def warnUnusedSuppressions(): Unit = { // if we stop before typer completes (errors in parser, Ystop), report all suspended messages - suspendedMessages.foreach(issueWarning) + suspendedMessages.valuesIterator.foreach(_.foreach(issueWarning)) // scaladoc doesn't run all phases, so not all warnings are emitted if (settings.warnUnusedNowarn && !settings.isScaladoc) for { @@ -85,12 +92,12 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio } if (!sup.used) issueWarning(Message.Plain(sup.annotPos, "@nowarn annotation does not suppress any warnings", WarningCategory.UnusedNowarn, "")) } - def reportSuspendedMessages(): Unit = { - suppressionsComplete = true + def reportSuspendedMessages(unit: CompilationUnit): Unit = { // sort suppressions. they are not added in any particular order because of lazy type completion - suppressions.mapValuesInPlace((_, sups) => sups.sortBy(sup => 0 - sup.start)) - suspendedMessages.foreach(m => if (!isSuppressed(m)) issueWarning(m)) - suspendedMessages.clear() + for (sups <- suppressions.get(unit.source)) + suppressions(unit.source) = sups.sortBy(sup => 0 - sup.start) + suppressionsComplete += unit.source + suspendedMessages.remove(unit.source).foreach(_.foreach(issueIfNotSuppressed)) } private def summaryMap(action: Action, category: WarningCategory) = { @@ -119,12 +126,12 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio } } - private def checkSuppressedAndIssue(warning: Message): Unit = { - if (suppressionsComplete) { + def issueIfNotSuppressed(warning: Message): Unit = { + if (suppressionsComplete(warning.pos.source)) { if (!isSuppressed(warning)) issueWarning(warning) } else - suspendedMessages += warning + suspendedMessages.getOrElseUpdate(warning.pos.source, mutable.LinkedHashSet.empty) += warning } private def summarize(action: Action, category: WarningCategory): Unit = { @@ -186,7 +193,7 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio } else "" def deprecationWarning(pos: Position, msg: String, since: String, site: String, origin: String): Unit = - checkSuppressedAndIssue(Message.Deprecation(pos, msg, site, origin, Version.fromString(since))) + issueIfNotSuppressed(Message.Deprecation(pos, msg, site, origin, Version.fromString(since))) def deprecationWarning(pos: Position, origin: Symbol, site: Symbol, msg: String, since: String): Unit = deprecationWarning(pos, msg, since, siteName(site), siteName(origin)) @@ -214,6 +221,7 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio ("macros", FeatureMacros) ).withDefaultValue(Feature) } + def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean, site: Symbol): Unit = { val req = if (required) "needs to" else "should" val fqname = "scala.language." + featureName @@ -238,13 +246,17 @@ trait Reporting extends internal.Reporting { self: ast.Positions with Compilatio && parentFileName(pos.source).getOrElse("") == "xsbt" && Thread.currentThread.getStackTrace.exists(_.getClassName.startsWith("sbt.")) ) - if (required && !isSbtCompat) reporter.error(pos, msg) - else warning(pos, msg, featureCategory(featureTrait.nameString), site) + // on postfix error, include interesting infix warning + def isXfix = featureName == "postfixOps" && suspendedMessages.get(pos.source).map(_.exists(w => pos.includes(w.pos))).getOrElse(false) + if (required && !isSbtCompat) { + val amended = if (isXfix) s"$msg\n${suspendedMessages(pos.source).filter(pos includes _.pos).map(_.msg).mkString("\n")}" else msg + reporter.error(pos, amended) + } else warning(pos, msg, featureCategory(featureTrait.nameString), site) } // Used in the optimizer where we don't have no symbols, the site string is created from the class internal name and method name. def warning(pos: Position, msg: String, category: WarningCategory, site: String): Unit = - checkSuppressedAndIssue(Message.Plain(pos, msg, category, site)) + issueIfNotSuppressed(Message.Plain(pos, msg, category, site)) // Preferred over the overload above whenever a site symbol is available def warning(pos: Position, msg: String, category: WarningCategory, site: Symbol): Unit = diff --git a/src/compiler/scala/tools/nsc/ast/Positions.scala b/src/compiler/scala/tools/nsc/ast/Positions.scala index 8cc1858297b4..6cda189bd82c 100644 --- a/src/compiler/scala/tools/nsc/ast/Positions.scala +++ b/src/compiler/scala/tools/nsc/ast/Positions.scala @@ -39,6 +39,6 @@ trait Positions extends scala.reflect.internal.Positions { } override protected[this] lazy val posAssigner: PosAssigner = - if (settings.Yrangepos && settings.debug || settings.Yposdebug) new ValidatingPosAssigner + if (settings.Yrangepos && settings.isDebug || settings.Yposdebug) new ValidatingPosAssigner else new DefaultPosAssigner } diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index cccd4326c375..ae55c09c3387 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -15,16 +15,14 @@ package tools.nsc package ast import scala.language.implicitConversions - import java.awt.{List => _, _} import java.awt.event._ import java.io.{StringWriter, Writer} import javax.swing._ import javax.swing.event.TreeModelListener import javax.swing.tree._ - import java.util.concurrent.CountDownLatch -import scala.annotation.tailrec +import scala.annotation.{nowarn, tailrec} /** * Tree browsers can show the AST in a graphical and interactive @@ -217,8 +215,8 @@ abstract class TreeBrowsers { } class ASTMenuBar extends JMenuBar { - val menuKey = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() - val shiftKey = InputEvent.SHIFT_MASK + val menuKey = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(): @nowarn("cat=deprecation") // deprecated since JDK 10, replacement only available in 10+ + val shiftKey = InputEvent.SHIFT_DOWN_MASK val jmFile = new JMenu("File") // val jmiSaveImage = new JMenuItem( // new AbstractAction("Save Tree Image") { diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala index a88de4d9c42d..f3979f6c94a2 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala @@ -65,16 +65,14 @@ trait TreeDSL { * a member called nme.EQ. Not sure if that should happen, but we can be * robust by dragging in Any regardless. */ - def MEMBER_== (other: Tree) = { - val opSym = if (target.tpe == null) NoSymbol else target.tpe member nme.EQ - if (opSym == NoSymbol) ANY_==(other) - else fn(target, opSym, other) - } + def MEMBER_== (other: Tree) = fn(target, (if (target.tpe == null) NoSymbol else target.tpe member nme.EQ).orElse(Any_==), other) def ANY_EQ (other: Tree) = OBJ_EQ(other AS ObjectTpe) def ANY_== (other: Tree) = fn(target, Any_==, other) def ANY_!= (other: Tree) = fn(target, Any_!=, other) - def OBJ_EQ (other: Tree) = fn(target, Object_eq, other) - def OBJ_NE (other: Tree) = fn(target, Object_ne, other) + def OBJ_EQ (other: Tree) = fn(target, Object_eq, other) + def OBJ_NE (other: Tree) = fn(target, Object_ne, other) + def OBJ_== (other: Tree) = fn(target, Object_equals, other) + def OBJ_## = fn(target, Object_hashCode) def INT_>= (other: Tree) = fn(target, getMember(IntClass, nme.GE), other) def INT_== (other: Tree) = fn(target, getMember(IntClass, nme.EQ), other) @@ -142,7 +140,17 @@ trait TreeDSL { def NEW(tpt: Tree, args: Tree*): Tree = New(tpt, List(args.toList)) def NOT(tree: Tree) = Select(tree, Boolean_not) - def AND(guards: Tree*) = if (guards.isEmpty) EmptyTree else guards reduceLeft gen.mkAnd + def AND(guards: Tree*) = { + def binaryTreeAnd(tests: Seq[Tree]): Tree = tests match{ + case Seq() => EmptyTree + case Seq(single) => single + case multiple => + val (before, after) = multiple.splitAt(tests.size / 2) + gen.mkAnd(binaryTreeAnd(before), binaryTreeAnd(after)) + } + + binaryTreeAnd(guards) + } def IF(tree: Tree) = new IfStart(tree, EmptyTree) def TRY(tree: Tree) = new TryStart(tree, Nil, EmptyTree) @@ -150,8 +158,8 @@ trait TreeDSL { def SOME(xs: Tree*) = Apply(SomeClass.companionSymbol, gen.mkTuple(xs.toList)) /** Typed trees from symbols. */ - def REF(sym: Symbol) = gen.mkAttributedRef(sym) - def REF(pre: Type, sym: Symbol) = gen.mkAttributedRef(pre, sym) + def REF(sym: Symbol): RefTree = gen.mkAttributedRef(sym) + def REF(pre: Type, sym: Symbol): RefTree = gen.mkAttributedRef(pre, sym) /** Implicits - some of these should probably disappear **/ implicit def mkTreeMethods(target: Tree): TreeMethods = new TreeMethods(target) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 4736df60916a..c3712f7b562f 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -674,6 +674,24 @@ self => case _ => false } + def isSoftModifier: Boolean = + currentRun.isScala3 && in.token == IDENTIFIER && softModifierNames.contains(in.name) + + /** Is the current token a soft modifier in a position where such a modifier is allowed? */ + def isValidSoftModifier: Boolean = + isSoftModifier && { + val mod = in.name + lookingAhead { + while (in.token == NEWLINE || isModifier || isSoftModifier) in.nextToken() + + in.token match { + case CLASS | CASECLASS => true + case DEF | TRAIT | TYPE => mod == nme.infix + case _ => false + } + } + } + def isAnnotation: Boolean = in.token == AT def isLocalModifier: Boolean = in.token match { @@ -705,6 +723,17 @@ self => def isRawBar = isRawIdent && in.name == raw.BAR def isRawIdent = in.token == IDENTIFIER + def isWildcardType = in.token == USCORE || isScala3WildcardType + def isScala3WildcardType = settings.isScala3 && isRawIdent && in.name == raw.QMARK + def checkQMarkUsage() = + if (!settings.isScala3 && isRawIdent && in.name == raw.QMARK) + deprecationWarning(in.offset, + "`?` in a type will be interpreted as a wildcard in the future, wrap it in backticks to keep the current meaning.", "2.13.6") + def checkQMarkDefinition() = + if (isRawIdent && in.name == raw.QMARK) + deprecationWarning(in.offset, + "using `?` as a type name will require backticks in the future.", "2.13.6") + def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT def isMacro = in.token == IDENTIFIER && in.name == nme.MACROkw @@ -715,12 +744,13 @@ self => } def isLiteral = isLiteralToken(in.token) - def isExprIntroToken(token: Token): Boolean = isLiteralToken(token) || (token match { - case IDENTIFIER | BACKQUOTED_IDENT | - THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE | - DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true - case _ => false - }) + def isExprIntroToken(token: Token): Boolean = + !isValidSoftModifier && (isLiteralToken(token) || (token match { + case IDENTIFIER | BACKQUOTED_IDENT | + THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE | + DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true + case _ => false + })) def isExprIntro: Boolean = isExprIntroToken(in.token) @@ -918,6 +948,16 @@ self => mkApply(Ident(op.encode), stripParens(left) :: arguments) } + /** Is current ident a `*`, and is it followed by a `)` or `, )`? */ + def followingIsScala3Vararg(): Boolean = + currentRun.isScala3 && isRawStar && lookingAhead { + in.token == RPAREN || + in.token == COMMA && { + in.nextToken() + in.token == RPAREN + } + } + /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ /** Modes for infix types. */ @@ -1023,13 +1063,14 @@ self => else { ts foreach checkNotByNameOrVarargs val tuple = atPos(start) { makeSafeTupleType(ts) } - infixTypeRest( + val tpt = infixTypeRest( compoundTypeRest( annotTypeRest( simpleTypeRest( tuple))), InfixMode.FirstOp ) + if (currentRun.isScala3) andType(tpt) else tpt } } private def makeExistentialTypeTree(t: Tree) = { @@ -1105,11 +1146,22 @@ self => } else atPos(start)(makeSafeTupleType(inParens(types()))) - case USCORE => wildcardType(in.skipToken()) case _ => - path(thisOK = false, typeOK = true) match { - case r @ SingletonTypeTree(_) => r - case r => convertToTypeId(r) + if (settings.isScala3 && (in.name == raw.PLUS || in.name == raw.MINUS) && lookingAhead(in.token == USCORE)) { + val start = in.offset + val identName = in.name.encode.append("_").toTypeName + in.nextToken() + in.nextToken() + atPos(start)(Ident(identName)) + } else if (isWildcardType) { + val scala3Wildcard = isScala3WildcardType + wildcardType(in.skipToken(), scala3Wildcard) + } else { + checkQMarkUsage() + path(thisOK = false, typeOK = true) match { + case r @ SingletonTypeTree(_) => r + case r => convertToTypeId(r) + } } }) } @@ -1193,12 +1245,44 @@ self => else t } + def andType(tpt: Tree): Tree = { + val parents = ListBuffer.empty[Tree] + var otherInfixOp: Tree = EmptyTree + def collect(tpt: Tree): Unit = tpt match { + case AppliedTypeTree(op @ Ident(tpnme.AND), List(left, right)) => + collect(left) + collect(right) + case AppliedTypeTree(op, args) if args.exists(arg => arg.pos.start < op.pos.point) => + otherInfixOp = op + parents += treeCopy.AppliedTypeTree(tpt, op, args.map(andType)) + case _ => + parents += tpt + } + collect(tpt) + if (parents.lengthCompare(1) > 0) { + if (!otherInfixOp.isEmpty) { + // TODO: Unlike Scala 3, we do not take precedence into account when + // parsing infix types, there's an unmerged PR that attempts to + // change that (#6147), but until that's merged we cannot accurately + // parse things like `A Map B & C`, so give up and emit an error + // rather than continuing with an incorrect parse tree. + syntaxError(otherInfixOp.pos.point, + s"Cannot parse infix type combining `&` and `$otherInfixOp`, please use `$otherInfixOp` as the head of a regular type application.") + } + atPos(tpt.pos.start)(CompoundTypeTree(Template(parents.toList, noSelfType, Nil))) + } + else + parents.head + } + /** {{{ * InfixType ::= CompoundType {id [nl] CompoundType} * }}} */ - def infixType(mode: InfixMode.Value): Tree = - placeholderTypeBoundary { infixTypeRest(compoundType(), mode) } + def infixType(mode: InfixMode.Value): Tree = { + val tpt = placeholderTypeBoundary { infixTypeRest(compoundType(), mode) } + if (currentRun.isScala3) andType(tpt) else tpt + } /** {{{ * Types ::= Type {`,` Type} @@ -1222,8 +1306,11 @@ self => def rawIdent(): Name = try in.name finally in.nextToken() /** For when it's known already to be a type name. */ - def identForType(): TypeName = ident().toTypeName - def identForType(skipIt: Boolean): TypeName = ident(skipIt).toTypeName + def identForType(): TypeName = identForType(skipIt = true) + def identForType(skipIt: Boolean): TypeName = { + checkQMarkDefinition() + ident(skipIt).toTypeName + } def identOrMacro(): Name = if (isMacro) rawIdent() else ident() @@ -1353,8 +1440,7 @@ self => else if (in.token == SYMBOLLIT) { def msg(what: String) = s"""symbol literal is $what; use Symbol("${in.strVal}") instead""" - if (settings.isScala3) syntaxError(in.offset, msg("unsupported")) - else deprecationWarning(in.offset, msg("deprecated"), "2.13.0") + deprecationWarning(in.offset, msg("deprecated"), "2.13.0") Apply(scalaDot(nme.Symbol), List(finish(in.strVal))) } else finish(in.token match { @@ -1472,8 +1558,8 @@ self => * WildcardType ::= `_` TypeBounds * }}} */ - def wildcardType(start: Offset) = { - val pname = freshTypeName("_$") + def wildcardType(start: Offset, qmark: Boolean) = { + val pname = if (qmark) freshTypeName("?$") else freshTypeName("_$") val t = atPos(start)(Ident(pname)) val bounds = typeBounds() val param = atPos(t.pos union bounds.pos) { makeSyntheticTypeParam(pname, bounds) } @@ -1546,22 +1632,14 @@ self => case TRY => def parseTry = atPos(in.skipToken()) { val body = expr() - def catchFromExpr() = List(makeCatchFromExpr(expr())) - val catches: List[CaseDef] = - if (in.token != CATCH) Nil - else { - in.nextToken() - if (in.token != LBRACE) catchFromExpr() - else inBracesOrNil { - if (in.token == CASE) caseClauses() - else catchFromExpr() - } - } + val handler: List[CaseDef] = + if (in.token == CATCH) { in.nextToken(); makeMatchFromExpr(expr()) } + else Nil val finalizer = in.token match { case FINALLY => in.nextToken() ; expr() case _ => EmptyTree } - Try(body, catches, finalizer) + Try(body, handler, finalizer) } parseTry case WHILE => @@ -1718,7 +1796,7 @@ self => val base = opstack @tailrec - def loop(top: Tree): Tree = if (!isIdent) top else { + def loop(top: Tree): Tree = if (!isIdent || followingIsScala3Vararg()) top else { pushOpInfo(reduceExprStack(base, top)) newLineOptWhenFollowing(isExprIntroToken) if (isExprIntro) @@ -1729,7 +1807,12 @@ self => else finishPostfixOp(start, base, popOpInfo()) } - reduceExprStack(base, loop(prefixExpr())) + val expr = reduceExprStack(base, loop(prefixExpr())) + if (followingIsScala3Vararg()) + atPos(expr.pos.start) { + Typed(expr, atPos(in.skipToken()) { Ident(tpnme.WILDCARD_STAR) }) + } + else expr } /** {{{ @@ -1934,6 +2017,12 @@ self => */ def generator(eqOK: Boolean, allowNestedIf: Boolean = true): List[Tree] = { val start = in.offset + val hasCase = in.token == CASE + if (hasCase) { + if (!currentRun.isScala3) syntaxError(in.offset, s"`case` keyword in for comprehension requires the -Xsource:3 flag.") + in.skipCASE() + } + val hasVal = in.token == VAL if (hasVal) in.nextToken() @@ -1952,7 +2041,7 @@ self => else syntaxError(in.offset, msg("unsupported", "just remove `val`")) } - if (hasEq && eqOK) in.nextToken() + if (hasEq && eqOK && !hasCase) in.nextToken() else accept(LARROW) val rhs = expr() @@ -1984,17 +2073,18 @@ self => final def functionArgType(): Tree = argType() final def argType(): Tree = { val start = in.offset - in.token match { - case USCORE => + if (isWildcardType) { + val scala3Wildcard = isScala3WildcardType in.nextToken() - if (in.token == SUBTYPE || in.token == SUPERTYPE) wildcardType(start) + if (in.token == SUBTYPE || in.token == SUPERTYPE) wildcardType(start, scala3Wildcard) else atPos(start) { Bind(tpnme.WILDCARD, EmptyTree) } - case _ => - typ() match { - case Ident(name: TypeName) if nme.isVariableName(name) => - atPos(start) { Bind(name, EmptyTree) } - case t => t - } + } else { + checkQMarkUsage() + typ() match { + case Ident(name: TypeName) if nme.isVariableName(name) => + atPos(start) { Bind(name, EmptyTree) } + case t => t + } } } @@ -2084,7 +2174,12 @@ self => if (isCloseDelim) atPos(top.pos.start, in.prev.offset)(Star(stripParens(top))) else EmptyTree ) - case _ => EmptyTree + case Ident(name) if isSequenceOK && followingIsScala3Vararg() => + atPos(top.pos.start) { + Bind(name, atPos(in.skipToken()) { Star(Ident(nme.WILDCARD)) }) + } + case _ => + EmptyTree } @tailrec def loop(top: Tree): Tree = reducePatternStack(base, top) match { @@ -2263,8 +2358,11 @@ self => */ def accessModifierOpt(): Modifiers = normalizeModifiers { in.token match { - case m @ (PRIVATE | PROTECTED) => in.nextToken() ; accessQualifierOpt(Modifiers(flagTokens(m))) - case _ => NoMods + case m @ (PRIVATE | PROTECTED) => + in.nextToken() + accessQualifierOpt(Modifiers(flagTokens(m))) + case _ => + NoMods } } @@ -2286,7 +2384,10 @@ self => in.nextToken() loop(mods) case _ => - mods + if (isValidSoftModifier) { + in.nextToken() + loop(mods) + } else mods } loop(NoMods) } @@ -2370,14 +2471,7 @@ self => if (vds.isEmpty) syntaxError(start, s"case classes must have a parameter list; try 'case class $name()' or 'case object $name'") else if (vds.head.nonEmpty && vds.head.head.mods.isImplicit) { - if (currentRun.isScala213) - syntaxError(start, s"case classes must have a non-implicit parameter list; try 'case class $name()$elliptical'") - else { - deprecationWarning(start, s"case classes should have a non-implicit parameter list; adapting to 'case class $name()$elliptical'", "2.12.2") - vds.insert(0, List.empty[ValDef]) - vds(1) = vds(1).map(vd => copyValDef(vd)(mods = vd.mods & ~Flags.CASEACCESSOR)) - if (implicitSection != -1) implicitSection += 1 - } + syntaxError(start, s"case classes must have a non-implicit parameter list; try 'case class $name()$elliptical'") } } if (implicitSection != -1 && implicitSection != vds.length - 1) @@ -2490,6 +2584,7 @@ self => } } val nameOffset = in.offset + checkQMarkDefinition() // TODO AM: freshTermName(o2p(in.skipToken()), "_$$"), will need to update test suite val pname: TypeName = wildcardOrIdent().toTypeName val param = atPos(start, nameOffset) { @@ -2575,19 +2670,27 @@ self => def loop(expr: Tree): Tree = { expr setPos expr.pos.makeTransparent val selectors: List[ImportSelector] = in.token match { - case USCORE => List(wildImportSelector()) // import foo.bar._; - case LBRACE => importSelectors() // import foo.bar.{ x, y, z } - case _ => - val nameOffset = in.offset - val name = ident() - if (in.token == DOT) { - // import foo.bar.ident. and so create a select node and recurse. - val t = atPos(start, if (name == nme.ERROR) in.offset else nameOffset)(Select(expr, name)) - in.nextToken() - return loop(t) + case USCORE => + List(wildImportSelector()) // import foo.bar._ + case IDENTIFIER if currentRun.isScala3 && in.name == raw.STAR => + List(wildImportSelector()) // import foo.bar.* + case LBRACE => + importSelectors() // import foo.bar.{ x, y, z } + case _ => + if (settings.isScala3 && lookingAhead { isRawIdent && in.name == nme.as }) + List(importSelector()) // import foo.bar as baz + else { + val nameOffset = in.offset + val name = ident() + if (in.token == DOT) { + // import foo.bar.ident. and so create a select node and recurse. + val t = atPos(start, if (name == nme.ERROR) in.offset else nameOffset)(Select(expr, name)) + in.nextToken() + return loop(t) + } + // import foo.bar.Baz; + else List(makeImportSelector(name, nameOffset)) } - // import foo.bar.Baz; - else List(makeImportSelector(name, nameOffset)) } // reaching here means we're done walking. atPos(start)(Import(expr, selectors)) @@ -2616,10 +2719,9 @@ self => selectors } - def wildcardOrIdent() = { - if (in.token == USCORE) { in.nextToken() ; nme.WILDCARD } + def wildcardOrIdent() = + if (in.token == USCORE || settings.isScala3 && isRawStar) { in.nextToken() ; nme.WILDCARD } else ident() - } /** {{{ * ImportSelector ::= Id [`=>` Id | `=>` `_`] @@ -2630,17 +2732,20 @@ self => val bbq = in.token == BACKQUOTED_IDENT val name = wildcardOrIdent() var renameOffset = -1 - val rename = in.token match { - case ARROW => + + val rename = + if (in.token == ARROW || (settings.isScala3 && isRawIdent && in.name == nme.as)) { in.nextToken() renameOffset = in.offset if (name == nme.WILDCARD && !bbq) syntaxError(renameOffset, "Wildcard import cannot be renamed") wildcardOrIdent() - case _ if name == nme.WILDCARD && !bbq => null - case _ => + } + else if (name == nme.WILDCARD && !bbq) null + else { renameOffset = start name - } + } + ImportSelector(name, start, rename, renameOffset) } @@ -2697,10 +2802,10 @@ self => in.nextToken() val lhs = commaSeparated(stripParens(noSeq.pattern2())) val tp = typedOpt() - val rhs = + val (rhs, rhsPos) = if (!tp.isEmpty && in.token != EQUALS) { newmods = newmods | Flags.DEFERRED - EmptyTree + (EmptyTree, NoPosition) } else { accept(EQUALS) expr() match { @@ -2712,14 +2817,14 @@ self => } placeholderParams = placeholderParams.tail newmods = newmods | Flags.DEFAULTINIT - EmptyTree - case x => x + (EmptyTree, x.pos) + case x => (x, x.pos) } } def mkDefs(p: Tree, tp: Tree, rhs: Tree): List[Tree] = { val trees = { val pat = if (tp.isEmpty) p else Typed(p, tp) setPos (p.pos union tp.pos) - makePatDef(newmods, pat, rhs) + makePatDef(newmods, pat, rhs, rhsPos) } if (newmods.isDeferred) { trees match { @@ -2874,8 +2979,7 @@ self => t = Apply(t, argumentExprs()) newLineOptWhenFollowedBy(LBRACE) } - if (classContextBounds.isEmpty) t - else Apply(t, vparamss.last.map(vp => Ident(vp.name))) + t } /** {{{ @@ -3216,7 +3320,7 @@ self => case IMPORT => in.flushDoc() importClause() - case _ if isAnnotation || isTemplateIntro || isModifier => + case _ if isAnnotation || isTemplateIntro || isModifier || isValidSoftModifier => joinComment(topLevelTmplDef :: Nil) } @@ -3266,7 +3370,7 @@ self => case IMPORT => in.flushDoc() importClause() - case _ if isDefIntro || isModifier || isAnnotation => + case _ if isDefIntro || isModifier || isAnnotation || isValidSoftModifier => joinComment(nonLocalDefOrDcl) case _ if isExprIntro => in.flushDoc() diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 99e08ab4bce6..17b46da9191c 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -13,6 +13,7 @@ package scala.tools.nsc package ast.parser +import scala.tools.nsc.settings.ScalaVersion import scala.tools.nsc.util.{CharArrayReader, CharArrayReaderData} import scala.reflect.internal.util._ import scala.reflect.internal.Chars._ @@ -393,6 +394,19 @@ trait Scanners extends ScannersCommon { case _ => } + /** Advance beyond a case token without marking the CASE in sepRegions. + * This method should be called to skip beyond CASE tokens that are + * not part of matches, i.e. no ARROW is expected after them. + */ + def skipCASE(): Unit = { + assert(token == CASE, s"Internal error: skipCASE() called on non-case token $token") + nextToken() + sepRegions = sepRegions.tail + } + + /** True to warn about migration change in infix syntax. */ + private val infixMigration = settings.Xmigration.value <= ScalaVersion("2.13.2") + /** Produce next token, filling TokenData fields of Scanner. */ def nextToken(): Unit = { @@ -432,18 +446,35 @@ trait Scanners extends ScannersCommon { token = nl } + def isOperator: Boolean = token == BACKQUOTED_IDENT || token == IDENTIFIER && isOperatorPart(name.charAt(name.length - 1)) + + /* A leading infix operator must be followed by a lexically suitable expression. + * Usually any simple expr will do. However, a backquoted identifier may serve as + * either an op or a reference. So the additional constraint is that the following + * token can't be an assignment operator. (Dotty disallows binary ops, hence the + * test for unary.) See run/multiLineOps.scala for 42 + `x` on 3 lines, where + + * is not leading infix because backquoted x is non-unary op. + */ + def followedByInfixRHS: Boolean = { + //def isCandidateInfixRHS: Boolean = isSimpleExprIntroToken(token) && (!isOperator || nme.raw.isUnary(name) || token == BACKQUOTED_IDENT) + def isAssignmentOperator: Boolean = + name.endsWith('=') && !name.startsWith('=') && isOperatorPart(name.startChar) && + (name.length != 2 || (name.startChar match { case '!' | '<' | '>' => false case _ => true })) + def isCandidateInfixRHS: Boolean = isSimpleExprIntroToken(token) && (!isOperator || token == BACKQUOTED_IDENT || !isAssignmentOperator) + lookingAhead { + isCandidateInfixRHS || token == NEWLINE && { nextToken() ; isCandidateInfixRHS } + } + } + /* A leading symbolic or backquoted identifier is treated as an infix operator * if it is followed by at least one ' ' and a token on the same line * that can start an expression. */ def isLeadingInfixOperator = allowLeadingInfixOperators && - (token == BACKQUOTED_IDENT || - token == IDENTIFIER && isOperatorPart(name.charAt(name.length - 1))) && - (ch == ' ') && lookingAhead { - // force a NEWLINE after current token if it is on its own line - isSimpleExprIntroToken(token) - } + isOperator && + (isWhitespace(ch) || ch == LF) && + followedByInfixRHS /* Insert NEWLINE or NEWLINES if * - we are after a newline @@ -459,8 +490,8 @@ trait Scanners extends ScannersCommon { val msg = """|Line starts with an operator that in future |will be taken as an infix expression continued from the previous line. |To force the previous interpretation as a separate statement, - |add an explicit `;`, add an empty line, or remove spaces after the operator.""".stripMargin - deprecationWarning(msg, "2.13.2") + |add an explicit `;`, add an empty line, or remove spaces after the operator.""" + if (infixMigration) deprecationWarning(msg.stripMargin, "2.13.2") insertNL(NEWLINE) } } @@ -670,11 +701,9 @@ trait Scanners extends ScannersCommon { val isEmptyCharLit = (ch == '\'') getLitChar() if (ch == '\'') { - if (isEmptyCharLit && currentRun.isScala213) + if (isEmptyCharLit) syntaxError("empty character literal (use '\\'' for single quote)") else { - if (isEmptyCharLit) - deprecationWarning("deprecated syntax for character literal (use '\\'' for single quote)", "2.12.2") nextChar() token = CHARLIT setStrVal() @@ -854,7 +883,12 @@ trait Scanners extends ScannersCommon { } else unclosedStringLit() } - private def unclosedStringLit(): Unit = syntaxError("unclosed string literal") + private def unclosedStringLit(seenEscapedQuoteInInterpolation: Boolean = false): Unit = { + val note = + if (seenEscapedQuoteInInterpolation) "; note that `\\\"` no longer closes single-quoted interpolated string literals since 2.13.6, you can use a triple-quoted string instead" + else "" + syntaxError(s"unclosed string literal$note") + } private def replaceUnicodeEscapesInTriple(): Unit = if(strVal != null) { @@ -890,7 +924,8 @@ trait Scanners extends ScannersCommon { } } - @tailrec private def getStringPart(multiLine: Boolean): Unit = { + // for interpolated strings + @tailrec private def getStringPart(multiLine: Boolean, seenEscapedQuote: Boolean = false): Unit = { def finishStringPart() = { setStrVal() token = STRINGPART @@ -904,18 +939,27 @@ trait Scanners extends ScannersCommon { setStrVal() token = STRINGLIT } else - getStringPart(multiLine) + getStringPart(multiLine, seenEscapedQuote) } else { nextChar() setStrVal() token = STRINGLIT } + } else if (ch == '\\' && !multiLine) { + putChar(ch) + nextRawChar() + val q = ch == '"' + if (q || ch == '\\') { + putChar(ch) + nextRawChar() + } + getStringPart(multiLine, seenEscapedQuote || q) } else if (ch == '$') { nextRawChar() - if (ch == '$') { + if (ch == '$' || ch == '"') { putChar(ch) nextRawChar() - getStringPart(multiLine) + getStringPart(multiLine, seenEscapedQuote) } else if (ch == '{') { finishStringPart() nextRawChar() @@ -938,20 +982,22 @@ trait Scanners extends ScannersCommon { next.token = kwArray(idx) } } else { - syntaxError(s"invalid string interpolation $$$ch, expected: $$$$, $$identifier or $${expression}") + val expectations = "$$, $\", $identifier or ${expression}" + syntaxError(s"invalid string interpolation $$$ch, expected: $expectations") } } else { val isUnclosedLiteral = (ch == SU || (!multiLine && (ch == CR || ch == LF))) if (isUnclosedLiteral) { if (multiLine) incompleteInputError("unclosed multi-line string literal") - else - unclosedStringLit() + else { + unclosedStringLit(seenEscapedQuote) + } } else { putChar(ch) nextRawChar() - getStringPart(multiLine) + getStringPart(multiLine, seenEscapedQuote) } } } @@ -1413,6 +1459,8 @@ trait Scanners extends ScannersCommon { final val token2name = (allKeywords map (_.swap)).toMap + final val softModifierNames = Set(nme.open, nme.infix) + // Token representation ---------------------------------------------------- /** Returns the string representation of given token. */ diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index ea7e9f1b0cc5..2b05b600b598 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -102,6 +102,17 @@ abstract class TreeBuilder { def makeCaseDef(pat: Tree, guard: Tree, rhs: Tree): CaseDef = CaseDef(gen.patvarTransformer.transform(pat), guard, rhs) + /** At parser, rejigger non-case catch expression. + * + * Match is eliminated by unwrapping. Other expression + * becomes a single CaseDef with empty pattern and + * expr tree as RHS. + */ + def makeMatchFromExpr(catchExpr: Tree): List[CaseDef] = catchExpr match { + case Match(EmptyTree, cases) => cases + case _ => CaseDef(EmptyTree, EmptyTree, catchExpr) :: Nil + } + /** Creates tree representing: * { case x: Throwable => * val catchFn = catchExpr @@ -124,6 +135,18 @@ abstract class TreeBuilder { makeCaseDef(pat, EmptyTree, body) } + /** Creates tree representing: + * { case x: Throwable => catchExpr(x) } + */ + def makeCatchFromFunc(catchFn: Tree): CaseDef = { + val binder = freshTermName() + val pat = Bind(binder, Typed(Ident(nme.WILDCARD), Ident(tpnme.Throwable))) + val body = atPos(catchFn.pos.makeTransparent)(Block( + Apply(Select(catchFn, nme.apply), List(Ident(binder))), + )) + makeCaseDef(pat, EmptyTree, body) + } + /** Create a tree representing the function type (argtpes) => restpe */ def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = gen.mkFunctionTypeTree(argtpes, restpe) @@ -143,5 +166,6 @@ abstract class TreeBuilder { } } - def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = gen.mkPatDef(mods, pat, rhs) + final def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree): List[ValDef] = makePatDef(mods, pat, rhs, rhs.pos) + def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree, rhsPos: Position) = gen.mkPatDef(mods, pat, rhs, rhsPos) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 6bba2f75190d..a40c04e6a527 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -953,12 +953,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { mbt.descriptor ) } - module.attachments.get[DottyEnumSingleton] match { // TODO [tasty]: dotty enum singletons are not modules. - case Some(enumAttach) => - val enumCompanion = symInfoTK(module.originalOwner).asClassBType - visitAccess(enumCompanion, enumAttach.name) - - case _ => visitAccess(mbt, strMODULE_INSTANCE_FIELD) + if (module.isScala3Defined && module.hasAttachment[DottyEnumSingleton.type]) { // TODO [tasty]: dotty enum singletons are not modules. + val enumCompanion = symInfoTK(module.originalOwner).asClassBType + visitAccess(enumCompanion, module.rawname.toString) + } else { + visitAccess(mbt, strMODULE_INSTANCE_FIELD) } } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index da8f110d5be6..c1eb637d8ae4 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -14,14 +14,15 @@ package scala package tools.nsc package backend.jvm +import scala.PartialFunction.cond +import scala.annotation.tailrec import scala.tools.asm -import BackendReporting._ -import scala.tools.asm.ClassWriter +import scala.tools.asm.{ClassWriter, Label} +import scala.tools.nsc.Reporting.WarningCategory import scala.tools.nsc.backend.jvm.BCodeHelpers.ScalaSigBytes +import scala.tools.nsc.backend.jvm.BackendReporting._ import scala.tools.nsc.reporters.NoReporter -import PartialFunction.cond -import scala.annotation.tailrec -import scala.tools.nsc.Reporting.WarningCategory +import scala.util.chaining.scalaUtilChainingOps /* * Traits encapsulating functionality to convert Scala AST Trees into ASM ClassNodes. @@ -31,9 +32,9 @@ import scala.tools.nsc.Reporting.WarningCategory */ abstract class BCodeHelpers extends BCodeIdiomatic { import global._ - import definitions._ import bTypes._ import coreBTypes._ + import definitions._ import genBCode.postProcessor.backendUtils /** @@ -282,7 +283,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic { |""".stripMargin, WarningCategory.Other, sym) - val possibles = (sym.tpe nonPrivateMember nme.main).alternatives + val possibles = sym.tpe.nonPrivateMember(nme.main).alternatives val hasApproximate = possibles.exists(m => cond(m.info) { case MethodType(p :: Nil, _) => p.tpe.typeSymbol == definitions.ArrayClass }) // Before erasure so we can identify generic mains. @@ -307,16 +308,19 @@ abstract class BCodeHelpers extends BCodeIdiomatic { val mainAdvice = if (hasExact) Nil else possibles.map { m => - m.info match { + val msg = m.info match { case PolyType(_, _) => - ("main methods cannot be generic", m) + "main methods cannot be generic" case MethodType(params, res) if res.typeSymbol :: params exists (_.isAbstractType) => - ("main methods cannot refer to type parameters or abstract types", m) + "main methods cannot refer to type parameters or abstract types" + case MethodType(param :: Nil, _) if definitions.isArrayOfSymbol(param.tpe, StringClass) => + "main methods must have the exact signature `(Array[String]): Unit`, though Scala runners will forgive a non-Unit result" case MethodType(_, _) => - ("main methods must have the exact signature (Array[String])Unit", m) + "main methods must have the exact signature `(Array[String]): Unit`" case tp => - (s"don't know what this is: $tp", m) + s"don't know what this is: $tp" } + (msg, m) } companionAdvice.foreach(msg => warnNoForwarder(msg, hasExact, exactly.fold(alternate)(_.info))) @@ -365,7 +369,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic { */ trait BCPickles { - import scala.reflect.internal.pickling.{ PickleFormat, PickleBuffer } + import scala.reflect.internal.pickling.{PickleBuffer, PickleFormat} val versionPickle = { val vp = new PickleBuffer(new Array[Byte](16), -1, 0) @@ -794,6 +798,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic { mirrorMethod.visitCode() + val codeStart: Label = new Label().tap(mirrorMethod.visitLabel) mirrorMethod.visitFieldInsn(asm.Opcodes.GETSTATIC, moduleName, strMODULE_INSTANCE_FIELD, classBTypeFromSymbol(moduleClass).descriptor) var index = 0 @@ -805,6 +810,13 @@ abstract class BCodeHelpers extends BCodeIdiomatic { mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, methodBTypeFromSymbol(m).descriptor, false) mirrorMethod.visitInsn(jReturnType.typedOpcode(asm.Opcodes.IRETURN)) + val codeEnd = new Label().tap(mirrorMethod.visitLabel) + + methodInfo.params.lazyZip(paramJavaTypes).foldLeft(0) { + case (idx, (p, tp)) => + mirrorMethod.visitLocalVariable(p.name.encoded, tp.descriptor, null, codeStart, codeEnd, idx) + idx + tp.size + } mirrorMethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments mirrorMethod.visitEnd() diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index 5afe4b101b71..86c0b83671c4 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -94,7 +94,7 @@ abstract class BCodeIdiomatic { /* Just a namespace for utilities that encapsulate MethodVisitor idioms. * In the ASM world, org.objectweb.asm.commons.InstructionAdapter plays a similar role, - * but the methods here allow choosing when to transition from ICode to ASM types + * but the methods here allow choosing when to transition from BType to ASM types * (including not at all, e.g. for performance). */ abstract class JCodeMethodN { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index 41c34d056ad2..48d8290535d0 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -644,7 +644,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { case Return(_) | Block(_, Return(_)) | Throw(_) | Block(_, Throw(_)) => () case EmptyTree => globalError("Concrete method has no definition: " + dd + ( - if (settings.debug) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")" + if (settings.isDebug) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")" else "")) case _ => bc emitRETURN returnType diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index 149c29a96ac2..f6a1c2a3e092 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -91,9 +91,9 @@ abstract class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol") assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym") - if (global.settings.debug) { - // OPT these assertions have too much performance overhead to run unconditionally - assertClassNotArrayNotPrimitive(classSym) + // note: classSym can be scala.Array, see https://github.com/scala/bug/issues/12225#issuecomment-729687859 + if (global.settings.isDebug) { + // OPT this assertion has too much performance overhead to run unconditionally assert(!primitiveTypeToBType.contains(classSym) || isCompilingPrimitive, s"Cannot create ClassBType for primitive class symbol $classSym") } @@ -221,11 +221,6 @@ abstract class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { assert(sym != definitions.ArrayClass || isCompilingArray, sym) } - def assertClassNotArrayNotPrimitive(sym: Symbol): Unit = { - assertClassNotArray(sym) - assert(!primitiveTypeToBType.contains(sym) || isCompilingPrimitive, sym) - } - def implementedInterfaces(classSym: Symbol): List[Symbol] = { def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait diff --git a/src/compiler/scala/tools/nsc/backend/jvm/ClassfileWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/ClassfileWriters.scala index 716a1d6de31f..15bce5921204 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/ClassfileWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/ClassfileWriters.scala @@ -63,7 +63,7 @@ abstract class ClassfileWriters { def apply(global: Global): ClassfileWriter = { //Note dont import global._ - its too easy to leak non threadsafe structures - import global.{cleanup, log, settings, statistics} + import global.{ cleanup, log, settings } def jarManifestMainClass: Option[String] = settings.mainClass.valueSetByUser.orElse { cleanup.getEntryPoints match { case List(name) => Some(name) @@ -91,7 +91,7 @@ abstract class ClassfileWriters { new DebugClassWriter(basicClassWriter, asmp, dump) } - val enableStats = statistics.enabled && settings.YaddBackendThreads.value == 1 + val enableStats = settings.areStatisticsEnabled && settings.YaddBackendThreads.value == 1 if (enableStats) new WithStatsWriter(withAdditionalFormats) else withAdditionalFormats } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CodeGen.scala b/src/compiler/scala/tools/nsc/backend/jvm/CodeGen.scala index 72cd7a0d5ca7..2765c063f17f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/CodeGen.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/CodeGen.scala @@ -50,7 +50,7 @@ abstract class CodeGen[G <: Global](val global: G) extends PerRunInit { } catch { case ex: InterruptedException => throw ex case ex: Throwable => - if (settings.debug) ex.printStackTrace() + if (settings.isDebug) ex.printStackTrace() globalError(s"Error while emitting ${unit.source}\n${ex.getMessage}") } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GeneratedClassHandler.scala b/src/compiler/scala/tools/nsc/backend/jvm/GeneratedClassHandler.scala index beec1ade9d06..5853b52a3142 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GeneratedClassHandler.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GeneratedClassHandler.scala @@ -59,7 +59,7 @@ private[jvm] object GeneratedClassHandler { new SyncWritingClassHandler(postProcessor) case maxThreads => - if (statistics.enabled) + if (settings.areStatisticsEnabled) runReporting.warning(NoPosition, "jvm statistics are not reliable with multi-threaded jvm class writing", WarningCategory.Other, site = "") val additionalThreads = maxThreads - 1 // The thread pool queue is limited in size. When it's full, the `CallerRunsPolicy` causes diff --git a/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala b/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala index b9ec6a85f060..748a8f3cc75a 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/PostProcessorFrontendAccess.scala @@ -184,7 +184,7 @@ object PostProcessorFrontendAccess { private def buildCompilerSettings(): CompilerSettings = new CompilerSettings { import global.{settings => s} - val debug: Boolean = s.debug + @inline def debug: Boolean = s.isDebug val target: String = s.target.value diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala index 5df66c34b707..7d790313f694 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala @@ -265,6 +265,10 @@ abstract class BoxUnbox { case c: EscapingConsumer => assert(keepBox, s"found escaping consumer, but box is eliminated: $c") + case Drop(insn) => + if (keepBox) toReplace(insn) = List(getPop(1)) + else toDelete += insn + case extraction => val (slot, tp) = localSlots(boxKind.extractedValueIndex(extraction)) val loadOps = new VarInsnNode(tp.getOpcode(ILOAD), slot) :: extraction.postExtractionAdaptationOps(tp) @@ -327,31 +331,38 @@ abstract class BoxUnbox { if (boxKind.boxedTypes.lengthCompare(1) == 0) { // fast path for single-value boxes allConsumers.foreach(extraction => extraction.postExtractionAdaptationOps(boxKind.boxedTypes.head) match { - case Nil => - toDelete ++= extraction.allInsns + case Nil => extraction match { + case Drop(_) => toReplace(extraction.consumer) = boxKind.boxedTypes.map(t => getPop(t.getSize)) + case _ => toDelete ++= extraction.allInsns + } case ops => toReplace(extraction.consumer) = ops toDelete ++= extraction.allInsns - extraction.consumer }) } else { for (extraction <- allConsumers) { - val valueIndex = boxKind.extractedValueIndex(extraction) - val replacementOps = if (valueIndex == 0) { - val pops = boxKind.boxedTypes.tail.map(t => getPop(t.getSize)) - pops ::: extraction.postExtractionAdaptationOps(boxKind.boxedTypes.head) - } else { - var loadOps: List[AbstractInsnNode] = null + val replacementOps = extraction match { + case Drop(_) => + boxKind.boxedTypes.reverseIterator.map(t => getPop(t.getSize)).toList + case _ => + val valueIndex = boxKind.extractedValueIndex(extraction) + if (valueIndex == 0) { + val pops = boxKind.boxedTypes.tail.map(t => getPop(t.getSize)) + pops ::: extraction.postExtractionAdaptationOps(boxKind.boxedTypes.head) + } else { + var loadOps: List[AbstractInsnNode] = null val consumeStack = boxKind.boxedTypes.zipWithIndex.reverseIterator.map { - case (tp, i) => - if (i == valueIndex) { - val resultSlot = getLocal(tp.getSize) - loadOps = new VarInsnNode(tp.getOpcode(ILOAD), resultSlot) :: extraction.postExtractionAdaptationOps(tp) - new VarInsnNode(tp.getOpcode(ISTORE), resultSlot) - } else { - getPop(tp.getSize) - } + case (tp, i) => + if (i == valueIndex) { + val resultSlot = getLocal(tp.getSize) + loadOps = new VarInsnNode(tp.getOpcode(ILOAD), resultSlot) :: extraction.postExtractionAdaptationOps(tp) + new VarInsnNode(tp.getOpcode(ISTORE), resultSlot) + } else { + getPop(tp.getSize) + } }.to(List) - consumeStack ::: loadOps + consumeStack ::: loadOps + } } toReplace(extraction.consumer) = replacementOps toDelete ++= extraction.allInsns - extraction.consumer @@ -621,7 +632,7 @@ abstract class BoxUnbox { val afterInit = initCall.getNext val stackTopAfterInit = prodCons.frameAt(afterInit).stackTop val initializedInstanceCons = prodCons.consumersOfValueAt(afterInit, stackTopAfterInit) - if (initializedInstanceCons == dupConsWithoutInit && prodCons.producersForValueAt(afterInit, stackTopAfterInit) == Set(dupOp)) { + if (initializedInstanceCons == dupConsWithoutInit) { return Some((dupOp, initCall)) } } @@ -708,6 +719,9 @@ abstract class BoxUnbox { val success = primBoxSupertypes(kind.boxClass).contains(ti.desc) Some(BoxedPrimitiveTypeCheck(ti, success)) + case i: InsnNode if i.getOpcode == POP => + Some(Drop(i)) + case _ => None } } @@ -761,6 +775,9 @@ abstract class BoxUnbox { case ti: TypeInsnNode if ti.getOpcode == INSTANCEOF => Some(BoxedPrimitiveTypeCheck(ti, ti.desc == kind.refClass || refSupertypes.contains(ti.desc))) + case i: InsnNode if i.getOpcode == POP => + Some(Drop(i)) + case _ => None } } @@ -824,6 +841,7 @@ abstract class BoxUnbox { } case _ => + if (insn.getOpcode == POP) return Some(Drop(insn)) } None } @@ -943,6 +961,8 @@ abstract class BoxUnbox { case class StaticSetterOrInstanceWrite(consumer: AbstractInsnNode) extends BoxConsumer /** `.\$isInstanceOf[T]` (can be statically proven true or false) */ case class BoxedPrimitiveTypeCheck(consumer: AbstractInsnNode, success: Boolean) extends BoxConsumer + /** POP */ + case class Drop(consumer: AbstractInsnNode) extends BoxConsumer /** An unknown box consumer */ case class EscapingConsumer(consumer: AbstractInsnNode) extends BoxConsumer } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala index 5b44a03bde65..2d08d3ea5d8b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala @@ -17,8 +17,9 @@ package opt import scala.annotation.nowarn import scala.collection.{concurrent, mutable} import scala.jdk.CollectionConverters._ +import scala.reflect.internal.util.NoPosition import scala.tools.asm -import scala.tools.asm.Attribute +import scala.tools.asm.{Attribute, Type} import scala.tools.asm.tree._ import scala.tools.nsc.backend.jvm.BTypes.InternalName import scala.tools.nsc.backend.jvm.BackendReporting._ @@ -34,7 +35,7 @@ abstract class ByteCodeRepository extends PerRunInit { import postProcessor.{bTypes, bTypesFromClassfile} import bTypes._ - import frontendAccess.{backendClassPath, recordPerRunCache} + import frontendAccess.{backendReporting, backendClassPath, recordPerRunCache} /** * Contains ClassNodes and the canonical path of the source file path of classes being compiled in @@ -161,9 +162,21 @@ abstract class ByteCodeRepository extends PerRunInit { def methodNode(ownerInternalNameOrArrayDescriptor: String, name: String, descriptor: String): Either[MethodNotFound, (MethodNode, InternalName)] = { def findMethod(c: ClassNode): Option[MethodNode] = c.methods.asScala.find(m => m.name == name && m.desc == descriptor) - // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9: "In Java SE 8, the only - // signature polymorphic methods are the invoke and invokeExact methods of the class MethodHandle. - def isSignaturePolymorphic(owner: InternalName) = owner == coreBTypes.jliMethodHandleRef.internalName && (name == "invoke" || name == "invokeExact") + // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-2.html#jvms-2.9.3 + def findSignaturePolymorphic(owner: ClassNode): Option[MethodNode] = { + def hasObjectArrayParam(m: MethodNode) = Type.getArgumentTypes(m.desc) match { + case Array(pt) => pt.getDimensions == 1 && pt.getElementType.getInternalName == coreBTypes.ObjectRef.internalName + case _ => false + } + // Don't try to build a BType for `VarHandle`, it doesn't exist on JDK 8 + if (owner.name == coreBTypes.jliMethodHandleRef.internalName || owner.name == "java/lang/invoke/VarHandle") + owner.methods.asScala.find(m => + m.name == name && + isNativeMethod(m) && + isVarargsMethod(m) && + hasObjectArrayParam(m)) + else None + } // Note: if `owner` is an interface, in the first iteration we search for a matching member in the interface itself. // If that fails, the recursive invocation checks in the superclass (which is Object) with `publicInstanceOnly == true`. @@ -172,9 +185,12 @@ abstract class ByteCodeRepository extends PerRunInit { findMethod(owner) match { case Some(m) if !publicInstanceOnly || (isPublicMethod(m) && !isStaticMethod(m)) => Right(Some((m, owner.name))) case _ => - if (isSignaturePolymorphic(owner.name)) Right(Some((owner.methods.asScala.find(_.name == name).get, owner.name))) - else if (owner.superName == null) Right(None) - else classNode(owner.superName).flatMap(findInSuperClasses(_, publicInstanceOnly = isInterface(owner))) + findSignaturePolymorphic(owner) match { + case Some(m) => Right(Some((m, owner.name))) + case _ => + if (owner.superName == null) Right(None) + else classNode(owner.superName).flatMap(findInSuperClasses(_, publicInstanceOnly = isInterface(owner))) + } } } @@ -276,25 +292,32 @@ abstract class ByteCodeRepository extends PerRunInit { private def parseClass(internalName: InternalName): Either[ClassNotFound, ClassNode] = { val fullName = internalName.replace('/', '.') - backendClassPath.findClassFile(fullName) map { classFile => + backendClassPath.findClassFile(fullName).flatMap { classFile => val classNode = new ClassNode1 val classReader = new asm.ClassReader(classFile.toByteArray) - // Passing the InlineInfoAttributePrototype makes the ClassReader invoke the specific `read` - // method of the InlineInfoAttribute class, instead of putting the byte array into a generic - // Attribute. - // We don't need frames when inlining, but we want to keep the local variable table, so we - // don't use SKIP_DEBUG. - classReader.accept(classNode, Array[Attribute](InlineInfoAttributePrototype), asm.ClassReader.SKIP_FRAMES) - // SKIP_FRAMES leaves line number nodes. Remove them because they are not correct after - // inlining. - // TODO: we need to remove them also for classes that are not parsed from classfiles, why not simplify and do it once when inlining? - // OR: instead of skipping line numbers for inlined code, use write a SourceDebugExtension - // attribute that contains JSR-45 data that encodes debugging info. + try { + // Passing the InlineInfoAttributePrototype makes the ClassReader invoke the specific `read` + // method of the InlineInfoAttribute class, instead of putting the byte array into a generic + // Attribute. + // We don't need frames when inlining, but we want to keep the local variable table, so we + // don't use SKIP_DEBUG. + classReader.accept(classNode, Array[Attribute](InlineInfoAttributePrototype), asm.ClassReader.SKIP_FRAMES) + // SKIP_FRAMES leaves line number nodes. Remove them because they are not correct after + // inlining. + // TODO: we need to remove them also for classes that are not parsed from classfiles, why not simplify and do it once when inlining? + // OR: instead of skipping line numbers for inlined code, use write a SourceDebugExtension + // attribute that contains JSR-45 data that encodes debugging info. // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.11 - // https://jcp.org/aboutJava/communityprocess/final/jsr045/index.html - removeLineNumbersAndAddLMFImplMethods(classNode) - classNode + // https://jcp.org/aboutJava/communityprocess/final/jsr045/index.html + removeLineNumbersAndAddLMFImplMethods(classNode) + Some(classNode) + } catch { + case ex: Exception => + if (frontendAccess.compilerSettings.debug) ex.printStackTrace() + backendReporting.warning(NoPosition, s"Error while reading InlineInfoAttribute from ${fullName}\n${ex.getMessage}") + None + } } match { case Some(node) => Right(node) case None => Left(ClassNotFound(internalName, javaDefinedClasses.get(internalName))) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala index 3da843e45244..5b58d29ecd6b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala @@ -126,6 +126,8 @@ object BytecodeUtils { def isNativeMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_NATIVE) != 0 + def isVarargsMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_VARARGS) != 0 + def isSyntheticMethod(methodNode: MethodNode): Boolean = (methodNode.access & ACC_SYNTHETIC) != 0 // cross-jdk diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index 3f8ee1166a08..770e680012c0 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -239,6 +239,9 @@ trait JavaScanners extends ast.parser.ScannersCommon { */ protected def putChar(c: Char): Unit = { cbuf.append(c) } + /** Remove the last N characters from the buffer */ + private def popNChars(n: Int): Unit = if (n > 0) cbuf.setLength(cbuf.length - n) + /** Clear buffer and set name */ private def setName(): Unit = { name = newTermName(cbuf.toString()) @@ -322,15 +325,26 @@ trait JavaScanners extends ast.parser.ScannersCommon { case '\"' => in.next() - while (in.ch != '\"' && (in.isUnicode || in.ch != CR && in.ch != LF && in.ch != SU)) { - getlitch() - } - if (in.ch == '\"') { - token = STRINGLIT - setName() - in.next() + if (in.ch != '\"') { // "..." non-empty string literal + while (in.ch != '\"' && (in.isUnicode || in.ch != CR && in.ch != LF && in.ch != SU)) { + getlitch() + } + if (in.ch == '\"') { + token = STRINGLIT + setName() + in.next() + } else { + syntaxError("unclosed string literal") + } } else { - syntaxError("unclosed string literal") + in.next() + if (in.ch != '\"') { // "" empty string literal + token = STRINGLIT + setName() + } else { + in.next() + getTextBlock() + } } return @@ -664,9 +678,12 @@ trait JavaScanners extends ast.parser.ScannersCommon { // Literals ----------------------------------------------------------------- /** read next character in character or string literal: - */ - protected def getlitch() = - if (in.ch == '\\') { + * + * @param scanOnly skip emitting errors or adding to the literal buffer + * @param inTextBlock is this for a text block? + */ + protected def getlitch(scanOnly: Boolean = false, inTextBlock: Boolean = false): Unit = { + val c: Char = if (in.ch == '\\') { in.next() if ('0' <= in.ch && in.ch <= '7') { val leadch: Char = in.ch @@ -680,27 +697,147 @@ trait JavaScanners extends ast.parser.ScannersCommon { in.next() } } - putChar(oct.asInstanceOf[Char]) + oct.asInstanceOf[Char] } else { - in.ch match { - case 'b' => putChar('\b') - case 't' => putChar('\t') - case 'n' => putChar('\n') - case 'f' => putChar('\f') - case 'r' => putChar('\r') - case '\"' => putChar('\"') - case '\'' => putChar('\'') - case '\\' => putChar('\\') + val c: Char = in.ch match { + case 'b' => '\b' + case 's' => ' ' + case 't' => '\t' + case 'n' => '\n' + case 'f' => '\f' + case 'r' => '\r' + case '\"' => '\"' + case '\'' => '\'' + case '\\' => '\\' + case CR | LF if inTextBlock => + in.next() + return case _ => - syntaxError(in.cpos - 1, "invalid escape character") - putChar(in.ch) + if (!scanOnly) syntaxError(in.cpos - 1, "invalid escape character") + in.ch } in.next() + c } } else { - putChar(in.ch) + val c = in.ch in.next() + c } + if (!scanOnly) putChar(c) + } + + /** read a triple-quote delimited text block, starting after the first three + * double quotes + */ + private def getTextBlock(): Unit = { + // Open delimiter is followed by optional space, then a newline + while (in.ch == ' ' || in.ch == '\t' || in.ch == FF) { + in.next() + } + if (in.ch != LF && in.ch != CR) { // CR-LF is already normalized into LF by `JavaCharArrayReader` + syntaxError("illegal text block open delimiter sequence, missing line terminator") + return + } + in.next() + + /* Do a lookahead scan over the full text block to: + * - compute common white space prefix + * - find the offset where the text block ends + */ + var commonWhiteSpacePrefix = Int.MaxValue + var blockEndOffset = 0 + val backtrackTo = in.copy + var blockClosed = false + var lineWhiteSpacePrefix = 0 + var lineIsOnlyWhitespace = true + while (!blockClosed && (in.isUnicode || in.ch != SU)) { + if (in.ch == '\"') { // Potential end of the block + in.next() + if (in.ch == '\"') { + in.next() + if (in.ch == '\"') { + blockClosed = true + commonWhiteSpacePrefix = commonWhiteSpacePrefix min lineWhiteSpacePrefix + blockEndOffset = in.cpos - 2 + } + } + + // Not the end of the block - just a single or double " character + if (!blockClosed) { + lineIsOnlyWhitespace = false + } + } else if (in.ch == CR || in.ch == LF) { // new line in the block + in.next() + if (!lineIsOnlyWhitespace) { + commonWhiteSpacePrefix = commonWhiteSpacePrefix min lineWhiteSpacePrefix + } + lineWhiteSpacePrefix = 0 + lineIsOnlyWhitespace = true + } else if (lineIsOnlyWhitespace && Character.isWhitespace(in.ch)) { // extend white space prefix + in.next() + lineWhiteSpacePrefix += 1 + } else { + lineIsOnlyWhitespace = false + getlitch(scanOnly = true, inTextBlock = true) + } + } + + // Bail out if the block never did have an end + if (!blockClosed) { + syntaxError("unclosed text block") + return + } + + // Second pass: construct the literal string value this time + in = backtrackTo + while (in.cpos < blockEndOffset) { + // Drop the line's leading whitespace + var remainingPrefix = commonWhiteSpacePrefix + while (remainingPrefix > 0 && in.ch != CR && in.ch != LF && in.cpos < blockEndOffset) { + in.next() + remainingPrefix -= 1 + } + + var trailingWhitespaceLength = 0 + var escapedNewline = false // Does the line end with `\`? + while (in.ch != CR && in.ch != LF && in.cpos < blockEndOffset && !escapedNewline) { + if (Character.isWhitespace(in.ch)) { + trailingWhitespaceLength += 1 + } else { + trailingWhitespaceLength = 0 + } + + // Detect if the line is about to end with `\` + if (in.ch == '\\' && { + val lookahead = in.copy + lookahead.next() + lookahead.ch == CR || lookahead.ch == LF + }) { + escapedNewline = true + } + + getlitch(scanOnly = false, inTextBlock = true) + } + + // Drop the line's trailing whitespace + popNChars(trailingWhitespaceLength) + + // Normalize line terminators + if ((in.ch == CR || in.ch == LF) && !escapedNewline) { + in.next() + putChar('\n') + } + } + + token = STRINGLIT + setName() + + // Trailing """ + in.next() + in.next() + in.next() + } /** read fractional part and exponent of floating point number * if one is present. diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala index 888c707a7c25..39edb8100815 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala @@ -142,19 +142,14 @@ object Plugin { ignoring: List[String], findPluginClassloader: (Seq[Path] => ClassLoader)): List[Try[AnyClass]] = { - def targeted(targets: List[List[Path]]) = targets.map { path => - val loader = findPluginClassloader(path) + def pluginResource(classpath: List[Path], loader: ClassLoader) = loader.getResource(PluginXML) match { - case null => Failure(new MissingPluginException(path)) + case null => Failure(new MissingPluginException(classpath)) case url => val inputStream = url.openStream - try { - Try((PluginDescription.fromXML(inputStream), loader)) - } finally { - inputStream.close() - } + try Try((PluginDescription.fromXML(inputStream), loader)) finally inputStream.close() } - } + def targeted(targets: List[List[Path]]) = targets.filter(_.nonEmpty).map(classpath => pluginResource(classpath, findPluginClassloader(classpath))) def dirList(dir: Path) = if (dir.isDirectory) dir.toDirectory.files.filter(Jar.isJarOrZip).toList.sortBy(_.name) else Nil // ask plugin loaders for plugin resources, but ignore if none in -Xpluginsdir @@ -179,9 +174,8 @@ object Plugin { /** Instantiate a plugin class, given the class and * the compiler it is to be used in. */ - def instantiate(clazz: AnyClass, global: Global): Plugin = { - (clazz getConstructor classOf[Global] newInstance global).asInstanceOf[Plugin] - } + def instantiate(clazz: AnyClass, global: Global): Plugin = + clazz.getConstructor(classOf[Global]).newInstance(global).asInstanceOf[Plugin] } class PluginLoadException(val path: String, message: String, cause: Exception) extends Exception(message, cause) { diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 89da75e9628e..c808cc59a21c 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -150,7 +150,7 @@ trait Plugins { global: Global => } globalError("bad option: -P:" + opt) // Plugins may opt out, unless we just want to show info - plugs filter (p => p.init(p.options, globalError) || (settings.debug && settings.isInfo)) + plugs filter (p => p.init(p.options, globalError) || (settings.isDebug && settings.isInfo)) } lazy val plugins: List[Plugin] = loadPlugins() diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala index 4262ec054914..219906e77fd8 100644 --- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -119,7 +119,7 @@ abstract class FilteringReporter extends Reporter { // Invoked when an error or warning is filtered by position. @inline def suppress = { if (settings.prompt) doReport(pos, msg, severity) - else if (settings.debug) doReport(pos, s"[ suppressed ] $msg", severity) + else if (settings.isDebug) doReport(pos, s"[ suppressed ] $msg", severity) Suppress } if (!duplicateOk(pos, severity, msg)) suppress else if (!maxOk) Count else Display diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index d070a7870652..1b25f95f46c6 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -23,7 +23,7 @@ import scala.language.existentials import scala.annotation.elidable import scala.tools.util.PathResolver.Defaults import scala.collection.mutable -import scala.reflect.internal.util.StringContextStripMarginOps +import scala.reflect.internal.util.{ StatisticsStatics, StringContextStripMarginOps } import scala.tools.nsc.util.DefaultJarFactory import scala.util.chaining._ @@ -92,25 +92,13 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett } withAbbreviation "--release" def releaseValue: Option[String] = Option(release.value).filter(_ != "") - /* - * The previous "-Xsource" option is intended to be used mainly - * though this helper. - */ - private[this] val version212 = ScalaVersion("2.12.0") - def isScala212: Boolean = source.value >= version212 - private[this] val version213 = ScalaVersion("2.13.0") - def isScala213: Boolean = source.value >= version213 - private[this] val version214 = ScalaVersion("2.14.0") - private[this] val version3 = ScalaVersion("3.0.0") - def isScala3: Boolean = source.value >= version3 - /** * -X "Advanced" settings */ val Xhelp = BooleanSetting ("-X", "Print a synopsis of advanced options.") val async = BooleanSetting ("-Xasync", "Enable the async phase for scala.async.Async.{async,await}.") val checkInit = BooleanSetting ("-Xcheckinit", "Wrap field accessors to throw an exception on uninitialized access.") - val developer = BooleanSetting ("-Xdev", "Issue warnings about anything which seems amiss in compiler internals. Intended for compiler developers") + val developer = BooleanSetting ("-Xdev", "Issue warnings about anything which seems amiss in compiler internals. Intended for compiler developers").withPostSetHook(s => if (s.value) StatisticsStatics.enableDeveloperAndDeoptimize()) val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions or assumptions.") andThen (flag => if (flag) elidebelow.value = elidable.ASSERTION + 1) val elidebelow = IntSetting ("-Xelide-below", "Calls to @elidable methods are omitted if method priority is lower than argument", @@ -145,10 +133,16 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett val mainClass = StringSetting ("-Xmain-class", "path", "Class for manifest's Main-Class entry (only useful with -d )", "") val sourceReader = StringSetting ("-Xsource-reader", "classname", "Specify a custom method for reading source files.", "") val reporter = StringSetting ("-Xreporter", "classname", "Specify a custom subclass of FilteringReporter for compiler messages.", "scala.tools.nsc.reporters.ConsoleReporter") - val source = ScalaVersionSetting ("-Xsource", "version", "Enable features that will be available in a future version of Scala, for purposes of early migration and alpha testing.", initial = version213).withPostSetHook { s => - if (s.value < version213) errorFn.apply(s"-Xsource must be at least the current major version (${version213.versionString})") - if (s.value >= version214 && s.value < version3) s.withDeprecationMessage("instead of -Xsource:2.14, use -Xsource:3").value = version3 + val source = ScalaVersionSetting ("-Xsource", "version", "Enable features that will be available in a future version of Scala, for purposes of early migration and alpha testing.", initial = ScalaVersion("2.13")).withPostSetHook { s => + if (s.value >= ScalaVersion("3")) + isScala3.value = true + else if (s.value >= ScalaVersion("2.14")) + s.withDeprecationMessage("instead of -Xsource:2.14, use -Xsource:3").value = ScalaVersion("3") + else if (s.value < ScalaVersion("2.13")) + errorFn.apply(s"-Xsource must be at least the current major version (${ScalaVersion("2.13").versionString})") } + val isScala3 = BooleanSetting ("isScala3", "Is -Xsource Scala 3?").internalOnly() + // The previous "-Xsource" option is intended to be used mainly though ^ helper val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.") @@ -459,7 +453,7 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett */ val Vhelp = BooleanSetting("-V", "Print a synopsis of verbose options.") val browse = PhasesSetting("-Vbrowse", "Browse the abstract syntax tree after") withAbbreviation "-Ybrowse" - val debug = BooleanSetting("-Vdebug", "Increase the quantity of debugging output.") withAbbreviation "-Ydebug" + val debug = BooleanSetting("-Vdebug", "Increase the quantity of debugging output.") withAbbreviation "-Ydebug" withPostSetHook (s => if (s.value) StatisticsStatics.enableDebugAndDeoptimize()) val YdebugTasty = BooleanSetting("-Vdebug-tasty", "Increase the quantity of debugging output when unpickling tasty.") withAbbreviation "-Ydebug-tasty" val Ydocdebug = BooleanSetting("-Vdoc", "Trace scaladoc activity.") withAbbreviation "-Ydoc-debug" val Yidedebug = BooleanSetting("-Vide", "Generate, validate and output trees using the interactive compiler.") withAbbreviation "-Yide-debug" @@ -502,13 +496,15 @@ trait ScalaSettings extends StandardScalaSettings with Warnings { _: MutableSett val Ystatistics = PhasesSetting("-Vstatistics", "Print compiler statistics for specific phases", "parser,typer,patmat,erasure,cleanup,jvm") .withPostSetHook(s => YstatisticsEnabled.value = s.value.nonEmpty) .withAbbreviation("-Ystatistics") - val YstatisticsEnabled = BooleanSetting("-Ystatistics-enabled", "Internal setting, indicating that statistics are enabled for some phase.").internalOnly() + val YstatisticsEnabled = BooleanSetting("-Ystatistics-enabled", "Internal setting, indicating that statistics are enabled for some phase.").internalOnly().withPostSetHook(s => if (s) StatisticsStatics.enableColdStatsAndDeoptimize()) val YhotStatisticsEnabled = BooleanSetting("-Vhot-statistics", s"Enable `${Ystatistics.name}` to also print hot statistics.") - .withAbbreviation("-Yhot-statistics") + .withAbbreviation("-Yhot-statistics").withPostSetHook(s => if (s && YstatisticsEnabled) StatisticsStatics.enableHotStatsAndDeoptimize()) val Yshowsyms = BooleanSetting("-Vsymbols", "Print the AST symbol hierarchy after each phase.") withAbbreviation "-Yshow-syms" val Ytyperdebug = BooleanSetting("-Vtyper", "Trace type assignments.") withAbbreviation "-Ytyper-debug" - val XlogImplicits = BooleanSetting("-Vimplicits", "Show more detail on why some implicits are not applicable.") - .withAbbreviation("-Xlog-implicits") + val Vimplicits = BooleanSetting("-Vimplicits", "Print dependent missing implicits.").withAbbreviation("-Xlog-implicits") + val VimplicitsVerboseTree = BooleanSetting("-Vimplicits-verbose-tree", "Display all intermediate implicits in a chain.") + val VimplicitsMaxRefined = IntSetting("-Vimplicits-max-refined", "max chars for printing refined types, abbreviate to `F {...}`", Int.MaxValue, Some((0, Int.MaxValue)), _ => None) + val VtypeDiffs = BooleanSetting("-Vtype-diffs", "Print found/required error messages as colored diffs.") val logImplicitConv = BooleanSetting("-Vimplicit-conversions", "Print a message whenever an implicit conversion is inserted.") .withAbbreviation("-Xlog-implicit-conversions") val logReflectiveCalls = BooleanSetting("-Vreflective-calls", "Print a message when a reflective method call is generated") diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 4e8ad9ab2f59..fa53c37a9263 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -19,7 +19,7 @@ import java.io.IOException import scala.reflect.internal.MissingRequirementError import scala.reflect.io.{AbstractFile, NoAbstractFile} import scala.tools.nsc.util.{ClassPath, ClassRepresentation} -import scala.reflect.internal.util.{ReusableInstance, StatisticsStatics} +import scala.reflect.internal.util.ReusableInstance import scala.tools.nsc.Reporting.WarningCategory /** This class ... @@ -57,7 +57,7 @@ abstract class SymbolLoaders { } protected def signalError(root: Symbol, ex: Throwable): Unit = { - if (settings.debug) ex.printStackTrace() + if (settings.isDebug) ex.printStackTrace() globalError(ex.getMessage() match { case null => "i/o error while loading " + root.name case msg => "error while loading " + root.name + ", " + msg @@ -312,7 +312,7 @@ abstract class SymbolLoaders { } } } - private lazy val classFileDataReader: ReusableInstance[ReusableDataReader] = ReusableInstance[ReusableDataReader](new ReusableDataReader(), enabled = isCompilerUniverse) + private lazy val classFileDataReader: ReusableInstance[ReusableDataReader] = ReusableInstance[ReusableDataReader](new ReusableDataReader(), initialSize = 1, enabled = isCompilerUniverse) class ClassfileLoader(val classfile: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol) extends SymbolLoader with FlagAssigningCompleter { private object classfileParser extends { val symbolTable: SymbolLoaders.this.symbolTable.type = SymbolLoaders.this.symbolTable @@ -337,11 +337,11 @@ abstract class SymbolLoaders { protected def description = "class file "+ classfile.toString protected def doComplete(root: Symbol): Unit = { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.classReadNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.classReadNanos) else null classfileParser.parse(classfile, clazz, module) if (clazz.associatedFile eq NoAbstractFile) clazz.associatedFile = classfile if (module.associatedFile eq NoAbstractFile) module.associatedFile = classfile - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.classReadNanos, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.classReadNanos, start) } override def sourcefile: Option[AbstractFile] = classfileParser.srcfile override def associatedFile(self: Symbol): AbstractFile = classfile diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala index e99ed0858a03..7a0af81ee22a 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala @@ -133,7 +133,7 @@ trait SymbolTrackers { else " (" + Flags.flagsToString(masked) + ")" } def symString(sym: Symbol) = ( - if (settings.debug && sym.hasCompleteInfo) { + if (settings.isDebug && sym.hasCompleteInfo) { val s = sym.defString take 240 if (s.length == 240) s + "..." else s } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index e1a218f5df4b..6816c6d01940 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -117,11 +117,11 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { } private def handleMissing(e: MissingRequirementError) = { - if (settings.debug) e.printStackTrace + if (settings.isDebug) e.printStackTrace throw new IOException(s"Missing dependency '${e.req}', required by $file") } private def handleError(e: Exception) = { - if (settings.debug) e.printStackTrace() + if (settings.isDebug) e.printStackTrace() throw new IOException(s"class file '$file' is broken\n(${e.getClass}/${e.getMessage})") } private def mismatchError(c: Symbol) = { @@ -420,7 +420,8 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { // - better owner than `NoSymbol` // - remove eager warning val msg = s"Class $name not found - continuing with a stub." - if ((!settings.isScaladoc) && (settings.verbose || settings.developer)) loaders.warning(NoPosition, msg, WarningCategory.OtherDebug, clazz.fullNameString) + if ((!settings.isScaladoc) && (settings.verbose || settings.isDeveloper)) + loaders.warning(NoPosition, msg, WarningCategory.OtherDebug, clazz.fullNameString) NoSymbol.newStubSymbol(name.toTypeName, msg) } @@ -471,7 +472,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { case ex: FatalError => // getClassByName can throw a MissingRequirementError (which extends FatalError) // definitions.getMember can throw a FatalError, for example in pos/t5165b - if (settings.debug) + if (settings.isDebug) ex.printStackTrace() stubClassSymbol(newTypeName(name)) } @@ -1007,7 +1008,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) { // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), // and that should never be swallowed silently. loaders.warning(NoPosition, s"Caught: $ex while parsing annotations in ${file}", WarningCategory.Other, clazz.fullNameString) - if (settings.debug) ex.printStackTrace() + if (settings.isDebug) ex.printStackTrace() None // ignore malformed annotations } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 23ef2573d91a..029be7dd30c5 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -112,7 +112,7 @@ abstract class Pickler extends SubComponent { // // OPT: do this only as a recovery after fatal error. Checking in advance was expensive. if (t.isErroneous) { - if (settings.debug) e.printStackTrace() + if (settings.isDebug) e.printStackTrace() reporter.error(t.pos, "erroneous or inaccessible type") return } diff --git a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala index 8a10f400b61a..45ae91f1fc67 100644 --- a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala +++ b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala @@ -36,7 +36,6 @@ class TreeUnpickler[Tasty <: TastyUniverse]( nameAtRef: NameRef => TastyName)(implicit val tasty: Tasty) { self => import tasty._ - import FlagSets._ import TreeUnpickler._ import MaybeCycle._ import TastyModes._ @@ -70,8 +69,12 @@ class TreeUnpickler[Tasty <: TastyUniverse]( //---------------- unpickling trees ---------------------------------------------------------------------------------- - private def registerSym(addr: Addr, sym: Symbol)(implicit ctx: Context) = { - ctx.log(s"$addr registered ${showSym(sym)} in ${location(sym.owner)}") + private def registerSym(addr: Addr, sym: Symbol, rejected: Boolean)(implicit ctx: Context) = { + assert(!(rejected && isSymbol(sym)), "expected no symbol when rejected") + ctx.log( + if (isSymbol(sym)) s"$addr registered ${showSym(sym)} in ${location(sym.owner)}" + else s"$addr registering symbol was rejected" + ) symAtAddr(addr) = sym } @@ -415,20 +418,23 @@ class TreeUnpickler[Tasty <: TastyUniverse]( if (isType) prior.toTypeName else prior } - private def normalizeFlags(tag: Int, tastyFlags: TastyFlagSet, name: TastyName, isAbsType: Boolean, isClass: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): TastyFlagSet = { + private def addInferredFlags(tag: Int, tastyFlags: TastyFlagSet, name: TastyName, isAbsType: Boolean, isClass: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): TastyFlagSet = { var flags = tastyFlags val lacksDefinition = rhsIsEmpty && - name.isTermName && !name.isConstructorName && !flags.isOneOf(TermParamOrAccessor) || + name.isTermName && !name.isConstructorName && !flags.isOneOf(FlagSets.TermParamOrAccessor) || isAbsType || flags.is(Opaque) && !isClass if (lacksDefinition && tag != PARAM) flags |= Deferred if (isClass && flags.is(Trait)) flags |= Abstract if (tag === DEFDEF) flags |= Method if (tag === VALDEF) { - if (flags.is(Inline) || ctx.owner.is(Trait)) flags |= FieldAccessor - if (flags.not(Mutable)) flags |= Stable - if (flags.is(SingletonEnumFlags)) flags |= Object // we will encode dotty enum constants as objects (this needs to be corrected in bytecode) + if (flags.is(Inline) || ctx.owner.is(Trait)) + flags |= FieldAccessor + if (flags.not(Mutable)) + flags |= Stable + if (flags.is(Case | Static | Enum)) // singleton enum case + flags |= Object | Stable // encode as a module (this needs to be corrected in bytecode) } if (ctx.owner.isClass) { if (tag === TYPEPARAM) flags |= Param @@ -439,7 +445,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( } } else if (isParamTag(tag)) flags |= Param - if (flags.is(Object)) flags |= (if (tag === VALDEF) ObjectCreationFlags else ObjectClassCreationFlags) + if (flags.is(Object)) flags |= (if (tag === VALDEF) FlagSets.Creation.ObjectDef else FlagSets.Creation.ObjectClassDef) flags } @@ -462,7 +468,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( createMemberSymbol() case TEMPLATE => val localDummy = ctx.newLocalDummy - registerSym(currentAddr, localDummy) + registerSym(currentAddr, localDummy, rejected = false) localDummy case tag => assert(tag != BIND, "bind pattern symbol creation from TASTy") @@ -473,12 +479,23 @@ class TreeUnpickler[Tasty <: TastyUniverse]( * @return the created symbol */ def createMemberSymbol()(implicit ctx: Context): Symbol = { + + def rejectSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet): Boolean = { + def isPureMixinCtor = + name == TastyName.MixinConstructor && owner.isTrait && flags.is(Stable) + def isInvisible = + flags.is(Invisible) + + isPureMixinCtor || isInvisible + } + val start = currentAddr val tag = readByte() def isTypeTag = tag === TYPEDEF || tag === TYPEPARAM val end = readEnd() val parsedName: TastyName = readTastyName() - ctx.log(s"$start ::: => create ${astTagToString(tag)} ${parsedName.debug}") + def debugSymCreate: String = s"${astTagToString(tag)} ${parsedName.debug}" + ctx.log(s"$start ::: => create $debugSymCreate") skipParams() val ttag = nextUnsharedTag val isAbsType = isAbstractType(ttag) @@ -487,13 +504,11 @@ class TreeUnpickler[Tasty <: TastyUniverse]( skipTree() // tpt val rhsIsEmpty = nothingButMods(end) if (!rhsIsEmpty) skipTree() - val (name, flags, annotations, privateWithin) = { - val (parsedFlags, annotations, privateWithin) = - readModifiers(end, readTypedAnnot, readTypedWithin, noSymbol) - val name = normalizeName(isTypeTag, parsedName) - val flags = normalizeFlags(tag, parsedFlags, name, isAbsType, isClass, rhsIsEmpty) - (name, flags, annotations, privateWithin) - } + val (parsedFlags0, annotations, privateWithin) = + readModifiers(end, readTypedAnnot, readTypedWithin, noSymbol) + val name = normalizeName(isTypeTag, parsedName) + val flags = addInferredFlags(tag, parsedFlags0, name, isAbsType, isClass, rhsIsEmpty) + def mkCompleter = new Completer(isClass, subReader(start, end), flags)(ctx.retractMode(IndexScopedStats)) def isTypeParameter = flags.is(Param) && isTypeTag def canEnterInClass = !isTypeParameter ctx.log { @@ -507,34 +522,46 @@ class TreeUnpickler[Tasty <: TastyUniverse]( } s"""$start parsed flags $debugFlags""" } + val rejected = rejectSymbol(ctx.owner, name, flags) val sym = { if (tag === TYPEPARAM && ctx.owner.isConstructor) { + // TASTy encodes type parameters for constructors + // nsc only has class type parameters ctx.findOuterClassTypeParameter(name.toTypeName) } else { - val completer = new Completer(isClass, subReader(start, end), flags)(ctx.retractMode(IndexScopedStats)) ctx.findRootSymbol(roots, name) match { case Some(rootd) => - ctx.adjustSymbol(rootd, flags, completer, privateWithin) // dotty "removes one completion" here from the flags, which is not possible in nsc - ctx.log(s"$start replaced info of ${showSym(rootd)}") - rootd + roots -= rootd + if (rejected) { + ctx.evict(rootd) + noSymbol + } + else { + ctx.redefineSymbol(rootd, flags, mkCompleter, privateWithin) + ctx.log(s"$start replaced info of ${showSym(rootd)}") + rootd + } case _ => - if (isClass) ctx.delayClassCompletion(ctx.owner, name.toTypeName, completer, privateWithin) - else ctx.delayCompletion(ctx.owner, name, completer, privateWithin) + if (rejected) noSymbol + else if (isClass) ctx.delayClassCompletion(ctx.owner, name.toTypeName, mkCompleter, privateWithin) + else ctx.delayCompletion(ctx.owner, name, mkCompleter, privateWithin) } } - }.ensuring(isSymbol(_), s"${ctx.classRoot}: Could not create symbol at $start") - if (tag == VALDEF && flags.is(SingletonEnumFlags)) - ctx.markAsEnumSingleton(sym) - registerSym(start, sym) - if (canEnterInClass && ctx.owner.isClass) - ctx.enterIfUnseen(sym) - if (isClass) { - val localCtx = ctx.withOwner(sym) - forkAt(templateStart).indexTemplateParams()(localCtx) + } + registerSym(start, sym, rejected) + if (isSymbol(sym)) { + if (tag == VALDEF && flags.is(FlagSets.SingletonEnum)) + ctx.markAsEnumSingleton(sym) + if (canEnterInClass && ctx.owner.isClass) + ctx.enterIfUnseen(sym) + if (isClass) { + val localCtx = ctx.withOwner(sym) + forkAt(templateStart).indexTemplateParams()(localCtx) + } + ctx.adjustAnnotations(sym, annotations) } goto(start) - ctx.adjustAnnotations(sym, annotations) sym } @@ -554,7 +581,6 @@ class TreeUnpickler[Tasty <: TastyUniverse]( } nextByte match { case PRIVATE => addFlag(Private) - case INTERNAL => addFlag(Internal) case PROTECTED => addFlag(Protected) case ABSTRACT => readByte() @@ -595,6 +621,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( case PARAMalias => addFlag(ParamAlias) case EXPORTED => addFlag(Exported) case OPEN => addFlag(Open) + case INVISIBLE => addFlag(Invisible) case PRIVATEqualified => readByte() privateWithin = readWithin(ctx) @@ -759,18 +786,9 @@ class TreeUnpickler[Tasty <: TastyUniverse]( checkUnsupportedFlags(repr.tastyOnlyFlags &~ (Enum | Extension | Exported)) val tpe = readTpt()(localCtx).tpe ctx.setInfo(sym, - if (repr.originalFlagSet.is(SingletonEnumFlags)) { - val enumClass = sym.objectImplementation - val selfTpe = defn.SingleType(sym.owner.thisPrefix, sym) - val ctor = ctx.unsafeNewSymbol( - owner = enumClass, - name = TastyName.Constructor, - flags = Method, - info = defn.DefDefType(Nil, Nil :: Nil, selfTpe) - ) - enumClass.typeOfThis = selfTpe - ctx.setInfo(enumClass, defn.ClassInfoType(intersectionParts(tpe), ctor :: Nil, enumClass)) - prefixedRef(sym.owner.thisPrefix, enumClass) + if (repr.originalFlagSet.is(FlagSets.SingletonEnum)) { + ctx.completeEnumSingleton(sym, tpe) + prefixedRef(sym.owner.thisPrefix, sym.objectImplementation) } else if (sym.isFinal && isConstantType(tpe)) defn.InlineExprType(tpe) else if (sym.isMethod) defn.ExprType(tpe) @@ -1001,7 +1019,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( (tag: @switch) match { case SELECTin => val name = readTastyName() - val qual = readTerm() + val qual = readTerm() if (inParentCtor) { assert(name.isSignedConstructor, s"Parent of ${ctx.owner} is not a constructor.") skipTree() @@ -1032,7 +1050,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( tpd.SeqLiteral(until(end)(readTerm()), elemtpt) case REFINEDtpt => val refineCls = symAtAddr.getOrElse(start, ctx.newRefinementClassSymbol) - registerSym(start, refineCls) + registerSym(start, refineCls, rejected = false) typeAtAddr(start) = refineCls.ref val parent = readTpt() ctx.withOwner(refineCls).enterRefinement(parent.tpe) { refinedCtx => @@ -1081,7 +1099,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( case UNAPPLY => unsupportedTermTreeError("unapply pattern") case INLINED => unsupportedTermTreeError("inlined expression") case SELECTouter => metaprogrammingIsUnsupported // only within inline - case HOLE => assertNoMacroHole + case HOLE => abortMacroHole case _ => readPathTerm() } assert(currentAddr === end, s"$start $currentAddr $end ${astTagToString(tag)}") @@ -1098,7 +1116,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( forkAt(readAddr()).readTpt() case BLOCK => // BLOCK appears in type position when quoting a type, but only in the body of a method metaprogrammingIsUnsupported - case HOLE => assertNoMacroHole + case HOLE => abortMacroHole case tag => if (isTypeTreeTag(tag)) readTerm()(ctx.retractMode(OuterTerm)) else { @@ -1112,7 +1130,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( /** * A HOLE should never appear in TASTy for a top level class, only in quotes. */ - private def assertNoMacroHole[T]: T = assertError("Scala 3 macro hole in pickled TASTy") + private def abortMacroHole[T]: T = abortWith(msg = "Scala 3 macro hole in pickled TASTy") private def metaprogrammingIsUnsupported[T](implicit ctx: Context): T = unsupportedError("Scala 3 metaprogramming features") diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala index 7ceb9c3a082e..ca1052bb7241 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala @@ -59,26 +59,29 @@ trait ContextOps { self: TastyUniverse => } final def location(owner: Symbol): String = { - if (owner.isClass) s"${owner.kindString} ${owner.fullNameString}" + if (!isSymbol(owner)) "" + else if (owner.isClass) s"${owner.kindString} ${owner.fullNameString}" else s"${describeOwner(owner)} in ${location(owner.owner)}" } @inline final def typeError[T](msg: String): T = throw new u.TypeError(msg) - @inline final def assertError[T](msg: String): T = - throw new AssertionError(s"assertion failed: ${u.supplementErrorMessage(msg)}") + final def abortWith[T](msg: String): T = { + u.assert(false, msg) + ??? + } @inline final def assert(assertion: Boolean, msg: => Any): Unit = - if (!assertion) assertError(String.valueOf(msg)) + u.assert(assertion, msg) @inline final def assert(assertion: Boolean): Unit = - if (!assertion) assertError("") + u.assert(assertion, "") private final def findObject(owner: Symbol, name: u.Name): Symbol = { val scope = if (owner != null && owner.isClass) owner.rawInfo.decls else u.EmptyScope - val it = scope.lookupAll(name).filter(_.isModule) + val it = scope.lookupAll(name).withFilter(_.isModule) if (it.hasNext) it.next() else u.NoSymbol //throw new AssertionError(s"no module $name in ${location(owner)}") } @@ -144,10 +147,8 @@ trait ContextOps { self: TastyUniverse => final def globallyVisibleOwner: Symbol = owner.logicallyEnclosingMember final def ignoreAnnotations: Boolean = u.settings.YtastyNoAnnotations - final def verboseDebug: Boolean = u.settings.debug - def requiresLatentEntry(decl: Symbol): Boolean = decl.isScala3Macro - def neverEntered(decl: Symbol): Boolean = decl.isPureMixinCtor + def requiresLatentEntry(decl: Symbol): Boolean = decl.isScala3Inline def canEnterOverload(decl: Symbol): Boolean = { !(decl.isModule && isSymbol(findObject(thisCtx.owner, decl.name))) @@ -190,8 +191,19 @@ trait ContextOps { self: TastyUniverse => final def newLocalDummy: Symbol = owner.newLocalDummy(u.NoPosition) - final def newWildcardSym(info: Type): Symbol = - owner.newTypeParameter(u.nme.WILDCARD.toTypeName, u.NoPosition, u.NoFlags).setInfo(info) + final def newWildcard(info: Type): Symbol = + owner.newTypeParameter( + name = u.freshTypeName("_$")(u.currentFreshNameCreator), + pos = u.NoPosition, + newFlags = FlagSets.Creation.Default + ).setInfo(info) + + final def newConstructor(owner: Symbol, info: Type): Symbol = unsafeNewSymbol( + owner = owner, + name = TastyName.Constructor, + flags = Method, + info = info + ) final def findRootSymbol(roots: Set[Symbol], name: TastyName): Option[Symbol] = { import TastyName.TypeName @@ -218,7 +230,8 @@ trait ContextOps { self: TastyUniverse => final def newRefinementSymbol(parent: Type, owner: Symbol, name: TastyName, tpe: Type): Symbol = { val overridden = parent.member(encodeTastyName(name)) val isOverride = isSymbol(overridden) - var flags = if (isOverride && overridden.isType) Override else EmptyTastyFlags + var flags = EmptyTastyFlags + if (isOverride && overridden.isType) flags |= Override val info = { if (name.isTermName) { flags |= Method | Deferred @@ -247,7 +260,7 @@ trait ContextOps { self: TastyUniverse => if (completer.originalFlagSet.is(Object)) { val sourceObject = findObject(owner, encodeTermName(name)) if (isSymbol(sourceObject)) - adjustSymbol(sourceObject, completer.originalFlagSet, completer, privateWithin) + redefineSymbol(sourceObject, completer.originalFlagSet, completer, privateWithin) else default() } @@ -263,7 +276,7 @@ trait ContextOps { self: TastyUniverse => if (completer.originalFlagSet.is(Object)) { val sourceObject = findObject(owner, encodeTermName(typeName.toTermName)) if (isSymbol(sourceObject)) - adjustSymbol(sourceObject.objectImplementation, completer.originalFlagSet, completer, privateWithin) + redefineSymbol(sourceObject.objectImplementation, completer.originalFlagSet, completer, privateWithin) else default() } @@ -272,11 +285,16 @@ trait ContextOps { self: TastyUniverse => } } + def evict(sym: Symbol): Unit = { + sym.owner.rawInfo.decls.unlink(sym) + sym.info = u.NoType + } + final def enterIfUnseen(sym: Symbol): Unit = { - if (mode.is(IndexScopedStats)) - initialContext.collectLatentEvidence(owner, sym) val decl = declaringSymbolOf(sym) - if (!(requiresLatentEntry(decl) || neverEntered(decl))) + if (mode.is(IndexScopedStats)) + initialContext.collectLatentEvidence(owner, decl) + if (!requiresLatentEntry(decl)) enterIfUnseen0(owner.rawInfo.decls, decl) } @@ -293,65 +311,64 @@ trait ContextOps { self: TastyUniverse => /** Unsafe to call for creation of a object val, prefer `delayCompletion` if info is a LazyType */ - final def unsafeNewSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet, info: Type, privateWithin: Symbol = noSymbol): Symbol = - adjustSymbol(unsafeNewUntypedSymbol(owner, name, flags), info, privateWithin) + private def unsafeNewSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet, info: Type, privateWithin: Symbol = noSymbol): Symbol = + unsafeSetInfoAndPrivate(unsafeNewUntypedSymbol(owner, name, flags), info, privateWithin) /** Unsafe to call for creation of a object class, prefer `delayClassCompletion` if info is a LazyType */ - final def unsafeNewClassSymbol(owner: Symbol, typeName: TastyName.TypeName, flags: TastyFlagSet, info: Type, privateWithin: Symbol): Symbol = - adjustSymbol(unsafeNewUntypedClassSymbol(owner, typeName, flags), info, privateWithin) + private def unsafeNewClassSymbol(owner: Symbol, typeName: TastyName.TypeName, flags: TastyFlagSet, info: Type, privateWithin: Symbol): Symbol = + unsafeSetInfoAndPrivate(unsafeNewUntypedClassSymbol(owner, typeName, flags), info, privateWithin) private final def unsafeNewUntypedSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet): Symbol = { if (flags.isOneOf(Param | ParamSetter)) { if (name.isTypeName) { - owner.newTypeParameter(encodeTypeName(name.toTypeName), u.NoPosition, encodeFlagSet(flags)) + owner.newTypeParameter(encodeTypeName(name.toTypeName), u.NoPosition, newSymbolFlagSet(flags)) } else { - if (owner.isClass && flags.is(FlagSets.FieldAccessorFlags)) { - val fieldFlags = flags &~ FlagSets.FieldAccessorFlags | FlagSets.LocalFieldFlags + if (owner.isClass && flags.is(FlagSets.FieldGetter)) { + val fieldFlags = flags &~ FlagSets.FieldGetter | FlagSets.LocalField val termName = encodeTermName(name) - val getter = owner.newMethodSymbol(termName, u.NoPosition, encodeFlagSet(flags)) - val fieldSym = owner.newValue(termName, u.NoPosition, encodeFlagSet(fieldFlags)) + val getter = owner.newMethodSymbol(termName, u.NoPosition, newSymbolFlagSet(flags)) + val fieldSym = owner.newValue(termName, u.NoPosition, newSymbolFlagSet(fieldFlags)) fieldSym.info = defn.CopyInfo(getter, fieldFlags) owner.rawInfo.decls.enter(fieldSym) getter } else { - owner.newValueParameter(encodeTermName(name), u.NoPosition, encodeFlagSet(flags)) + owner.newValueParameter(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags)) } } } - else if (name === TastyName.Constructor) { - owner.newConstructor(u.NoPosition, encodeFlagSet(flags &~ Stable)) - } - else if (name === TastyName.MixinConstructor) { - owner.newMethodSymbol(u.nme.MIXIN_CONSTRUCTOR, u.NoPosition, encodeFlagSet(flags &~ Stable)) - } - else if (flags.is(FlagSets.ObjectCreationFlags)) { + else if (flags.is(FlagSets.Creation.ObjectDef)) { log(s"!!! visited module value $name first") - assert(!owner.rawInfo.decls.lookupAll(encodeTermName(name)).exists(_.isModule)) - val module = owner.newModule(encodeTermName(name), u.NoPosition, encodeFlagSet(flags)) + val module = owner.newModule(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags)) module.moduleClass.info = defn.DefaultInfo module } else if (name.isTypeName) { - owner.newTypeSymbol(encodeTypeName(name.toTypeName), u.NoPosition, encodeFlagSet(flags)) + owner.newTypeSymbol(encodeTypeName(name.toTypeName), u.NoPosition, newSymbolFlagSet(flags)) + } + else if (name === TastyName.Constructor) { + owner.newConstructor(u.NoPosition, newSymbolFlagSet(flags &~ Stable)) + } + else if (name === TastyName.MixinConstructor) { + owner.newMethodSymbol(u.nme.MIXIN_CONSTRUCTOR, u.NoPosition, newSymbolFlagSet(flags &~ Stable)) } else { - owner.newMethodSymbol(encodeTermName(name), u.NoPosition, encodeFlagSet(flags)) + owner.newMethodSymbol(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags)) } } private final def unsafeNewUntypedClassSymbol(owner: Symbol, typeName: TastyName.TypeName, flags: TastyFlagSet): Symbol = { - if (flags.is(FlagSets.ObjectClassCreationFlags)) { + if (flags.is(FlagSets.Creation.ObjectClassDef)) { log(s"!!! visited module class $typeName first") - val module = owner.newModule(encodeTermName(typeName), u.NoPosition, encodeFlagSet(FlagSets.ObjectCreationFlags)) + val module = owner.newModule(encodeTermName(typeName), u.NoPosition, FlagSets.Creation.Default) module.info = defn.DefaultInfo - module.moduleClass.flags = encodeFlagSet(flags) + module.moduleClass.flags = newSymbolFlagSet(flags) module.moduleClass } else { - owner.newClassSymbol(encodeTypeName(typeName), u.NoPosition, encodeFlagSet(flags)) + owner.newClassSymbol(encodeTypeName(typeName), u.NoPosition, newSymbolFlagSet(flags)) } } @@ -388,16 +405,29 @@ trait ContextOps { self: TastyUniverse => parentTypes } - final def removeFlags(symbol: Symbol, flags: TastyFlagSet): symbol.type = - symbol.resetFlag(encodeFlagSet(flags)) + private[bridge] final def resetFlag0(symbol: Symbol, flags: u.FlagSet): symbol.type = + symbol.resetFlag(flags) - final def addFlags(symbol: Symbol, flags: TastyFlagSet): symbol.type = - symbol.setFlag(encodeFlagSet(flags)) + final def completeEnumSingleton(sym: Symbol, tpe: Type): Unit = { + val moduleCls = sym.moduleClass + val moduleClsFlags = FlagSets.withAccess( + flags = FlagSets.Creation.ObjectClassDef, + inheritedAccess = sym.repr.originalFlagSet + ) + val selfTpe = defn.SingleType(sym.owner.thisPrefix, sym) + val ctor = newConstructor(moduleCls, selfTpe) + moduleCls.typeOfThis = selfTpe + moduleCls.flags = newSymbolFlagSet(moduleClsFlags) + moduleCls.info = defn.ClassInfoType(intersectionParts(tpe), ctor :: Nil, moduleCls) + moduleCls.privateWithin = sym.privateWithin + } - final def adjustSymbol(symbol: Symbol, flags: TastyFlagSet, info: Type, privateWithin: Symbol): symbol.type = - adjustSymbol(addFlags(symbol, flags), info, privateWithin) + final def redefineSymbol(symbol: Symbol, flags: TastyFlagSet, completer: TastyCompleter, privateWithin: Symbol): symbol.type = { + symbol.flags = newSymbolFlagSet(flags) + unsafeSetInfoAndPrivate(symbol, completer, privateWithin) + } - final def adjustSymbol(symbol: Symbol, info: Type, privateWithin: Symbol): symbol.type = { + private def unsafeSetInfoAndPrivate(symbol: Symbol, info: Type, privateWithin: Symbol): symbol.type = { symbol.privateWithin = privateWithin symbol.info = info symbol @@ -428,7 +458,7 @@ trait ContextOps { self: TastyUniverse => final def setInfo(sym: Symbol, info: Type): Unit = sym.info = info final def markAsEnumSingleton(sym: Symbol): Unit = - sym.updateAttachment(new u.DottyEnumSingleton(sym.name.toString)) + sym.updateAttachment(u.DottyEnumSingleton) final def markAsOpaqueType(sym: Symbol, alias: Type): Unit = sym.updateAttachment(new u.DottyOpaqueTypeAlias(alias)) diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala index b4f88b88c886..cc49e5131a71 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala @@ -16,29 +16,59 @@ import scala.tools.tasty.TastyFlags._ import scala.tools.nsc.tasty.TastyUniverse import scala.reflect.internal.{Flags, ModifierFlags} -/**Handles encoding of `TastyFlagSet` to `scala.reflect` flags and witnessing which flags do not map directly - * from TASTy. +/** Handles encoding of `TastyFlagSet` to `scala.reflect` flags and witnessing which flags do not map directly + * from TASTy. */ trait FlagOps { self: TastyUniverse => import self.{symbolTable => u} object FlagSets { + val TastyOnlyFlags: TastyFlagSet = ( - Erased | Internal | Inline | InlineProxy | Opaque | Extension | Given | Exported | Transparent | Enum | Infix - | Open | ParamAlias + Erased | Inline | InlineProxy | Opaque | Extension | Given | Exported | Transparent + | Enum | Infix | Open | ParamAlias | Invisible ) + + object Creation { + val ObjectDef: TastyFlagSet = Object | Lazy | Final | Stable + val ObjectClassDef: TastyFlagSet = Object | Final + val Default: u.FlagSet = newSymbolFlagSet(EmptyTastyFlags) + val BoundedType: u.FlagSet = newSymbolFlagSet(Deferred) + } + def withAccess(flags: TastyFlagSet, inheritedAccess: TastyFlagSet): TastyFlagSet = + flags | (inheritedAccess & (Private | Local | Protected)) + val SingletonEnum: TastyFlagSet = Case | Static | Enum | Stable val TermParamOrAccessor: TastyFlagSet = Param | ParamSetter - val ObjectCreationFlags: TastyFlagSet = Object | Lazy | Final | Stable - val ObjectClassCreationFlags: TastyFlagSet = Object | Final - val SingletonEnumFlags: TastyFlagSet = Case | Static | Enum | Stable - val FieldAccessorFlags: TastyFlagSet = FieldAccessor | Stable - val LocalFieldFlags: TastyFlagSet = Private | Local + val FieldGetter: TastyFlagSet = FieldAccessor | Stable + val ParamGetter: TastyFlagSet = FieldGetter | ParamSetter + val LocalField: TastyFlagSet = Private | Local + val Scala2Macro: TastyFlagSet = Erased | Macro + } + + /** For purpose of symbol initialisation, encode a `TastyFlagSet` as a `symbolTable.FlagSet`. */ + private[bridge] def newSymbolFlagSet(tflags: TastyFlagSet): u.FlagSet = + unsafeEncodeTastyFlagSet(tflags) | ModifierFlags.SCALA3X + + implicit final class SymbolFlagOps(val sym: Symbol) { + def reset(tflags: TastyFlagSet)(implicit ctx: Context): sym.type = + ctx.resetFlag0(sym, unsafeEncodeTastyFlagSet(tflags)) + def isOneOf(mask: TastyFlagSet): Boolean = + sym.hasFlag(unsafeEncodeTastyFlagSet(mask)) + def is(mask: TastyFlagSet): Boolean = + sym.hasAllFlags(unsafeEncodeTastyFlagSet(mask)) + def is(mask: TastyFlagSet, butNot: TastyFlagSet): Boolean = + if (!butNot) + sym.is(mask) + else + sym.is(mask) && sym.not(butNot) + def not(mask: TastyFlagSet): Boolean = + sym.hasNoFlags(unsafeEncodeTastyFlagSet(mask)) } - /**encodes a `TastyFlagSet` as `scala.reflect` flags and will ignore flags that can't be converted, such as - * members of `FlagSets.TastyOnlyFlags` + /** encodes a `TastyFlagSet` as a `symbolTable.FlagSet`, the flags in `FlagSets.TastyOnlyFlags` are ignored. + * @note Do not use directly to initialise symbol flags, use `newSymbolFlagSet` */ - private[bridge] def encodeFlagSet(tflags: TastyFlagSet): u.FlagSet = { + private def unsafeEncodeTastyFlagSet(tflags: TastyFlagSet): u.FlagSet = { import u.Flag var flags = u.NoFlags if (tflags.is(Private)) flags |= Flag.PRIVATE @@ -78,7 +108,6 @@ trait FlagOps { self: TastyUniverse => else { val sb = collection.mutable.ArrayBuffer.empty[String] if (flags.is(Erased)) sb += "erased" - if (flags.is(Internal)) sb += "" if (flags.is(Inline)) sb += "inline" if (flags.is(InlineProxy)) sb += "" if (flags.is(Opaque)) sb += "opaque" @@ -90,6 +119,7 @@ trait FlagOps { self: TastyUniverse => if (flags.is(Open)) sb += "open" if (flags.is(ParamAlias)) sb += "" if (flags.is(Infix)) sb += "infix" + if (flags.is(Invisible)) sb += "" sb.mkString(" | ") } } diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala index 3127ede4df3f..21afc92da34f 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala @@ -17,6 +17,7 @@ import scala.tools.nsc.tasty.SafeEq import scala.tools.nsc.tasty.{TastyUniverse, TastyModes}, TastyModes._ import scala.tools.tasty.{TastyName, Signature, TastyFlags}, TastyName.SignedName, Signature.MethodSignature, TastyFlags._ import scala.tools.tasty.ErasedTypeRef +import scala.util.chaining._ /**This layer deals with selecting a member symbol from a type using a `TastyName`, * also contains factories for making type references to symbols. @@ -46,17 +47,12 @@ trait SymbolOps { self: TastyUniverse => implicit final class SymbolDecorator(val sym: Symbol) { - def isScala3Macro: Boolean = repr.originalFlagSet.is(Inline | Macro) def isScala3Inline: Boolean = repr.originalFlagSet.is(Inline) - def isScala2Macro: Boolean = repr.originalFlagSet.is(Erased | Macro) - - def isPureMixinCtor: Boolean = isMixinCtor && repr.originalFlagSet.is(Stable) - def isMixinCtor: Boolean = u.nme.MIXIN_CONSTRUCTOR == sym.name && sym.owner.isTrait - + def isScala2Macro: Boolean = repr.originalFlagSet.is(FlagSets.Scala2Macro) def isTraitParamAccessor: Boolean = sym.owner.isTrait && repr.originalFlagSet.is(FieldAccessor|ParamSetter) def isParamGetter: Boolean = - sym.isMethod && sym.repr.originalFlagSet.is(FlagSets.FieldAccessorFlags) + sym.isMethod && sym.repr.originalFlagSet.is(FlagSets.ParamGetter) /** A computed property that should only be called on a symbol which is known to have been initialised by the * Tasty Unpickler and is not yet completed. @@ -85,18 +81,6 @@ trait SymbolOps { self: TastyUniverse => def termRef: Type = sym.preciseRef(u.NoPrefix) def preciseRef(pre: Type): Type = u.typeRef(pre, sym, Nil) def safeOwner: Symbol = if (sym.owner eq sym) sym else sym.owner - - def set(mask: TastyFlagSet)(implicit ctx: Context): sym.type = ctx.addFlags(sym, mask) - def reset(mask: TastyFlagSet)(implicit ctx: Context): sym.type = ctx.removeFlags(sym, mask) - - def isOneOf(mask: TastyFlagSet): Boolean = sym.hasFlag(encodeFlagSet(mask)) - def is(mask: TastyFlagSet): Boolean = sym.hasAllFlags(encodeFlagSet(mask)) - def is(mask: TastyFlagSet, butNot: TastyFlagSet): Boolean = - if (!butNot) - sym.is(mask) - else - sym.is(mask) && sym.not(butNot) - def not(mask: TastyFlagSet): Boolean = sym.hasNoFlags(encodeFlagSet(mask)) } /** if isConstructor, make sure it has one non-implicit parameter list */ @@ -130,7 +114,11 @@ trait SymbolOps { self: TastyUniverse => space.member(selector).orElse(lookInTypeCtor) } } - else space.member(encodeTermName(tname)) + else { + val firstTry = space.member(encodeTermName(tname)) + if (firstTry.isOverloaded) firstTry.filter(!_.isPrivateLocal) + else firstTry + } } if (isSymbol(member) && hasType(member)) member else errorMissing(space, tname) @@ -144,13 +132,13 @@ trait SymbolOps { self: TastyUniverse => val kind = if (tname.isTypeName) "type" else "term" def typeToString(tpe: Type) = { def inner(sb: StringBuilder, tpe: Type): StringBuilder = tpe match { - case u.SingleType(pre, sym) => inner(sb, pre) append '.' append ( - if (sym.isPackageObjectOrClass) s"`${sym.name}`" - else String valueOf sym.name - ) - case u.TypeRef(pre, sym, _) if sym.isTerm => - if ((pre eq u.NoPrefix) || (pre eq u.NoType)) sb append sym.name - else inner(sb, pre) append '.' append sym.name + case u.ThisType(cls) => sb append cls.fullNameString + case u.SingleType(pre, sym) => + if ((pre eq u.NoPrefix) || (pre eq u.NoType)) sb append sym.nameString + else inner(sb, pre) append '.' append sym.nameString + case u.TypeRef(pre, sym, _) => + if ((pre eq u.NoPrefix) || (pre eq u.NoType)) sb append sym.nameString + else inner(sb, pre) append '.' append sym.nameString case tpe => sb append tpe } inner(new StringBuilder(), tpe).toString @@ -171,7 +159,7 @@ trait SymbolOps { self: TastyUniverse => ctx.log(s"""<<< looking for overload in symbolOf[$space] @@ $qual: ${showSig(sig)}""") val member = space.member(encodeTermName(qual)) if (!(isSymbol(member) && hasType(member))) errorMissing(space, qual) - val (tyParamCount, argTpeRefs) = { + val (tyParamCount, paramRefs) = { val (tyParamCounts, params) = sig.params.partitionMap(identity) if (tyParamCounts.length > 1) { unsupportedError(s"method with unmergeable type parameters: $qual") @@ -180,27 +168,32 @@ trait SymbolOps { self: TastyUniverse => } def compareSym(sym: Symbol): Boolean = sym match { case sym: u.MethodSymbol => - val method = sym.tpe.asSeenFrom(space, sym.owner) - ctx.log(s">>> trying $sym: $method") - val params = method.paramss.flatten - val isJava = sym.isJavaDefined - NameErasure.sigName(method.finalResultType, isJava) === sig.result && - params.length === argTpeRefs.length && - (qual === TastyName.Constructor && tyParamCount === member.owner.typeParams.length - || tyParamCount === sym.typeParams.length) && - params.zip(argTpeRefs).forall { case (param, tpe) => NameErasure.sigName(param.tpe, isJava) === tpe } && { - ctx.log(s">>> selected ${showSym(sym)}: ${sym.tpe}") - true - } + val meth0 = u.unwrapWrapperTypes(sym.tpe.asSeenFrom(space, sym.owner)) + val paramSyms = meth0.paramss.flatten + val resTpe = meth0.finalResultType + val sameParamSize = paramSyms.length === paramRefs.length + def sameTyParamSize = tyParamCount === ({ + // the signature of a class/mixin constructor includes + // type parameters, in nsc these come from the parent. + val tyParamOwner = if (qual.isConstructorName) member.owner else sym + tyParamOwner.typeParams.length + }) + def sameParams = paramSyms.lazyZip(paramRefs).forall({ + case (paramSym, paramRef) => sameErasure(sym)(paramSym.tpe, paramRef) + }) + sameParamSize && sameTyParamSize && sameParams && sameErasure(sym)(resTpe, sig.result) case _ => ctx.log(s"""! member[$space]("$qual") ${showSym(sym)} is not a method""") false } member.asTerm.alternatives.find(compareSym).getOrElse( - typeError(s"No matching overload of $space.$qual with signature ${showSig(sig)}")) + typeError(s"No matching overload of $space.$qual with signature ${showSig(sig)}") + ).tap(overload => + ctx.log(s">>> selected ${showSym(overload)}: ${overload.tpe}") + ) } } def showSig(sig: MethodSignature[ErasedTypeRef]): String = sig.map(_.signature).show - def showSym(sym: Symbol): String = s"Symbol($sym, #${sym.id})" + def showSym(sym: Symbol): String = s"Symbol(${sym.accurateKindString} ${sym.name}, #${sym.id})" } diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala index be9ba985c945..6f6edd0de981 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala @@ -116,6 +116,7 @@ trait TreeOps { self: TastyUniverse => val sym = tree.tpe match { case u.SingleType(_, sym) => sym case u.TypeRef(_, sym, _) => sym + case u.ThisType(sym) => sym case x => throw new MatchError(x) } if (tree.tpe.prefix === u.NoPrefix && (sym.hasFlag(Flags.PACKAGE) && !sym.isPackageObjectOrClass || sym.isLocalToBlock)) { diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala index 94d9645b8ca3..f553f3a6b030 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala @@ -192,7 +192,7 @@ trait TypeOps { self: TastyUniverse => if (args.exists(tpe => tpe.isInstanceOf[u.TypeBounds] | tpe.isInstanceOf[LambdaPolyType])) { val syms = mutable.ListBuffer.empty[Symbol] def bindWildcards(tpe: Type) = tpe match { - case tpe: u.TypeBounds => ctx.newWildcardSym(tpe).tap(syms += _).pipe(_.ref) + case tpe: u.TypeBounds => ctx.newWildcard(tpe).tap(syms += _).pipe(_.ref) case tpe: LambdaPolyType => tpe.toNested case tpe => tpe } @@ -240,6 +240,9 @@ trait TypeOps { self: TastyUniverse => bounds } + private[bridge] def sameErasure(sym: Symbol)(tpe: Type, ref: ErasedTypeRef) = + NameErasure.sigName(tpe, sym) === ref + /** This is a port from Dotty of transforming a Method type to an ErasedTypeRef */ private[bridge] object NameErasure { @@ -251,9 +254,9 @@ trait TypeOps { self: TastyUniverse => * `from` and `to` must be static classes, both with one type parameter, and the same variance. * Do the same for by name types => From[T] and => To[T] */ - def translateParameterized(self: Type)(from: u.ClassSymbol, to: u.ClassSymbol, wildcardArg: Boolean = false)(implicit ctx: Context): Type = self match { + def translateParameterized(self: Type)(from: u.ClassSymbol, to: u.ClassSymbol, wildcardArg: Boolean): Type = self match { case self @ u.NullaryMethodType(tp) => - u.NullaryMethodType(translateParameterized(tp)(from, to, wildcardArg=false)) + u.NullaryMethodType(translateParameterized(tp)(from, to, wildcardArg = false)) case _ => if (self.typeSymbol.isSubClass(from)) { def elemType(tp: Type): Type = tp.dealiasWiden match { @@ -268,23 +271,23 @@ trait TypeOps { self: TastyUniverse => else self } - def translateFromRepeated(self: Type)(toArray: Boolean, translateWildcard: Boolean = false)(implicit ctx: Context): Type = { + def translateFromRepeated(self: Type)(toArray: Boolean): Type = { val seqClass = if (toArray) u.definitions.ArrayClass else u.definitions.SeqClass - if (translateWildcard && self === u.WildcardType) - seqClass.ref(u.WildcardType :: Nil) - else if (isRepeatedParam(self)) + if (isRepeatedParam(self)) // We want `Array[? <: T]` because arrays aren't covariant until after // erasure. See `tests/pos/i5140`. translateParameterized(self)(u.definitions.RepeatedParamClass, seqClass, wildcardArg = toArray) else self } - def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): ErasedTypeRef = { - val normTp = translateFromRepeated(tp)(toArray = isJava) - erasedSigName(normTp.erasure) + def sigName(tp: Type, sym: Symbol): ErasedTypeRef = { + val normTp = translateFromRepeated(tp)(toArray = sym.isJavaDefined) + erasedSigName( + u.erasure.erasure(sym)(normTp) + ) } - private def erasedSigName(erased: Type)(implicit ctx: Context): ErasedTypeRef = erased match { + private def erasedSigName(erased: Type): ErasedTypeRef = erased match { case erased: u.ExistentialType => erasedSigName(erased.underlying) case erased: u.TypeRef => import TastyName._ @@ -417,6 +420,7 @@ trait TypeOps { self: TastyUniverse => override final def complete(sym: Symbol): Unit = { underlying.ensureCompleted() sym.info = underlying.tpe + underlying.attachments.all.foreach(sym.updateAttachment(_)) } } @@ -613,7 +617,8 @@ trait TypeOps { self: TastyUniverse => val paramInfos: List[Type] = paramInfosOp() override val params: List[Symbol] = paramNames.lazyZip(paramInfos).map { - case (name, argInfo) => ctx.owner.newValueParameter(name, u.NoPosition, encodeFlagSet(defaultFlags)).setInfo(argInfo) + case (name, argInfo) => + ctx.owner.newValueParameter(name, u.NoPosition, newSymbolFlagSet(defaultFlags)).setInfo(argInfo) } val resType: Type = resultTypeOp() @@ -641,7 +646,7 @@ trait TypeOps { self: TastyUniverse => override val typeParams: List[Symbol] = paramNames.lazyZip(paramInfos).map { case (name, bounds) => val argInfo = normaliseIfBounds(bounds) - ctx.owner.newTypeParameter(name, u.NoPosition, u.Flag.DEFERRED).setInfo(argInfo) + ctx.owner.newTypeParameter(name, u.NoPosition, FlagSets.Creation.BoundedType).setInfo(argInfo) } val resType: Type = lambdaResultType(resultTypeOp()) @@ -668,7 +673,8 @@ trait TypeOps { self: TastyUniverse => val paramInfos: List[Type] = paramInfosOp() override val typeParams: List[Symbol] = paramNames.lazyZip(paramInfos).map { - case (name, argInfo) => ctx.owner.newTypeParameter(name, u.NoPosition, u.Flag.DEFERRED).setInfo(argInfo) + case (name, argInfo) => + ctx.owner.newTypeParameter(name, u.NoPosition, FlagSets.Creation.BoundedType).setInfo(argInfo) } val resType: Type = resultTypeOp() // potentially need to flatten? (probably not, happens in typer in dotty) diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 298105445393..41922c945662 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -17,14 +17,14 @@ import symtab._ import Flags._ import scala.collection._ import scala.tools.nsc.Reporting.WarningCategory +import scala.util.chaining._ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { import global._ import definitions._ import CODE._ - import treeInfo.StripCast + import treeInfo.{ SYNTH_CASE_FLAGS, isDefaultCase, StripCast } - /** the following two members override abstract members in Transform */ val phaseName: String = "cleanup" /* used in GenBCode: collects ClassDef symbols owning a main(Array[String]) method */ @@ -369,7 +369,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { /* For testing purposes, the dynamic application's condition * can be printed-out in great detail. Remove? */ - if (settings.debug) { + if (settings.isDebug) { def paramsToString(xs: Any*) = xs map (_.toString) mkString ", " val mstr = ad.symbol.tpe match { case MethodType(mparams, resType) => @@ -398,105 +398,94 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { } } - // transform scrutinee of all matches to ints - def transformSwitch(sw: Match): Tree = { import CODE._ - sw.selector.tpe.widen match { - case IntTpe => sw // can switch directly on ints - case StringTpe => - // these assumptions about the shape of the tree are justified by the codegen in MatchOptimization - val Match(Typed(selTree, _), cases) = sw: @unchecked - def selArg = selTree match { - case x: Ident => REF(x.symbol) - case x: Literal => x - case x => throw new MatchError(x) + private def transformStringSwitch(sw: Match): Tree = { import CODE._ + // these assumptions about the shape of the tree are justified by the codegen in MatchOptimization + val Match(Typed(selTree, _), cases) = sw: @unchecked + def selArg = selTree match { + case x: Ident => REF(x.symbol) + case x: Literal => x + case x => throw new MatchError(x) + } + val newSel = selTree match { + case x: Ident => atPos(x.symbol.pos)(IF (x.symbol OBJ_EQ NULL) THEN ZERO ELSE selArg.OBJ_##) + case x: Literal => atPos(x.pos) (if (x.value.value == null) ZERO else selArg.OBJ_##) + case x => throw new MatchError(x) + } + val restpe = sw.tpe + val resUnit = restpe =:= UnitTpe + val swPos = sw.pos.focus + + /* From this: + * string match { case "AaAa" => 1 case "BBBB" | "c" => 2 case _ => 3 } + * Generate this: + * string.## match { + * case 2031744 => + * if ("AaAa" equals string) goto matchEnd (1) + * else if ("BBBB" equals string) goto case2 + * else goto defaultCase + * case 99 => + * if ("c" equals string) goto case2 + * else goto defaultCase + * case _ => goto defaultCase + * } + * case2: goto matchEnd (2) + * defaultCase: goto matchEnd (3) // or `goto matchEnd (throw new MatchError(string))` if no default was given + * matchEnd(res: Int): res + * Extra labels are added for alternative patterns branches, since multiple branches in the + * resulting switch may need to correspond to a single case body. + */ + + val labels = mutable.ListBuffer.empty[LabelDef] + var defaultCaseBody = Throw(New(MatchErrorClass.tpe_*, selArg)): Tree + + def LABEL(name: String) = currentOwner.newLabel(unit.freshTermName(name), swPos).setFlag(SYNTH_CASE_FLAGS) + def newCase() = LABEL( "case").setInfo(MethodType(Nil, restpe)) + val defaultCase = LABEL("defaultCase").setInfo(MethodType(Nil, restpe)) + val matchEnd = LABEL("matchEnd").tap { lab => + // genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of + // `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending. + lab.setInfo(MethodType(if (resUnit) Nil else List(lab.newSyntheticValueParam(restpe)), restpe)) + } + def goto(sym: Symbol, params: Tree*) = REF(sym) APPLY (params: _*) + def gotoEnd(body: Tree) = if (resUnit) BLOCK(body, goto(matchEnd)) else goto(matchEnd, body) + + val casesByHash = cases.flatMap { + case cd@CaseDef(StringsPattern(strs), _, body) => + val jump = newCase() // always create a label so when its used it matches the source case (e.g. `case4()`) + strs match { + case str :: Nil => List((str, gotoEnd(body), cd.pat.pos)) + case _ => + labels += LabelDef(jump, Nil, gotoEnd(body)) + strs.map((_, goto(jump), cd.pat.pos)) } - val restpe = sw.tpe - val swPos = sw.pos.focus - - /* From this: - * string match { case "AaAa" => 1 case "BBBB" | "c" => 2 case _ => 3} - * Generate this: - * string.## match { - * case 2031744 => - * if ("AaAa" equals string) goto match1 - * else if ("BBBB" equals string) goto match2 - * else goto matchFailure - * case 99 => - * if ("c" equals string) goto match2 - * else goto matchFailure - * case _ => goto matchFailure - * } - * match1: goto matchSuccess (1) - * match2: goto matchSuccess (2) - * matchFailure: goto matchSuccess (3) // would be throw new MatchError(string) if no default was given - * matchSuccess(res: Int): res - * This proliferation of labels is needed to handle alternative patterns, since multiple branches in the - * resulting switch may need to correspond to a single case body. - */ - - val stats = mutable.ListBuffer.empty[Tree] - var failureBody = Throw(New(definitions.MatchErrorClass.tpe_*, selArg)) : Tree - - // genbcode isn't thrilled about seeing labels with Unit arguments, so `success`'s type is one of - // `${sw.tpe} => ${sw.tpe}` or `() => Unit` depending. - val success = { - val lab = currentOwner.newLabel(unit.freshTermName("matchEnd"), swPos) - if (restpe =:= UnitTpe) { - lab.setInfo(MethodType(Nil, restpe)) - } else { - lab.setInfo(MethodType(lab.newValueParameter(nme.x_1).setInfo(restpe) :: Nil, restpe)) - } + case cd if isDefaultCase(cd) => defaultCaseBody = gotoEnd(cd.body); None + case cd => globalError(s"unhandled in switch: $cd"); None + }.groupBy(_._1.##) + + val newCases = casesByHash.toList.sortBy(_._1).map { + case (hash, cases) => + val newBody = cases.foldRight(atPos(swPos)(goto(defaultCase): Tree)) { + case ((null, rhs, pos), next) => atPos(pos)(IF (NULL OBJ_EQ selArg) THEN rhs ELSE next) + case ((str, rhs, pos), next) => atPos(pos)(IF (LIT(str) OBJ_== selArg) THEN rhs ELSE next) } - def succeed(res: Tree): Tree = - if (restpe =:= UnitTpe) BLOCK(res, REF(success) APPLY Nil) else REF(success) APPLY res - - val failure = currentOwner.newLabel(unit.freshTermName("matchEnd"), swPos).setInfo(MethodType(Nil, restpe)) - def fail(): Tree = atPos(swPos) { Apply(REF(failure), Nil) } - - val ifNull = LIT(0) - val noNull = Apply(selArg DOT Object_hashCode, Nil) + CASE(LIT(hash)) ==> newBody + } - val newSel = selTree match { - case _: Ident => atPos(selTree.symbol.pos) { IF(selTree.symbol OBJ_EQ NULL) THEN ifNull ELSE noNull } - case x: Literal => atPos(selTree.pos) { if (x.value.value == null) ifNull else noNull } - case x => throw new MatchError(x) - } - val casesByHash = - cases.flatMap { - case cd@CaseDef(StringsPattern(strs), _, body) => - val jump = currentOwner.newLabel(unit.freshTermName("case"), swPos).setInfo(MethodType(Nil, restpe)) - stats += LabelDef(jump, Nil, succeed(body)) - strs.map((_, jump, cd.pat.pos)) - case cd@CaseDef(Ident(nme.WILDCARD), _, body) => - failureBody = succeed(body) - None - case cd => globalError(s"unhandled in switch: $cd"); None - }.groupBy(_._1.##) - val newCases = casesByHash.toList.sortBy(_._1).map { - case (hash, cases) => - val newBody = cases.foldLeft(fail()) { - case (next, (pat, jump, pos)) => - val comparison = if (pat == null) Object_eq else Object_equals - atPos(pos) { - IF(LIT(pat) DOT comparison APPLY selArg) THEN (REF(jump) APPLY Nil) ELSE next - } - } - CaseDef(LIT(hash), EmptyTree, newBody) - } + labels += LabelDef(defaultCase, Nil, defaultCaseBody) + labels += LabelDef(matchEnd, matchEnd.info.params, matchEnd.info.params.headOption.fold(UNIT: Tree)(REF)) - stats += LabelDef(failure, Nil, failureBody) + val stats = Match(newSel, newCases :+ (DEFAULT ==> goto(defaultCase))) :: labels.toList - stats += (if (restpe =:= UnitTpe) { - LabelDef(success, Nil, gen.mkLiteralUnit) - } else { - LabelDef(success, success.info.params.head :: Nil, REF(success.info.params.head)) - }) - - stats prepend Match(newSel, newCases :+ CaseDef(Ident(nme.WILDCARD), EmptyTree, fail())) + val res = Block(stats: _*) + localTyper.typedPos(sw.pos)(res) + } - val res = Block(stats.result() : _*) - localTyper.typedPos(sw.pos)(res) - case _ => globalError(s"unhandled switch scrutinee type ${sw.selector.tpe}: $sw"); sw + // transform scrutinee of all matches to switchable types (ints, strings) + def transformSwitch(sw: Match): Tree = { + sw.selector.tpe.widen match { + case IntTpe => sw // can switch directly on ints + case StringTpe => transformStringSwitch(sw) + case _ => globalError(s"unhandled switch scrutinee type ${sw.selector.tpe}: $sw"); sw } } @@ -596,18 +585,47 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { // with just `ArrayValue(...).$asInstanceOf[...]` // // See scala/bug#6611; we must *only* do this for literal vararg arrays. - case Apply(appMeth @ Select(appMethQual, _), Apply(wrapRefArrayMeth, (arg @ StripCast(ArrayValue(_, _))) :: Nil) :: _ :: Nil) - if wrapRefArrayMeth.symbol == currentRun.runDefinitions.wrapVarargsRefArrayMethod && appMeth.symbol == ArrayModule_genericApply && treeInfo.isQualifierSafeToElide(appMethQual) => - arg.transform(this) + case Apply(appMeth @ Select(appMethQual, _), Apply(wrapRefArrayMeth, (arg @ StripCast(ArrayValue(elemtpt, elems))) :: Nil) :: classTagEvidence :: Nil) + if (wrapRefArrayMeth.symbol == currentRun.runDefinitions.wrapVarargsRefArrayMethod || wrapRefArrayMeth.symbol == currentRun.runDefinitions.genericWrapVarargsRefArrayMethod) && appMeth.symbol == ArrayModule_genericApply && treeInfo.isQualifierSafeToElide(appMethQual) && + !elemtpt.tpe.typeSymbol.isBottomClass && !elemtpt.tpe.typeSymbol.isPrimitiveValueClass /* can happen via specialization.*/ => + classTagEvidence.attachments.get[analyzer.MacroExpansionAttachment] match { + case Some(att) if att.expandee.symbol.name == nme.materializeClassTag && tree.isInstanceOf[ApplyToImplicitArgs] => + super.transform(arg) + case _ => + localTyper.typedPos(tree.pos) { + gen.evalOnce(classTagEvidence, currentOwner, unit) { ev => + val arr = localTyper.typedPos(tree.pos)(gen.mkMethodCall(classTagEvidence, definitions.ClassTagClass.info.decl(nme.newArray), Nil, Literal(Constant(elems.size)) :: Nil)) + gen.evalOnce(arr, currentOwner, unit) { arr => + val stats = mutable.ListBuffer[Tree]() + foreachWithIndex(elems) { (elem, i) => + stats += gen.mkMethodCall(gen.mkAttributedRef(definitions.ScalaRunTimeModule), currentRun.runDefinitions.arrayUpdateMethod, + Nil, arr() :: Literal(Constant(i)) :: elem :: Nil) + } + super.transform(Block(stats.toList, arr())) + } + } + } + } case Apply(appMeth @ Select(appMethQual, _), elem0 :: Apply(wrapArrayMeth, (rest @ ArrayValue(elemtpt, _)) :: Nil) :: Nil) if wrapArrayMeth.symbol == wrapVarargsArrayMethod(elemtpt.tpe) && appMeth.symbol == ArrayModule_apply(elemtpt.tpe) && treeInfo.isQualifierSafeToElide(appMethQual) => treeCopy.ArrayValue(rest, rest.elemtpt, elem0 :: rest.elems).transform(this) + // See scala/bug#12201, should be rewrite as Primitive Array. + // Match Array + case Apply(appMeth @ Select(appMethQual, _), Apply(wrapRefArrayMeth, StripCast(ArrayValue(elemtpt, elems)) :: Nil) :: _ :: Nil) + if appMeth.symbol == ArrayModule_genericApply && treeInfo.isQualifierSafeToElide(appMethQual) && currentRun.runDefinitions.primitiveWrapArrayMethod.contains(wrapRefArrayMeth.symbol) => + localTyper.typedPos(elemtpt.pos) { + ArrayValue(TypeTree(elemtpt.tpe), elems) + } transform this case Apply(appMeth @ Select(appMethQual, _), elem :: (nil: RefTree) :: Nil) if nil.symbol == NilModule && appMeth.symbol == ArrayModule_apply(elem.tpe.widen) && treeInfo.isExprSafeToInline(nil) && treeInfo.isQualifierSafeToElide(appMethQual) => localTyper.typedPos(elem.pos) { ArrayValue(TypeTree(elem.tpe), elem :: Nil) } transform this - + case Apply(appMeth @ Select(appMethQual, _), elem :: (nil: RefTree) :: Nil) + if nil.symbol == NilModule && appMeth.symbol == ArrayModule_apply(elem.tpe.widen) && treeInfo.isExprSafeToInline(nil) && treeInfo.isQualifierSafeToElide(appMethQual) => + localTyper.typedPos(elem.pos) { + ArrayValue(TypeTree(elem.tpe), elem :: Nil) + } transform this // List(a, b, c) ~> new ::(a, new ::(b, new ::(c, Nil))) // Seq(a, b, c) ~> new ::(a, new ::(b, new ::(c, Nil))) case Apply(appMeth @ Select(appQual, _), List(Apply(wrapArrayMeth, List(StripCast(rest @ ArrayValue(elemtpt, _)))))) diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index daf574fcabe8..751134fd6b07 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -94,7 +94,7 @@ abstract class Erasure extends InfoTransform if (! ts.isEmpty && ! result) { apply(ts.head) ; untilApply(ts.tail) } } - override protected def verifyJavaErasure = settings.Xverify || settings.debug + override protected def verifyJavaErasure = settings.Xverify || settings.isDebug private def needsJavaSig(sym: Symbol, tp: Type, throwsArgs: List[Type]) = !settings.Ynogenericsig && { def needs(tp: Type) = NeedsSigCollector(sym.isClassConstructor).collect(tp) needs(tp) || throwsArgs.exists(needs) @@ -518,7 +518,7 @@ abstract class Erasure extends InfoTransform clashErrors += Tuple2(pos, msg) } for (bc <- root.baseClasses) { - if (settings.debug) + if (settings.isDebug) exitingPostErasure(println( sm"""check bridge overrides in $bc |${bc.info.nonPrivateDecl(bridge.name)} @@ -652,7 +652,7 @@ abstract class Erasure extends InfoTransform val rhs = member.tpe match { case MethodType(Nil, FoldableConstantType(c)) => Literal(c) case _ => - val sel: Tree = Select(This(root), member) + val sel: Tree = gen.mkAttributedSelect(gen.mkAttributedThis(root), member) val bridgingCall = bridge.paramss.foldLeft(sel)((fun, vparams) => Apply(fun, vparams map Ident)) maybeWrap(bridgingCall) diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 8be86de53038..3971302b1c98 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -465,7 +465,7 @@ abstract class ExplicitOuter extends InfoTransform }) super.transform(treeCopy.Apply(tree, sel, outerVal :: args)) - // for the new pattern matcher + // for the pattern matcher // base..eq(o) --> base.$outer().eq(o) if there's an accessor, else the whole tree becomes TRUE // TODO remove the synthetic `` method from outerFor?? case Apply(eqsel@Select(eqapp@Apply(sel@Select(base, nme.OUTER_SYNTH), Nil), eq), args) => @@ -488,6 +488,31 @@ abstract class ExplicitOuter extends InfoTransform transform(treeCopy.Apply(tree, treeCopy.Select(eqsel, outerSelect, eq), args)) } + // (t12312) C.this.a().X().isInstanceOf[C.this.a.X.type]() --> + // D.this.$outer().a().X().isInstanceOf[D.this.$outer.a.X.type]() + case TypeApply(fun, targs) => + val rewriteTypeToExplicitOuter = new TypeMap { typeMap => + def apply(tp: Type) = tp match { + case ThisType(sym) if sym != currentClass && !(sym.hasModuleFlag && sym.isStatic) => + var cls = currentClass + var tpe = cls.thisType + do { + tpe = singleType(tpe, outerAccessor(cls)) + cls = cls.outerClass + } while (cls != NoSymbol && sym != cls) + tpe.mapOver(typeMap) + case tp => tp.mapOver(typeMap) + } + } + val fun2 = transform(fun) + val targs2 = targs.mapConserve { targ0 => + val targ = transform(targ0) + val targTp = targ.tpe + val targTp2 = rewriteTypeToExplicitOuter(targTp.dealias) + if (targTp eq targTp2) targ else TypeTree(targTp2).setOriginal(targ) + } + treeCopy.TypeApply(tree, fun2, targs2) + case _ => val x = super.transform(tree) if (x.tpe eq null) x diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala index 6387ddde49d7..1eeb283560f3 100644 --- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala +++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala @@ -37,7 +37,7 @@ abstract class OverridingPairs extends SymbolPairs { * including bridges. But it may be refined in subclasses. */ override protected def exclude(sym: Symbol) = ( - sym.isPrivateLocal + (sym.isPrivateLocal && sym.isParamAccessor) || sym.isArtifact || sym.isConstructor || (sym.isPrivate && sym.owner != base) // Privates aren't inherited. Needed for pos/t7475a.scala @@ -54,8 +54,20 @@ abstract class OverridingPairs extends SymbolPairs { && (lowMemberType matches (self memberType high)) ) // TODO we don't call exclude(high), should we? - override def skipOwnerPair(lowClass: Symbol, highClass: Symbol): Boolean = - lowClass.isJavaDefined && highClass.isJavaDefined // javac is already checking this better than we could + override protected def skipOwnerPair(lowClass: Symbol, highClass: Symbol): Boolean = { + // Two Java-defined methods can be skipped in most cases, as javac will check the overrides; skipping is + // actually necessary to avoid false errors, as Java doesn't have the Scala's linearization rules. However, when + // a Java interface is mixed into a Scala class, mixed-in default methods need to go through override checking + // (neg/t12394). Checking is also required if the "mixed-in" Java interface method is abstract (neg/t12380). + lowClass.isJavaDefined && highClass.isJavaDefined && { + !lowClass.isJavaInterface && !highClass.isJavaInterface || { + !base.info.parents.tail.exists(p => { + val psym = p.typeSymbol + psym.isNonBottomSubClass(lowClass) || psym.isNonBottomSubClass(highClass) + }) + } + } + } } private def bothJavaOwnedAndEitherIsField(low: Symbol, high: Symbol): Boolean = { diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 0e68021ae7ca..7c10c86a7bbd 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1903,32 +1903,35 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { debuglog("specializing body of" + symbol.defString) val DefDef(_, _, tparams, vparams :: Nil, tpt, _) = tree: @unchecked val env = typeEnv(symbol) - val origtparams = source.typeParams.filter(tparam => !env.contains(tparam) || !isPrimitiveValueType(env(tparam))) - if (origtparams.nonEmpty || symbol.typeParams.nonEmpty) - debuglog("substituting " + origtparams + " for " + symbol.typeParams) + + val srcVparams = parameters(source) + val srcTparams = source.typeParams.filter(tparam => !env.contains(tparam) || !isPrimitiveValueType(env(tparam))) + if (settings.isDebug && (srcTparams.nonEmpty || symbol.typeParams.nonEmpty)) + debuglog("substituting " + srcTparams + " for " + symbol.typeParams) // skolemize type parameters - val oldtparams = tparams map (_.symbol) - val newtparams = deriveFreshSkolems(oldtparams) - map2(tparams, newtparams)(_ setSymbol _) + val oldTparams = tparams.map(_.symbol) + val newTparams = deriveFreshSkolems(oldTparams) + map2(tparams, newTparams)(_ setSymbol _) // create fresh symbols for value parameters to hold the skolem types - val newSyms = cloneSymbolsAtOwnerAndModify(vparams map (_.symbol), symbol, _.substSym(oldtparams, newtparams)) + val oldVparams = vparams.map(_.symbol) + val newVparams = cloneSymbolsAtOwnerAndModify(oldVparams, symbol, _.substSym(oldTparams, newTparams)) + + val srcParams = srcVparams ::: srcTparams + val oldParams = oldVparams ::: oldTparams + val newParams = newVparams ::: newTparams // replace value and type parameters of the old method with the new ones // log("Adding body for " + tree.symbol + " - origtparams: " + origtparams + "; tparams: " + tparams) // log("Type vars of: " + source + ": " + source.typeParams) // log("Type env of: " + tree.symbol + ": " + boundTvars) // log("newtparams: " + newtparams) - val symSubstituter = new ImplementationAdapter( - parameters(source) ::: origtparams, - newSyms ::: newtparams, - source.enclClass, - false) // don't make private fields public - - val newBody = symSubstituter(body(source).duplicate) - tpt modifyType (_.substSym(oldtparams, newtparams)) - copyDefDef(tree)(vparamss = List(newSyms map ValDef.apply), rhs = newBody) + // don't make private fields public + val substituter = new ImplementationAdapter(srcParams, newParams, source.enclClass, false) + val newRhs = substituter(body(source).duplicate) + tpt.modifyType(_.substSym(oldParams, newParams)) + copyDefDef(tree)(vparamss = newVparams.map(ValDef.apply) :: Nil, rhs = newRhs) } /** Create trees for specialized members of 'sClass', based on the diff --git a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala index 0f327b540fa8..93eb50dc6939 100644 --- a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala +++ b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala @@ -97,7 +97,7 @@ trait TypeAdaptingTransformer { self: TreeDSL => case ArrayClass => assert(pt.typeSymbol != ArrayClass, "array") ; tree case _ => val unboxer = currentRun.runDefinitions.unboxMethod(pt.typeSymbol) - if (settings.developer) assert(boxedClass(pt.typeSymbol).tpe <:< tree.tpe, s"${tree.tpe} is not a boxed ${pt}") + if (settings.isDeveloper) assert(boxedClass(pt.typeSymbol).tpe <:< tree.tpe, s"${tree.tpe} is not a boxed ${pt}") Apply(unboxer, tree) // don't `setType pt` the Apply tree, as the Apply's fun won't be typechecked if the Apply tree already has a type } } @@ -116,7 +116,7 @@ trait TypeAdaptingTransformer { self: TreeDSL => * @note Pre-condition: pt eq pt.normalize */ final def cast(tree: Tree, pt: Type): Tree = { - if (settings.debug && (tree.tpe ne null) && !(tree.tpe =:= ObjectTpe)) { + if (settings.isDebug && (tree.tpe ne null) && !(tree.tpe =:= ObjectTpe)) { def word = if (tree.tpe <:< pt) "upcast" else if (pt <:< tree.tpe) "downcast" diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index f3428ca3c677..0ee1246b3357 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -445,7 +445,7 @@ abstract class UnCurry extends InfoTransform if (sym.isMethod) level < settings.elidebelow.value else { // TODO: report error? It's already done in RefChecks. https://github.com/scala/scala/pull/5539#issuecomment-331376887 - if (currentRun.isScala213) reporter.error(sym.pos, s"${sym.name}: Only methods can be marked @elidable.") + reporter.error(sym.pos, s"${sym.name}: Only methods can be marked @elidable.") false } } diff --git a/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala b/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala index 3d6a550b08d0..19924c2ffb70 100644 --- a/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala +++ b/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala @@ -55,6 +55,8 @@ private[async] trait AnfTransform extends TransformUtils { tree case _ if !treeContainsAwait => tree + case Apply(TypeApply(sel@Select(_, _), _), arg :: Nil) if sel.symbol == definitions.Object_synchronized && containsAwait(arg) => + tree // pass through unchanged, this will be reported as an error later case Apply(sel@Select(fun, _), arg :: Nil) if isBooleanAnd(sel.symbol) && containsAwait(arg) => transform(treeCopy.If(tree, fun, arg, literalBool(false))) case Apply(sel@Select(fun, _), arg :: Nil) if isBooleanOr(sel.symbol) && containsAwait(arg) => diff --git a/src/compiler/scala/tools/nsc/transform/async/AsyncAnalysis.scala b/src/compiler/scala/tools/nsc/transform/async/AsyncAnalysis.scala index 2fae40c492e1..201e474628a4 100644 --- a/src/compiler/scala/tools/nsc/transform/async/AsyncAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/async/AsyncAnalysis.scala @@ -45,8 +45,8 @@ trait AsyncAnalysis extends TransformUtils { reportUnsupportedAwait(defDef, "nested method") } - override def byNameArgument(arg: Tree): Unit = { - reportUnsupportedAwait(arg, "by-name argument") + override def synchronizedCall(arg: Tree): Unit = { + reportUnsupportedAwait(arg, "synchronized call") } override def function(function: Function): Unit = { diff --git a/src/compiler/scala/tools/nsc/transform/async/AsyncPhase.scala b/src/compiler/scala/tools/nsc/transform/async/AsyncPhase.scala index 60d7c510723f..dd6f2f491640 100644 --- a/src/compiler/scala/tools/nsc/transform/async/AsyncPhase.scala +++ b/src/compiler/scala/tools/nsc/transform/async/AsyncPhase.scala @@ -178,7 +178,7 @@ abstract class AsyncPhase extends Transform with TypingTransformers with AnfTran val applyBody = atPos(asyncPos)(asyncBlock.onCompleteHandler) // Logging - if ((settings.debug.value && shouldLogAtThisPhase)) + if ((settings.isDebug && shouldLogAtThisPhase)) logDiagnostics(anfTree, asyncBlock, asyncBlock.asyncStates.map(_.toString)) // Offer async frontends a change to produce the .dot diagram transformState.dotDiagram(applySym, asyncBody).foreach(f => f(asyncBlock.toDot)) diff --git a/src/compiler/scala/tools/nsc/transform/async/ExprBuilder.scala b/src/compiler/scala/tools/nsc/transform/async/ExprBuilder.scala index 9761bf0ed6dd..6cecc2487382 100644 --- a/src/compiler/scala/tools/nsc/transform/async/ExprBuilder.scala +++ b/src/compiler/scala/tools/nsc/transform/async/ExprBuilder.scala @@ -303,7 +303,7 @@ trait ExprBuilder extends TransformUtils with AsyncAnalysis { buildStateAndOpenNextState(afterLabelState, style = StateTransitionStyle.None) } } else if (containsAwait(rhs)) { - // A while loop containg an await. We assuming that the the backward branch is reachable across the async + // A while loop containing an await. We assuming that the the backward branch is reachable across the async // code path and create a state for the `while` label. // // In theory we could avoid creating this state in code like: diff --git a/src/compiler/scala/tools/nsc/transform/async/LiveVariables.scala b/src/compiler/scala/tools/nsc/transform/async/LiveVariables.scala index f30caba93d96..01ef248e060d 100644 --- a/src/compiler/scala/tools/nsc/transform/async/LiveVariables.scala +++ b/src/compiler/scala/tools/nsc/transform/async/LiveVariables.scala @@ -91,7 +91,7 @@ trait LiveVariables extends ExprBuilder { override def nestedMethod(defdef: DefDef): Unit = capturingCheck(defdef) - override def byNameArgument(arg: Tree): Unit = capturingCheck(arg) + override def synchronizedCall(arg: Tree): Unit = capturingCheck(arg) override def function(function: Function): Unit = capturingCheck(function) override def function(expandedFunction: ClassDef): Unit = capturingCheck(expandedFunction) diff --git a/src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala b/src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala index 69e2888447a9..70cc6e317171 100644 --- a/src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala +++ b/src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala @@ -123,7 +123,7 @@ private[async] trait TransformUtils extends AsyncTransformStates { def nestedMethod(defdef: DefDef): Unit = { } - def byNameArgument(arg: Tree): Unit = { + def synchronizedCall(arg: Tree): Unit = { } def function(function: Function): Unit = { @@ -134,13 +134,14 @@ private[async] trait TransformUtils extends AsyncTransformStates { override def traverse(tree: Tree): Unit = { tree match { - case cd: ClassDef => + case cd: ClassDef => if (cd.symbol.isAnonymousClass) function(cd) else if (cd.symbol.isModuleClass) nestedModuleClass(cd) else nestedClass(cd) - case dd: DefDef => nestedMethod(dd) - case fun: Function => function(fun) - case _ => super.traverse(tree) + case dd: DefDef => nestedMethod(dd) + case fun: Function => function(fun) + case Apply(TypeApply(fun, _), arg :: Nil) if fun.symbol == definitions.Object_synchronized => synchronizedCall(arg) + case _ => super.traverse(tree) } } } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index a575a4c933e7..551a54f9cf02 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -16,9 +16,9 @@ package tools.nsc.transform.patmat import scala.collection.mutable import scala.collection.immutable.ArraySeq import scala.reflect.internal.util.Collections._ -import scala.reflect.internal.util.{HashSet, StatisticsStatics} +import scala.reflect.internal.util.HashSet -trait Logic extends Debugging { +trait Logic extends Debugging { import global._ private def max(xs: Seq[Int]) = if (xs.isEmpty) 0 else xs.max @@ -117,12 +117,20 @@ trait Logic extends Debugging { // but that requires typing relations like And(x: Tx, y: Ty) : (if(Tx == PureProp && Ty == PureProp) PureProp else Prop) final case class And(ops: Set[Prop]) extends Prop object And { - def apply(ops: Prop*) = new And(ops.toSet) + def apply(ps: Prop*) = create(ps) + def create(ps: Iterable[Prop]) = ps match { + case ps: Set[Prop] => new And(ps) + case _ => new And(ps.to(scala.collection.immutable.ListSet)) + } } final case class Or(ops: Set[Prop]) extends Prop object Or { - def apply(ops: Prop*) = new Or(ops.toSet) + def apply(ps: Prop*) = create(ps) + def create(ps: Iterable[Prop]) = ps match { + case ps: Set[Prop] => new Or(ps) + case _ => new Or(ps.to(scala.collection.immutable.ListSet)) + } } final case class Not(a: Prop) extends Prop @@ -161,8 +169,17 @@ trait Logic extends Debugging { implicit val SymOrdering: Ordering[Sym] = Ordering.by(_.id) } - def /\(props: Iterable[Prop]) = if (props.isEmpty) True else And(props.toSeq: _*) - def \/(props: Iterable[Prop]) = if (props.isEmpty) False else Or(props.toSeq: _*) + def /\(props: Iterable[Prop]) = props match { + case _ if props.isEmpty => True + case _ if props.sizeIs == 1 => props.head + case _ => And.create(props) + } + + def \/(props: Iterable[Prop]) = props match { + case _ if props.isEmpty => False + case _ if props.sizeIs == 1 => props.head + case _ => Or.create(props) + } /** * Simplifies propositional formula according to the following rules: @@ -267,61 +284,44 @@ trait Logic extends Debugging { | (_: AtMostOne) => p } - def simplifyProp(p: Prop): Prop = p match { - case And(fv) => - // recurse for nested And (pulls all Ands up) - // build up Set in order to remove duplicates - val opsFlattenedBuilder = collection.immutable.Set.newBuilder[Prop] - for (prop <- fv) { - val simplified = simplifyProp(prop) - if (simplified != True) { // ignore `True` - simplified match { - case And(fv) => fv.foreach(opsFlattenedBuilder += _) - case f => opsFlattenedBuilder += f - } - } - } - val opsFlattened = opsFlattenedBuilder.result() - - if (opsFlattened.contains(False) || hasImpureAtom(opsFlattened)) { - False - } else { - opsFlattened.size match { - case 0 => True - case 1 => opsFlattened.head - case _ => new And(opsFlattened) - } + def simplifyAnd(ps: Set[Prop]): Prop = { + // recurse for nested And (pulls all Ands up) + // build up Set in order to remove duplicates + val props = mutable.LinkedHashSet.empty[Prop] + for (prop <- ps) { + simplifyProp(prop) match { + case True => // ignore `True` + case And(fv) => fv.foreach(props += _) + case f => props += f } - case Or(fv) => - // recurse for nested Or (pulls all Ors up) - // build up Set in order to remove duplicates - val opsFlattenedBuilder = collection.immutable.Set.newBuilder[Prop] - for (prop <- fv) { - val simplified = simplifyProp(prop) - if (simplified != False) { // ignore `False` - simplified match { - case Or(fv) => fv.foreach(opsFlattenedBuilder += _) - case f => opsFlattenedBuilder += f - } - } - } - val opsFlattened = opsFlattenedBuilder.result() - - if (opsFlattened.contains(True) || hasImpureAtom(opsFlattened)) { - True - } else { - opsFlattened.size match { - case 0 => False - case 1 => opsFlattened.head - case _ => new Or(opsFlattened) - } + } + + if (props.contains(False) || hasImpureAtom(props)) False + else /\(props) + } + + def simplifyOr(ps: Set[Prop]): Prop = { + // recurse for nested Or (pulls all Ors up) + // build up Set in order to remove duplicates + val props = mutable.LinkedHashSet.empty[Prop] + for (prop <- ps) { + simplifyProp(prop) match { + case False => // ignore `False` + case Or(fv) => props ++= fv + case f => props += f } - case Not(Not(a)) => - simplify(a) - case Not(p) => - Not(simplify(p)) - case p => - p + } + + if (props.contains(True) || hasImpureAtom(props)) True + else \/(props) + } + + def simplifyProp(p: Prop): Prop = p match { + case And(ps) => simplifyAnd(ps) + case Or(ps) => simplifyOr(ps) + case Not(Not(a)) => simplify(a) + case Not(p) => Not(simplify(p)) + case p => p } val nnf = negationNormalForm(f) @@ -344,7 +344,7 @@ trait Logic extends Debugging { } def gatherVariables(p: Prop): collection.Set[Var] = { - val vars = new mutable.HashSet[Var]() + val vars = new mutable.LinkedHashSet[Var]() (new PropTraverser { override def applyVar(v: Var) = vars += v })(p) @@ -352,7 +352,7 @@ trait Logic extends Debugging { } def gatherSymbols(p: Prop): collection.Set[Sym] = { - val syms = new mutable.HashSet[Sym]() + val syms = new mutable.LinkedHashSet[Sym]() (new PropTraverser { override def applySymbol(s: Sym) = syms += s })(p) @@ -395,7 +395,7 @@ trait Logic extends Debugging { // according to subtyping, e.g., V = ConstantType(1) and V = Int are valid assignments // we rewrite V = C to a fresh boolean symbol, and model what we know about the variable's domain // in a prelude (the equality axioms) - // 1. a variable with a closed domain (of a sealed type) must be assigned one of the instantiatable types in its domain + // 1. a variable with a closed domain (of a sealed type) must be assigned one of the instantiable types in its domain // 2. for each variable V in props, and each constant C it is compared to, // compute which assignments imply each other (as in the example above: V = 1 implies V = Int) // and which assignments are mutually exclusive (V = String implies -(V = Int)) @@ -408,7 +408,7 @@ trait Logic extends Debugging { // V1 = Nil implies -(V2 = Ci) for all Ci in V2's domain (i.e., it is unassignable) // may throw an AnalysisBudget.Exception def removeVarEq(props: List[Prop], modelNull: Boolean = false): (Prop, List[Prop]) = { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.patmatAnaVarEq) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.patmatAnaVarEq) else null val vars = new mutable.LinkedHashSet[Var] @@ -491,7 +491,7 @@ trait Logic extends Debugging { debug.patmat(s"eqAxioms:\n${eqAxiomsSeq.mkString("\n")}") debug.patmat(s"pure:${pure.mkString("\n")}") - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.patmatAnaVarEq, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.patmatAnaVarEq, start) (And(eqAxiomsSeq: _*), pure) } @@ -511,7 +511,7 @@ trait Logic extends Debugging { final case class Solution(model: Model, unassigned: List[Sym]) - def findModelFor(solvable: Solvable): Model + def hasModel(solvable: Solvable): Boolean def findAllModelsFor(solvable: Solvable, sym: Symbol = NoSymbol): List[Solution] } @@ -562,7 +562,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { val subConsts = enumerateSubtypes(staticTp, grouped = false) .headOption.map { tps => - tps.toSet[Type].map{ tp => + tps.to(scala.collection.immutable.ListSet).map { tp => val domainC = TypeConst(tp) registerEquality(domainC) domainC @@ -583,7 +583,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { val subtypes = enumerateSubtypes(staticTp, grouped = true) subtypes.map { subTypes => - val syms = subTypes.flatMap(tpe => symForEqualsTo.get(TypeConst(tpe))).toSet + val syms = subTypes.flatMap(tpe => symForEqualsTo.get(TypeConst(tpe))).to(scala.collection.immutable.ListSet) if (mayBeNull) syms + symForEqualsTo(NullConst) else syms }.filter(_.nonEmpty) } @@ -719,13 +719,14 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { lazy val symForStaticTp: Option[Sym] = symForEqualsTo.get(TypeConst(staticTpCheckable)) // don't access until all potential equalities have been registered using registerEquality - private lazy val equalitySyms = {observed(); symForEqualsTo.values.toList} + private lazy val equalitySyms = {observed(); symForEqualsTo.values.toList.sortBy(_.toString) } // don't call until all equalities have been registered and registerNull has been called (if needed) def describe = { + val consts = symForEqualsTo.keys.toSeq.sortBy(_.toString) def domain_s = domain match { - case Some(d) => d.mkString(" ::= ", " | ", "// "+ symForEqualsTo.keys) - case _ => symForEqualsTo.keys.mkString(" ::= ", " | ", " | ...") + case Some(d) => d.mkString(" ::= ", " | ", "// " + consts) + case _ => consts.mkString(" ::= ", " | ", " | ...") } s"$this: ${staticTp}${domain_s} // = $path" } @@ -858,14 +859,14 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { else ConstantType(c) case Ident(_) if p.symbol.isStable => // for Idents, can encode uniqueness of symbol as uniqueness of the corresponding singleton type - // for Selects, which are handled by the next case, the prefix of the select varies independently of the symbol (see pos/virtpatmat_unreach_select.scala) + // for Selects, which are handled by the next case, the prefix of the select varies independently of the symbol (see neg/virtpatmat_unreach_select.scala) singleType(tp.prefix, p.symbol) case _ => Const.uniqueTpForTree(p) } val toString = - if (hasStableSymbol(p)) p.symbol.name.toString // tp.toString + if (p.hasSymbolField && p.symbol.isStable) p.symbol.name.toString // tp.toString else p.toString //+"#"+ id Const.unique(narrowTp, new ValueConst(narrowTp, checkableType(wideTp), toString)) // must make wide type checkable so that it is comparable to types from TypeConst diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 2ea32b41e270..99aafbee6a03 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -14,7 +14,6 @@ package scala.tools.nsc.transform.patmat import scala.annotation.tailrec import scala.collection.mutable -import scala.reflect.internal.util.StatisticsStatics import scala.tools.nsc.Reporting.WarningCategory trait TreeAndTypeAnalysis extends Debugging { @@ -459,7 +458,7 @@ trait MatchAnalysis extends MatchApproximation { // or, equivalently, P \/ -C, or C => P def unreachableCase(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Int] = { debug.patmat("reachability analysis") - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.patmatAnaReach) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.patmatAnaReach) else null // use the same approximator so we share variables, // but need different conditions depending on whether we're conservatively looking for failure or success @@ -498,17 +497,12 @@ trait MatchAnalysis extends MatchApproximation { else { prefix += prefHead current = current.tail - val and = And((current.head +: prefix).toIndexedSeq: _*) - val model = findModelFor(eqFreePropToSolvable(and)) - - // debug.patmat("trying to reach:\n"+ cnfString(current.head) +"\nunder prefix:\n"+ cnfString(prefix)) - // if (NoModel ne model) debug.patmat("reached: "+ modelString(model)) - - reachable = NoModel ne model + val and = And((current.head +: prefix).toIndexedSeq: _*) + reachable = hasModel(eqFreePropToSolvable(and)) } } - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.patmatAnaReach, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.patmatAnaReach, start) if (reachable) None else Some(caseIndex) } catch { @@ -526,7 +520,7 @@ trait MatchAnalysis extends MatchApproximation { // - approximate the pattern `List()` (unapplySeq on List with empty length) as `Nil`, // otherwise the common (xs: List[Any]) match { case List() => case x :: xs => } is deemed unexhaustive // - back off (to avoid crying exhaustive too often) in unhandled cases - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.patmatAnaExhaust) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.patmatAnaExhaust) else null var backoff = false val strict = !settings.nonStrictPatmatAnalysis.value @@ -573,13 +567,9 @@ trait MatchAnalysis extends MatchApproximation { val matchFailModels = findAllModelsFor(propToSolvable(matchFails), prevBinder) val scrutVar = Var(prevBinderTree) - val counterExamples = { - matchFailModels.flatMap { - model => - val varAssignments = expandModel(model) - varAssignments.flatMap(modelToCounterExample(scrutVar) _) - } - } + val counterExamples = matchFailModels.iterator.flatMap { model => + expandModel(model).flatMap(modelToCounterExample(scrutVar)) + }.take(AnalysisBudget.maxDPLLdepth).toList // sorting before pruning is important here in order to // keep neg/t7020.scala stable @@ -587,7 +577,7 @@ trait MatchAnalysis extends MatchApproximation { // and make sure the strings are distinct, see Shmeez & TestSequence06 in run/patmatnew.scala val pruned = CounterExample.prune(counterExamples.sortBy(_.toString)).map(_.toString).distinct - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.patmatAnaExhaust, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.patmatAnaExhaust, start) pruned } catch { case ex: AnalysisBudget.Exception => @@ -658,16 +648,18 @@ trait MatchAnalysis extends MatchApproximation { case object WildcardExample extends CounterExample { override def toString = "_" } case object NoExample extends CounterExample { override def toString = "??" } + type VarAssignment = Map[Var, (Seq[Const], Seq[Const])] + // returns a mapping from variable to // equal and notEqual symbols - def modelToVarAssignment(model: Model): Map[Var, (Seq[Const], Seq[Const])] = + def modelToVarAssignment(model: Model): VarAssignment = model.toSeq.groupBy(_._1.variable).view.mapValues{ xs => val (trues, falses) = xs.partition(_._2) (trues map (_._1.const), falses map (_._1.const)) // should never be more than one value in trues... }.to(Map) - def varAssignmentString(varAssignment: Map[Var, (Seq[Const], Seq[Const])]) = + def varAssignmentString(varAssignment: VarAssignment) = varAssignment.toSeq.sortBy(_._1.toString).map { case (v, (trues, falses)) => s"$v(=${v.path}: ${v.staticTpCheckable}) == ${trues.mkString("(", ", ", ")")} != (${falses.mkString(", ")})" }.mkString("\n") @@ -702,7 +694,7 @@ trait MatchAnalysis extends MatchApproximation { * Only one of these symbols can be set to true, * since `V2` can at most be equal to one of {2,6,5,4,7}. */ - def expandModel(solution: Solution): List[Map[Var, (Seq[Const], Seq[Const])]] = { + def expandModel(solution: Solution): List[VarAssignment] = { val model = solution.model @@ -719,7 +711,7 @@ trait MatchAnalysis extends MatchApproximation { val groupedByVar: Map[Var, List[Sym]] = solution.unassigned.groupBy(_.variable) val expanded = for { - (variable, syms) <- groupedByVar.toList + (variable, syms) <- groupedByVar.toList.sortBy(_._1.toString) } yield { val (equal, notEqual) = varAssignment.getOrElse(variable, Nil -> Nil) @@ -735,7 +727,7 @@ trait MatchAnalysis extends MatchApproximation { // a list counter example could contain wildcards: e.g. `List(_,_)` val allEqual = addVarAssignment(syms.map(_.const), Nil) - if(equal.isEmpty) { + if (equal.isEmpty) { val oneHot = for { s <- syms } yield { @@ -747,34 +739,32 @@ trait MatchAnalysis extends MatchApproximation { } } - if (expanded.isEmpty) { - List(varAssignment) - } else { - // we need the Cartesian product here, - // since we want to report all missing cases - // (i.e., combinations) - val cartesianProd = expanded.reduceLeft((xs, ys) => - for {map1 <- xs - map2 <- ys} yield { - map1 ++ map2 - }) - - // add expanded variables - // note that we can just use `++` - // since the Maps have disjoint keySets - for { - m <- cartesianProd - } yield { - varAssignment ++ m + // we need the Cartesian product here, + // since we want to report all missing cases + // (i.e., combinations) + @tailrec def loop(acc: List[VarAssignment], in: List[List[VarAssignment]]): List[VarAssignment] = { + if (acc.sizeIs > AnalysisBudget.maxDPLLdepth) acc.take(AnalysisBudget.maxDPLLdepth) + else in match { + case vs :: vss => loop(for (map1 <- acc; map2 <- vs) yield map1 ++ map2, vss) + case _ => acc } } + expanded match { + case head :: tail => + val cartesianProd = loop(head, tail) + // add expanded variables + // note that we can just use `++` + // since the Maps have disjoint keySets + for (m <- cartesianProd) yield varAssignment ++ m + case _ => List(varAssignment) + } } // return constructor call when the model is a true counter example // (the variables don't take into account type information derived from other variables, // so, naively, you might try to construct a counter example like _ :: Nil(_ :: _, _ :: _), // since we didn't realize the tail of the outer cons was a Nil) - def modelToCounterExample(scrutVar: Var)(varAssignment: Map[Var, (Seq[Const], Seq[Const])]): Option[CounterExample] = { + def modelToCounterExample(scrutVar: Var)(varAssignment: VarAssignment): Option[CounterExample] = { val strict = !settings.nonStrictPatmatAnalysis.value // chop a path into a list of symbols @@ -823,7 +813,7 @@ trait MatchAnalysis extends MatchApproximation { // node in the tree that describes how to construct a counter-example case class VariableAssignment(variable: Var, equalTo: List[Const], notEqualTo: List[Const]) { - private val fields: mutable.Map[Symbol, VariableAssignment] = mutable.HashMap.empty + private val fields: mutable.LinkedHashMap[Symbol, VariableAssignment] = mutable.LinkedHashMap.empty // need to prune since the model now incorporates all super types of a constant (needed for reachability) private lazy val uniqueEqualTo = equalTo filterNot (subsumed => equalTo.exists(better => (better ne subsumed) && instanceOfTpImplies(better.tp, subsumed.tp))) private lazy val inSameDomain = uniqueEqualTo forall (const => variable.domainSyms.exists(_.exists(_.const.tp =:= const.tp))) @@ -919,7 +909,7 @@ trait MatchAnalysis extends MatchApproximation { } // slurp in information from other variables - varAssignment.keys.foreach{ v => if (v != scrutVar) VariableAssignment(v) } + varAssignment.keys.toSeq.sortBy(_.toString).foreach(v => if (v != scrutVar) VariableAssignment(v)) // this is the variable we want a counter example for VariableAssignment(scrutVar).toCounterExample() diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala index 46d4b4784cc8..6d87a6bb1e74 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala @@ -14,11 +14,7 @@ package scala.tools.nsc.transform.patmat import scala.tools.nsc.symtab.Flags.SYNTHETIC -/** Factory methods used by TreeMakers to make the actual trees. - * - * We have two modes in which to emit trees: optimized (the default) - * and pure (aka "virtualized": match is parametric in its monad). - */ +/** Factory methods used by TreeMakers to make the actual trees. */ trait MatchCodeGen extends Interface { import global._ @@ -27,7 +23,7 @@ trait MatchCodeGen extends Interface { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// trait CodegenCore extends MatchMonadInterface { private var ctr = 0 - def freshName(prefix: String) = {ctr += 1; vpmName.counted(prefix, ctr)} + def freshName(prefix: String) = { ctr += 1; newTermName(s"$prefix$ctr") } // assert(owner ne null); assert(owner ne NoSymbol) def freshSym(pos: Position, tp: Type = NoType, prefix: String = "x") = @@ -164,8 +160,8 @@ trait MatchCodeGen extends Interface { ValDef(prevSym, prev), // must be isEmpty and get as we don't control the target of the call (prev is an extractor call) ifThenElseZero( - NOT(prevSym DOT vpmName.isEmpty), - Substitution(b, prevSym DOT vpmName.get)(next) + NOT(prevSym DOT nme.isEmpty), + Substitution(b, prevSym DOT nme.get)(next) ) ) } @@ -193,7 +189,7 @@ trait MatchCodeGen extends Interface { def flatMapCondStored(cond: Tree, condSym: Symbol, res: Tree, nextBinder: Symbol, next: Tree): Tree = ifThenElseZero(cond, BLOCK( - condSym === mkTRUE, + condSym === TRUE, nextBinder === res, next )) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index f94b457ce70f..1274ca468669 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -22,8 +22,7 @@ import scala.tools.nsc.Reporting.WarningCategory * The patmat translation doesn't rely on this, so it could be disabled in principle. * - well, not quite: the backend crashes if we emit duplicates in switches (e.g. scala/bug#7290) */ -// TODO: split out match analysis -trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { +trait MatchOptimization extends MatchTreeMaking with MatchApproximation { import global._ import global.definitions._ @@ -43,8 +42,8 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { val testss = approximateMatchConservative(prevBinder, cases) // interpret: - val dependencies = new mutable.LinkedHashMap[Test, Set[Prop]] - val tested = new mutable.HashSet[Prop] + val dependencies = new mutable.LinkedHashMap[Test, mutable.LinkedHashSet[Prop]] + val tested = new mutable.LinkedHashSet[Prop] val reusesMap = new mutable.LinkedHashMap[Int, Test] val reusesTest = { (test: Test) => reusesMap.get(test.id) } val registerReuseBy = { (priorTest: Test, later: Test) => @@ -57,32 +56,32 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { val cond = test.prop def simplify(c: Prop): Set[Prop] = c match { - case And(ops) => ops.toSet flatMap simplify + case And(ops) => ops flatMap simplify case Or(ops) => Set(False) // TODO: make more precise - case Not(Eq(Var(_), NullConst)) => Set(True) // not worth remembering + case Not(Eq(Var(_), NullConst)) => Set.empty // not worth remembering + case True => Set.empty // same case _ => Set(c) } val conds = simplify(cond) if (conds(False)) false // stop when we encounter a definite "no" or a "not sure" else { - val nonTrivial = conds - True - if (!nonTrivial.isEmpty) { - tested ++= nonTrivial + if (!conds.isEmpty) { + tested ++= conds // is there an earlier test that checks our condition and whose dependencies are implied by ours? dependencies find { case (priorTest, deps) => - ((simplify(priorTest.prop) == nonTrivial) || // our conditions are implied by priorTest if it checks the same thing directly - (nonTrivial subsetOf deps) // or if it depends on a superset of our conditions - ) && (deps subsetOf tested) // the conditions we've tested when we are here in the match satisfy the prior test, and hence what it tested + ((simplify(priorTest.prop) == conds) || // our conditions are implied by priorTest if it checks the same thing directly + (conds subsetOf deps) // or if it depends on a superset of our conditions + ) && (deps subsetOf tested) // the conditions we've tested when we are here in the match satisfy the prior test, and hence what it tested } foreach { case (priorTest, _) => // if so, note the dependency in both tests registerReuseBy(priorTest, test) } - dependencies(test) = tested.toSet // copies + dependencies(test) = tested.clone() } true } @@ -108,7 +107,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { val collapsed = testss map { tests => // map tests to the equivalent list of treemakers, replacing shared prefixes by a reusing treemaker // if there's no sharing, simply map to the tree makers corresponding to the tests - var currDeps = Set[Prop]() + var currDeps = mutable.LinkedHashSet.empty[Prop] val (sharedPrefix, suffix) = tests span { test => (test.prop == True) || (for( reusedTest <- reusesTest(test); @@ -160,7 +159,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { } // TODO: finer-grained duplication - def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(codegen eq optimizedCodegen) + def chainBefore(next: Tree)(casegen: Casegen): Tree = atPos(pos)(casegen.flatMapCondStored(cond, storedCond, res, nextBinder, substitution(next).duplicate)) override def toString = "Memo"+((nextBinder.name, storedCond.name, cond, res, substitution)) @@ -206,20 +205,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { } } - - //// DCE -// trait DeadCodeElimination extends TreeMakers { -// // TODO: non-trivial dead-code elimination -// // e.g., the following match should compile to a simple instanceof: -// // case class Ident(name: String) -// // for (Ident(name) <- ts) println(name) -// def doDCE(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type): List[List[TreeMaker]] = { -// // do minimal DCE -// cases -// } -// } - - //// SWITCHES -- TODO: operate on Tests rather than TreeMakers + //// SWITCHES trait SwitchEmission extends TreeMakers with MatchMonadInterface { import treeInfo.isGuardedCase @@ -527,7 +513,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { case _ => None }} - def scrutRef(scrut: Symbol): Tree = dealiasWiden(scrut.tpe) match { + def scrutRef(scrut: Symbol): Tree = scrut.tpe.dealiasWiden match { case subInt if subInt =:= IntTpe => REF(scrut) case subInt if definitions.isNumericSubClass(subInt.typeSymbol, IntClass) => @@ -557,7 +543,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, matchFailGenOverride: Option[Tree => Tree], unchecked: Boolean): Option[Tree] = { import CODE._ val regularSwitchMaker = new RegularSwitchMaker(scrutSym, matchFailGenOverride, unchecked) // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result - if (regularSwitchMaker.switchableTpe(dealiasWiden(scrutSym.tpe))) { + if (regularSwitchMaker.switchableTpe(scrutSym.tpe.dealiasWiden)) { val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt) if (caseDefsWithDefault.isEmpty) None // not worth emitting a switch. else { @@ -615,13 +601,8 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { with SwitchEmission with CommonSubconditionElimination { override def optimizeCases(prevBinder: Symbol, cases: List[List[TreeMaker]], pt: Type, selectorPos: Position): (List[List[TreeMaker]], List[Tree]) = { - // TODO: do CSE on result of doDCE(prevBinder, cases, pt) val optCases = doCSE(prevBinder, cases, pt, selectorPos) - val toHoist = ( - for (treeMakers <- optCases) - yield treeMakers.collect{case tm: ReusedCondTreeMaker => tm.treesToHoist} - ).flatten.flatten.toList - (optCases, toHoist) + (optCases, optCases.flatMap(flatCollect(_) { case tm: ReusedCondTreeMaker => tm.treesToHoist })) } } } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala index 64cde9496d45..c02bf8d339d6 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala @@ -12,8 +12,6 @@ package scala.tools.nsc.transform.patmat -import scala.reflect.internal.util.StatisticsStatics - /** Translate typed Trees that represent pattern matches into the patternmatching IR, defined by TreeMakers. */ trait MatchTranslation { @@ -125,16 +123,9 @@ trait MatchTranslation { // TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC] // (the prefix of the argument passed to the unapply must equal the prefix of the type of the binder) val typeTest = TypeTestTreeMaker(binder, binder, paramType, paramType)(pos, extractorArgTypeTest = true) - val binderKnownNonNull = typeTest impliesBinderNonNull binder - // skip null test if it's implied - if (binderKnownNonNull) { - val unappBinder = typeTest.nextBinder - (typeTest :: treeMakers(unappBinder, pos), unappBinder) - } else { - val nonNullTest = NonNullTestTreeMaker(typeTest.nextBinder, paramType, pos) - val unappBinder = nonNullTest.nextBinder - (typeTest :: nonNullTest :: treeMakers(unappBinder, pos), unappBinder) - } + // binder is known non-null because the type test would not succeed on `null` + val unappBinder = typeTest.nextBinder + (typeTest :: treeMakers(unappBinder, pos), unappBinder) } } @@ -187,15 +178,9 @@ trait MatchTranslation { override def toString = if (subpatterns.isEmpty) "" else subpatterns.mkString("(", ", ", ")") } - /** Implement a pattern match by turning its cases (including the implicit failure case) - * into the corresponding (monadic) extractors, and combining them with the `orElse` combinator. - * - * For `scrutinee match { case1 ... caseN }`, the resulting tree has the shape - * `runOrElse(scrutinee)(x => translateCase1(x).orElse(translateCase2(x)).....orElse(zero))` - * - * NOTE: the resulting tree is not type checked, nor are nested pattern matches transformed + /** NOTE: the resulting tree is not type checked, nor are nested pattern matches transformed * thus, you must typecheck the result (and that will in turn translate nested matches) - * this could probably optimized... (but note that the matchStrategy must be solved for each nested patternmatch) + * this could probably be optimized... */ def translateMatch(match_ : Match): Tree = { val Match(selector, cases) = match_ @@ -215,7 +200,7 @@ trait MatchTranslation { debug.patmat("translating "+ cases.mkString("{", "\n", "}")) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.patmatNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.patmatNanos) else null val selectorTp = repeatedToSeq(elimAnonymousClass(selector.tpe.withoutAnnotations)) @@ -226,12 +211,12 @@ trait MatchTranslation { val pt = repeatedToSeq(origPt) // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner)) - val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS + val selectorSym = freshSym(selector.pos, selectorTp) setFlag treeInfo.SYNTH_CASE_FLAGS // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala - val combined = combineCases(selector, selectorSym, nonSyntheticCases map translateCase(selectorSym, pt), pt, selectorPos, matchOwner, defaultOverride) + val combined = combineCases(selector, selectorSym, nonSyntheticCases map translateCase(selectorSym, pt), pt, selectorPos, matchOwner, defaultOverride, getSuppression(selector)) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.patmatNanos, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.patmatNanos, start) combined } @@ -255,8 +240,8 @@ trait MatchTranslation { val bindersAndCases = caseDefs.map(_.duplicate) map { caseDef => // generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there) // if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this) - val caseScrutSym = freshSym(caseDef.pat.pos, pureType(ThrowableTpe)) - (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, pt)(caseDef), EmptySubstitution)) + val caseScrutSym = freshSym(caseDef.pat.pos, ThrowableTpe) + (caseScrutSym, translateCase(caseScrutSym, pt)(caseDef)) } for(cases <- emitTypeSwitch(bindersAndCases, pt).toList @@ -265,10 +250,10 @@ trait MatchTranslation { } val catches = if (swatches.nonEmpty) swatches else { - val scrutSym = freshSym(caseDefs.head.pat.pos, pureType(ThrowableTpe)) - val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, pt)(caseDef), EmptySubstitution))} + val scrutSym = freshSym(caseDefs.head.pat.pos, ThrowableTpe) + val cases = caseDefs.map(translateCase(scrutSym, pt)) - val exSym = freshSym(pos, pureType(ThrowableTpe), "ex") + val exSym = freshSym(pos, ThrowableTpe, "ex") val suppression = if (settings.XnoPatmatAnalysis) Suppression.FullSuppression else Suppression.NoSuppression.copy(suppressExhaustive = true) // try/catches needn't be exhaustive @@ -278,7 +263,7 @@ trait MatchTranslation { CaseDef( Bind(exSym, Ident(nme.WILDCARD)), // TODO: does this need fixing upping? EmptyTree, - combineCasesNoSubstOnly(REF(exSym), scrutSym, casesNoSubstOnly, pt, selectorPos, matchOwner, Some(scrut => Throw(REF(exSym))), suppression) + combineCases(REF(exSym), scrutSym, cases, pt, selectorPos, matchOwner, Some(scrut => Throw(REF(exSym))), suppression) ) }) } @@ -316,7 +301,8 @@ trait MatchTranslation { */ def translateCase(scrutSym: Symbol, pt: Type)(caseDef: CaseDef): List[TreeMaker] = { val CaseDef(pattern, guard, body) = caseDef - translatePattern(BoundTree(scrutSym, pattern)) ++ translateGuard(guard) :+ translateBody(body, pt) + val treeMakers = translatePattern(BoundTree(scrutSym, pattern)) ++ translateGuard(guard) :+ translateBody(body, pt) + propagateSubstitution(treeMakers, EmptySubstitution) } def translatePattern(bound: BoundTree): List[TreeMaker] = bound.translate() @@ -325,11 +311,7 @@ trait MatchTranslation { if (guard == EmptyTree) Nil else List(GuardTreeMaker(guard)) - // TODO: 1) if we want to support a generalisation of Kotlin's patmat continue, must not hard-wire lifting into the monad (which is now done by codegen.one), - // so that user can generate failure when needed -- use implicit conversion to lift into monad on-demand? - // to enable this, probably need to move away from Option to a monad specific to pattern-match, - // so that we can return Option's from a match without ambiguity whether this indicates failure in the monad, or just some result in the monad - // 2) body.tpe is the type of the body after applying the substitution that represents the solution of GADT type inference + // TODO: body.tpe is the type of the body after applying the substitution that represents the solution of GADT type inference // need the explicit cast in case our substitutions in the body change the type to something that doesn't take GADT typing into account def translateBody(body: Tree, matchPt: Type): TreeMaker = BodyTreeMaker(body, matchPt) @@ -554,7 +536,7 @@ trait MatchTranslation { // can't simplify this when subPatBinders.isEmpty, since UnitTpe is definitely // wrong when isSeq, and resultInMonad should always be correct since it comes // directly from the extractor's result type - val binder = freshSym(pos, pureType(resultInMonad(patBinderOrCasted))) + val binder = freshSym(pos, resultInMonad(patBinderOrCasted)) val potentiallyMutableBinders: Set[Symbol] = if (extractorApply.tpe.typeSymbol.isNonBottomSubClass(OptionClass) && !isSeq) Set.empty diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index aa6412d55883..0c7646fb03b4 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -19,12 +19,10 @@ import scala.tools.nsc.Reporting.WarningCategory /** Translate our IR (TreeMakers) into actual Scala Trees using the factory methods in MatchCodeGen. * - * The IR is mostly concerned with sequencing, substitution, and rendering all necessary conditions, - * mostly agnostic to whether we're in optimized/pure (virtualized) mode. + * The IR is mostly concerned with sequencing, substitution, and rendering all necessary conditions. */ trait MatchTreeMaking extends MatchCodeGen with Debugging { - import global._ - import definitions._ + import global._, definitions._, CODE._ final case class Suppression(suppressExhaustive: Boolean, suppressUnreachable: Boolean) object Suppression { @@ -145,7 +143,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { // mutable case class fields need to be stored regardless (scala/bug#5158, scala/bug#6070) -- see override in ProductExtractorTreeMaker // sub patterns bound to wildcard (_) are never stored as they can't be referenced // dirty debuggers will have to get dirty to see the wildcards - lazy val storedBinders: Set[Symbol] = + private lazy val storedBinders: Set[Symbol] = (if (debugInfoEmitVars) subPatBinders.toSet else Set.empty) ++ extraStoredBinders diff ignoredSubPatBinders // e.g., mutable fields of a case class in ProductExtractorTreeMaker @@ -210,18 +208,17 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { prevBinder: Symbol, expectedTp: Type, override val pos: Position) extends FunTreeMaker { - import CODE._ override lazy val nextBinder = prevBinder.asTerm // just passing through val nextBinderTp = nextBinder.info.widen val nullCheck = REF(prevBinder) OBJ_NE NULL lazy val localSubstitution = Substitution(Nil, Nil) - def isExpectedPrimitiveType = isPrimitiveValueType(expectedTp) + def skipNullTest = isPrimitiveValueType(expectedTp) || expectedTp.typeSymbol.isDerivedValueClass def chainBefore(next: Tree)(casegen: Casegen): Tree = atPos(pos) { - if (isExpectedPrimitiveType) next + if (skipNullTest) next else casegen.ifThenElseZero(nullCheck, next) } @@ -269,7 +266,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { bindSubPats(substitution(next)) } atPos(extractor.pos)( - if (extractorReturnsBoolean) casegen.flatMapCond(extractor, CODE.UNIT, nextBinder, condAndNext) + if (extractorReturnsBoolean) casegen.flatMapCond(extractor, UNIT, nextBinder, condAndNext) else casegen.flatMap(extractor, nextBinder, condAndNext) ) } @@ -339,20 +336,17 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def tru: Result } - object treeCondStrategy extends TypeTestCondStrategy { import CODE._ + object treeCondStrategy extends TypeTestCondStrategy { type Result = Tree def and(a: Result, b: Result): Result = a AND b - def tru = mkTRUE + def tru = TRUE def typeTest(testedBinder: Symbol, expectedTp: Type) = codegen._isInstanceOf(testedBinder, expectedTp) def nonNullTest(testedBinder: Symbol) = REF(testedBinder) OBJ_NE NULL def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) def eqTest(pat: Tree, testedBinder: Symbol) = REF(testedBinder) OBJ_EQ pat override def withOuterTest(orig: Tree)(testedBinder: Symbol, expectedTp: Type): Tree = { - val expectedPrefix = expectedTp.prefix - val testedPrefix = testedBinder.info.prefix - // Check if a type is defined in a static location. Unlike `tp.isStatic` before `flatten`, // this also includes methods and (possibly nested) objects inside of methods. def definedInStaticLocation(tp: Type): Boolean = { @@ -364,20 +358,87 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { tp.typeSymbol.owner == tp.prefix.typeSymbol && isStatic(tp.prefix) } - if ((expectedPrefix eq NoPrefix) - || expectedTp.typeSymbol.isJava - || definedInStaticLocation(expectedTp) - || testedPrefix =:= expectedPrefix) orig - else gen.mkAttributedQualifierIfPossible(expectedPrefix) match { - case None => orig - case Some(expectedOuterRef) => - // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` - // by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` - // if there's an outer accessor, otherwise the condition becomes `true` - // TODO: centralize logic whether there's an outer accessor and use here? - val synthOuterGetter = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedPrefix - val outerTest = (Select(codegen._asInstanceOf(testedBinder, expectedTp), synthOuterGetter)) OBJ_EQ expectedOuterRef - and(orig, outerTest) + // In `def foo(a: b.B) = a match { case _: p.P }` + // testedBinder.symbol.info = b.B + // expectedTp = p.P + + expectedTp.dealias match { + case RefinedType(Nil, _) => orig + case rt@RefinedType(parent :: rest, scope) => + // If the pattern type is refined type, emit outer tests for each component. + withOuterTest(withOuterTest(orig)(testedBinder, parent))(testedBinder, copyRefinedType(rt, rest, scope)) + case expectedTp => + val expectedClass = expectedTp.typeSymbol + // .typeSymbol dealiases, so look at the prefix of the base type at the dealiased symbol, + // not of expectedTp itself. + val expectedPrefix = expectedTp.baseType(expectedClass).prefix + + + // Given `(a: x.B) match { case _: x.P }` where P is subclass of B, is it possible + // that a value conforms to both x.B and x1.P where `x ne x1`? + // + // To answer this, we create a new prefix based on a fresh symbol and check the + // base type of TypeRef(freshPrefix, typePatternSymbol (P), args) at the binder + // symbol (B). If that is prefixed by the fresh symbol, they are statically the + // same. + // + // It is not sufficient to show that x.P is a subtype of x.B, as this + // would incorrectly elide the outer test in: + // + // class P extends p1.B + // def test(b: p1.B) = b match { case _: p1.P } + // test(new p2.P) + def prefixAligns: Boolean = { + expectedTp match { + case TypeRef(pre, _, _) if !pre.isStable => // e.g. _: Outer#Inner + false + case TypeRef(pre, sym, args) => + val testedBinderClass = testedBinder.info.upperBound.typeSymbol + // alternatively..... = testedBinder.info.baseClasses.find(_.isClass).getOrElse(NoSymbol) + val testedBinderType = testedBinder.info.baseType(testedBinderClass) + + val testedPrefixIsExpectedTypePrefix = pre =:= testedBinderType.prefix + val testedPrefixAndExpectedPrefixAreStaticallyIdentical: Boolean = { + def check(freshPrefix: Type): Boolean = { + val expectedTpFromFreshPrefix = TypeRef(freshPrefix, sym, args) + val baseTypeFromFreshPrefix = expectedTpFromFreshPrefix.baseType(testedBinderClass) + freshPrefix eq baseTypeFromFreshPrefix.prefix + } + pre match { + case ThisType(thissym) => + check(ThisType(thissym.cloneSymbol(thissym.owner))) + case _ => + pre.termSymbol match { + case NoSymbol => false + case preSym => + val freshPreSym = preSym.cloneSymbol(preSym.owner).setInfo(preSym.info) + check(singleType(pre.prefix, freshPreSym)) + } + } + + } + testedPrefixAndExpectedPrefixAreStaticallyIdentical && testedPrefixIsExpectedTypePrefix + case _ => + false + } + } + + if ((expectedPrefix eq NoPrefix) + || expectedTp.typeSymbol.isJava + || definedInStaticLocation(expectedTp) + || testedBinder.info <:< expectedTp + || prefixAligns) orig + else gen.mkAttributedQualifierIfPossible(expectedPrefix) match { + case None => orig + case Some(expectedOuterRef) => + // ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix` + // by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix` + // if there's an outer accessor, otherwise the condition becomes `true` + // TODO: centralize logic whether there's an outer accessor and use here? + val synthOuterGetter = expectedTp.typeSymbol.newMethod(nme.OUTER_SYNTH, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedPrefix + val outerTest = (Select(codegen._asInstanceOf(testedBinder, expectedTp), synthOuterGetter)) OBJ_EQ expectedOuterRef + and(orig, outerTest) + } } } } @@ -393,17 +454,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { def and(a: Result, b: Result): Result = false // we don't and type tests, so the conjunction must include at least one false def tru = true } - - def nonNullImpliedByTestChecker(binder: Symbol) = new TypeTestCondStrategy { - type Result = Boolean - - def typeTest(testedBinder: Symbol, expectedTp: Type): Result = testedBinder eq binder - def nonNullTest(testedBinder: Symbol): Result = testedBinder eq binder - def equalsTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null - def eqTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null - def and(a: Result, b: Result): Result = a || b - def tru = false - } } /** implements the run-time aspects of (§8.2) (typedPattern has already done the necessary type transformations) @@ -486,8 +536,8 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { else mkEqualsTest(expected) // Should revisit if we end up lifting `eq`'s definition to `Any`, as discussed here: // https://groups.google.com/d/msg/scala-internals/jsVlJI4H5OQ/8emZWRmgzcoJ - case ThisType(sym) if sym.isModule => and(mkEqualsTest(CODE.REF(sym)), mkTypeTest) // must use == to support e.g. List() == Nil - case ConstantType(Constant(null)) if isAnyRef => mkEqTest(expTp(CODE.NULL)) + case ThisType(sym) if sym.isModule => and(mkEqualsTest(REF(sym)), mkTypeTest) // must use == to support e.g. List() == Nil + case ConstantType(Constant(null)) if isAnyRef => mkEqTest(expTp(NULL)) case ConstantType(const) => mkEqualsTest(expTp(Literal(const))) case ThisType(sym) => mkEqTest(expTp(This(sym))) case _ => mkDefault @@ -500,8 +550,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { // is this purely a type test, e.g. no outer check, no equality tests (used in switch emission) def isPureTypeTest = renderCondition(pureTypeTestChecker) - def impliesBinderNonNull(binder: Symbol) = renderCondition(nonNullImpliedByTestChecker(binder)) - override def toString = "TT"+((expectedTp, testedBinder.name, nextBinderTp)) } @@ -529,10 +577,10 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { // one alternative may still generate multiple trees (e.g., an extractor call + equality test) // (for now,) alternatives may not bind variables (except wildcards), so we don't care about the final substitution built internally by makeTreeMakers val combinedAlts = altss map (altTreeMakers => - ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(mkTRUE)))(casegen)) + ((casegen: Casegen) => combineExtractors(altTreeMakers :+ TrivialTreeMaker(casegen.one(TRUE)))(casegen)) ) - val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanTpe)(combinedAlts, Some(x => mkFALSE)) + val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, BooleanTpe)(combinedAlts, Some(x => FALSE)) codegenAlt.ifThenElseZero(findAltMatcher, substitution(next)) } } @@ -576,69 +624,61 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { case _ => Suppression.NoSuppression } - // calls propagateSubstitution on the treemakers - def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, selectorPos: Position, owner: Symbol, matchFailGenOverride: Option[Tree => Tree]): Tree = { - // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them - val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) - combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, selectorPos, owner, matchFailGenOverride, getSuppression(scrut)) + def requiresSwitch(scrut: Tree, cases: List[List[TreeMaker]]): Boolean = { + if (settings.XnoPatmatAnalysis) false + else scrut match { + case Typed(tree, tpt) => + val hasSwitchAnnotation = treeInfo.isSwitchAnnotation(tpt.tpe) + // matches with two or fewer cases need not apply for switchiness (if-then-else will do) + // `case 1 | 2` is considered as two cases. + def exceedsTwoCasesOrAlts = { + // avoids traversing the entire list if there are more than 3 elements + def lengthMax3(l: List[List[TreeMaker]]): Int = l match { + case a :: b :: c :: _ => 3 + case cases => cases.map { + case AlternativesTreeMaker(_, alts, _) :: _ => lengthMax3(alts) + case c => 1 + }.sum + } + lengthMax3(cases) > 2 + } + hasSwitchAnnotation && exceedsTwoCasesOrAlts + case _ => false + } } // pt is the fully defined type of the cases (either pt or the lub of the types of the cases) - def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, - selectorPos: Position, owner: Symbol, matchFailGenOverride: Option[Tree => Tree], - suppression: Suppression, + def combineCases( + scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type, + selectorPos: Position, owner: Symbol, matchFailGenOverride: Option[Tree => Tree], + suppression: Suppression, ): Tree = fixerUpper(owner, scrut.pos) { def matchFailGen = matchFailGenOverride orElse Some(Throw(MatchErrorClass.tpe, _: Tree)) - debug.patmat("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}"))) - - val requireSwitch: Boolean = - if (settings.XnoPatmatAnalysis) false - else scrut match { - case Typed(tree, tpt) => - val hasSwitchAnnotation = treeInfo.isSwitchAnnotation(tpt.tpe) - // matches with two or fewer cases need not apply for switchiness (if-then-else will do) - // `case 1 | 2` is considered as two cases. - def exceedsTwoCasesOrAlts = { - // avoids traversing the entire list if there are more than 3 elements - def lengthMax3(l: List[List[TreeMaker]]): Int = l match { - case a :: b :: c :: _ => 3 - case cases => - cases.map { - case AlternativesTreeMaker(_, alts, _) :: _ => lengthMax3(alts) - case c => 1 - }.sum - } - lengthMax3(casesNoSubstOnly) > 2 - } - hasSwitchAnnotation && exceedsTwoCasesOrAlts - case _ => - false - } + debug.patmat("combining cases: "+ (cases.map(_.mkString(" >> ")).mkString("{", "\n", "}"))) - emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, unchecked = suppression.suppressExhaustive).getOrElse { - if (requireSwitch) typer.context.warning(scrut.pos, "could not emit switch for @switch annotated match", WarningCategory.OtherMatchAnalysis) + emitSwitch(scrut, scrutSym, cases, pt, matchFailGenOverride, unchecked = suppression.suppressExhaustive).getOrElse { + if (requiresSwitch(scrut, cases)) + typer.context.warning(scrut.pos, "could not emit switch for @switch annotated match", WarningCategory.OtherMatchAnalysis) - if (!casesNoSubstOnly.isEmpty) { - // before optimizing, check casesNoSubstOnly for presence of a default case, + if (!cases.isEmpty) { + // before optimizing, check cases for presence of a default case, // since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one // exhaustivity and reachability must be checked before optimization as well // TODO: improve notion of trivial/irrefutable -- a trivial type test before the body still makes for a default case // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op) // irrefutability checking should use the approximation framework also used for CSE, unreachability and exhaustivity checking - val synthCatchAll = - if (casesNoSubstOnly.nonEmpty && { - val nonTrivLast = casesNoSubstOnly.last - nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker] - }) None - else matchFailGen + val synthCatchAll = cases match { + case _ :+ Seq(_: BodyTreeMaker, _*) => None + case _ => matchFailGen + } - analyzeCases(scrutSym, casesNoSubstOnly, pt, suppression) + analyzeCases(scrutSym, cases, pt, suppression) - val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt, selectorPos) + val (optimizedCases, toHoist) = optimizeCases(scrutSym, cases, pt, selectorPos) - val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases map combineExtractors, synthCatchAll) + val matchRes = codegen.matcher(scrut, scrutSym, pt)(optimizedCases map combineExtractors, synthCatchAll) if (toHoist.isEmpty) matchRes else Block(toHoist, matchRes) } else { diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index 8e013493a483..b9d562ff9756 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -90,9 +90,6 @@ trait PatternMatching extends Transform case _ => super.transform(tree) } - // TODO: only instantiate new match translator when localTyper has changed - // override def atOwner[A](tree: Tree, owner: Symbol)(trans: => A): A - // as this is the only time TypingTransformer changes it def translator(selectorPos: Position): MatchTranslator with CodegenCore = { new OptimizingMatchTranslator(localTyper, selectorPos) } @@ -124,70 +121,9 @@ trait Interface extends ast.TreeDSL { import global._ import analyzer.Typer - // 2.10/2.11 compatibility - protected final def dealiasWiden(tp: Type) = tp.dealiasWiden - protected final def mkTRUE = CODE.TRUE - protected final def mkFALSE = CODE.FALSE - protected final def hasStableSymbol(p: Tree) = p.hasSymbolField && p.symbol.isStable - - object vpmName { - val one = newTermName("one") - val flatMap = newTermName("flatMap") - val get = newTermName("get") - val guard = newTermName("guard") - val isEmpty = newTermName("isEmpty") - val orElse = newTermName("orElse") - val outer = newTermName("") - val runOrElse = newTermName("runOrElse") - val zero = newTermName("zero") - val _match = newTermName("__match") // don't call the val __match, since that will trigger virtual pattern matching... - - def counted(str: String, i: Int) = newTermName(str + i) - } - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// talking to userland -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - /** Interface with user-defined match monad? - * if there's a __match in scope, we use this as the match strategy, assuming it conforms to MatchStrategy as defined below: - - {{{ - type Matcher[P[_], M[+_], A] = { - def flatMap[B](f: P[A] => M[B]): M[B] - def orElse[B >: A](alternative: => M[B]): M[B] - } - - abstract class MatchStrategy[P[_], M[+_]] { - // runs the matcher on the given input - def runOrElse[T, U](in: P[T])(matcher: P[T] => M[U]): P[U] - - def zero: M[Nothing] - def one[T](x: P[T]): M[T] - def guard[T](cond: P[Boolean], then: => P[T]): M[T] - } - }}} - - * P and M are derived from one's signature (`def one[T](x: P[T]): M[T]`) - - - * if no __match is found, we assume the following implementation (and generate optimized code accordingly) - - {{{ - object __match extends MatchStrategy[({type Id[x] = x})#Id, Option] { - def zero = None - def one[T](x: T) = Some(x) - // NOTE: guard's return type must be of the shape M[T], where M is the monad in which the pattern match should be interpreted - def guard[T](cond: Boolean, then: => T): Option[T] = if(cond) Some(then) else None - def runOrElse[T, U](x: T)(f: T => Option[U]): U = f(x) getOrElse (throw new MatchError(x)) - } - }}} - - */ trait MatchMonadInterface { val typer: Typer val matchOwner = typer.context.owner - def pureType(tp: Type): Type = tp def reportUnreachable(pos: Position) = typer.context.warning(pos, "unreachable code", WarningCategory.OtherMatchAnalysis) def reportMissingCases(pos: Position, counterExamples: List[String]) = { diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala index 6f52f70bc53b..dd6a524549dc 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala @@ -12,61 +12,37 @@ package scala.tools.nsc.transform.patmat -import java.util - import scala.annotation.tailrec import scala.collection.mutable.ArrayBuffer import scala.collection.{immutable, mutable} -import scala.reflect.internal.util.StatisticsStatics - -// a literal is a (possibly negated) variable -case class Lit(val v: Int) { - private var negated: Lit = null - def unary_- : Lit = { - if (negated eq null) negated = Lit(-v) - negated - } - - def variable: Int = Math.abs(v) - - def positive = v >= 0 - - override def toString(): String = s"Lit#$v" - - override val hashCode: Int = v -} -object Lit { - def apply(v: Int): Lit = new Lit(v) - - implicit val LitOrdering: Ordering[Lit] = Ordering.by(_.v) -} - -/** Solve pattern matcher exhaustivity problem via DPLL. - */ +/** Solve pattern matcher exhaustivity problem via DPLL. */ trait Solving extends Logic { import global._ trait CNF extends PropositionalLogic { + // a literal is a (possibly negated) variable + type Lit <: LitApi + trait LitApi { + def unary_- : Lit + } - type Clause = Set[Lit] + def Lit: LitModule + trait LitModule { + def apply(v: Int): Lit + } - val NoClauses: Array[Clause] = Array() + type Clause = Set[Lit] + + val NoClauses: Array[Clause] = Array() val ArrayOfFalse: Array[Clause] = Array(clause()) + // a clause is a disjunction of distinct literals - def clause(): Clause = Set.empty - def clause(l: Lit): Clause = { - Set.empty + l - } - def clause(l: Lit, l2: Lit): Clause = { - Set.empty + l + l2 - } - def clause(l: Lit, l2: Lit, ls: Lit*): Clause = { - Set.empty + l + l2 ++ ls - } - def clause(ls: IterableOnce[Lit]): Clause = { - Set.from(ls) - } + def clause(): Clause = Set.empty + def clause(l: Lit): Clause = Set.empty + l + def clause(l: Lit, l2: Lit): Clause = Set.empty + l + l2 + def clause(l: Lit, l2: Lit, ls: Lit*): Clause = Set.empty + l + l2 ++ ls + def clause(ls: IterableOnce[Lit]): Clause = Set.from(ls) /** Conjunctive normal form (of a Boolean formula). * A formula in this form is amenable to a SAT solver @@ -83,8 +59,7 @@ trait Solving extends Logic { val symForVar: Map[Int, Sym] = variableForSymbol.map(_.swap) - val relevantVars = - symForVar.keysIterator.map(math.abs).to(immutable.BitSet) + val relevantVars = symForVar.keysIterator.map(math.abs).to(immutable.BitSet) def lit(sym: Sym): Lit = Lit(variableForSymbol(sym)) @@ -390,7 +365,22 @@ trait Solving extends Logic { } // simple solver using DPLL + // adapted from https://lara.epfl.ch/w/sav10:simple_sat_solver (original by Hossein Hojjat) trait Solver extends CNF { + case class Lit(v: Int) extends LitApi { + private lazy val negated: Lit = Lit(-v) + + def unary_- : Lit = negated + def variable: Int = Math.abs(v) + def positive: Boolean = v >= 0 + + override def toString = s"Lit#$v" + override def hashCode = v + } + + object Lit extends LitModule { + def apply(v: Int): Lit = new Lit(v) + } def cnfString(f: Array[Clause]): String = { val lits: Array[List[String]] = f map (_.map(_.toString).toList) @@ -399,8 +389,6 @@ trait Solving extends Logic { aligned } - // adapted from https://lara.epfl.ch/w/sav10:simple_sat_solver (original by Hossein Hojjat) - // empty set of clauses is trivially satisfied val EmptyModel = Map.empty[Sym, Boolean] @@ -411,57 +399,59 @@ trait Solving extends Logic { // this model contains the auxiliary variables as well type TseitinModel = List[Lit] - val EmptyTseitinModel = Nil val NoTseitinModel: TseitinModel = null // returns all solutions, if any (TODO: better infinite recursion backstop -- detect fixpoint??) def findAllModelsFor(solvable: Solvable, owner: Symbol): List[Solution] = { - debug.patmat("find all models for\n"+ cnfString(solvable.cnf)) + import solvable.{ cnf, symbolMapping }, symbolMapping.{ symForVar, relevantVars } + debug.patmat(s"find all models for\n${cnfString(cnf)}") // we must take all vars from non simplified formula // otherwise if we get `T` as formula, we don't expand the variables // that are not in the formula... - val relevantVars: immutable.BitSet = solvable.symbolMapping.relevantVars // debug.patmat("vars "+ vars) // the negation of a model -(S1=True/False /\ ... /\ SN=True/False) = clause(S1=False/True, ...., SN=False/True) // (i.e. the blocking clause - used for ALL-SAT) - def negateModel(m: TseitinModel) = { + def negateModel(m: TseitinModel): TseitinModel = { // filter out auxiliary Tseitin variables - val relevantLits = m.filter(l => relevantVars.contains(l.variable)) - relevantLits.map(lit => -lit) + m.filter(lit => relevantVars.contains(lit.variable)).map(lit => -lit) } - final case class TseitinSolution(model: TseitinModel, unassigned: List[Int]) { - def projectToSolution(symForVar: Map[Int, Sym]) = Solution(projectToModel(model, symForVar), unassigned map symForVar) + def newSolution(model: TseitinModel, unassigned: List[Int]): Solution = { + val newModel: Model = if (model eq NoTseitinModel) NoModel else { + model.iterator.collect { + case lit if symForVar.isDefinedAt(lit.variable) => (symForVar(lit.variable), lit.positive) + }.to(scala.collection.immutable.ListMap) + } + Solution(newModel, unassigned.map(symForVar)) } @tailrec def findAllModels(clauses: Array[Clause], - models: List[TseitinSolution], - recursionDepthAllowed: Int = AnalysisBudget.maxDPLLdepth): List[TseitinSolution]= + models: List[Solution], + recursionDepthAllowed: Int = AnalysisBudget.maxDPLLdepth): List[Solution] = { if (recursionDepthAllowed == 0) { uncheckedWarning(owner.pos, AnalysisBudget.recursionDepthReached, owner) models } else { - debug.patmat("find all models for\n" + cnfString(clauses)) + debug.patmat(s"find all models for\n${cnfString(clauses)}") val model = findTseitinModelFor(clauses) // if we found a solution, conjunct the formula with the model's negation and recurse - if (model ne NoTseitinModel) { + if (model eq NoTseitinModel) models else { // note that we should not expand the auxiliary variables (from Tseitin transformation) // since they are existentially quantified in the final solution - val unassigned: List[Int] = (relevantVars.toList.filterNot(x => model.exists(lit => x == lit.variable))) - debug.patmat("unassigned "+ unassigned +" in "+ model) + val unassigned: List[Int] = relevantVars.filterNot(x => model.exists(lit => x == lit.variable)).toList.sorted + debug.patmat(s"unassigned $unassigned in $model") - val solution = TseitinSolution(model, unassigned) - val negated = negateModel(model) - findAllModels(clauses :+ negated.toSet, solution :: models, recursionDepthAllowed - 1) + val solution = newSolution(model, unassigned) + val negated = negateModel(model).to(scala.collection.immutable.ListSet) + findAllModels(clauses :+ negated, solution :: models, recursionDepthAllowed - 1) } - else models } + } - val tseitinSolutions = findAllModels(solvable.cnf, Nil) - tseitinSolutions.map(_.projectToSolution(solvable.symbolMapping.symForVar)) + findAllModels(solvable.cnf, Nil) } /** Drop trivially true clauses, simplify others by dropping negation of `unitLit`. @@ -485,18 +475,15 @@ trait Solving extends Logic { } } - def findModelFor(solvable: Solvable): Model = { - projectToModel(findTseitinModelFor(solvable.cnf.map(_.toSet)), solvable.symbolMapping.symForVar) - } + def hasModel(solvable: Solvable): Boolean = findTseitinModelFor(solvable.cnf) != NoTseitinModel def findTseitinModelFor(clauses: Array[Clause]): TseitinModel = { - debug.patmat(s"DPLL\n${cnfString(clauses)}") - - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.patmatAnaDPLL) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.patmatAnaDPLL) else null - val satisfiableWithModel = findTseitinModel0((util.Arrays.copyOf(clauses, clauses.length), Nil) :: Nil) + debug.patmat(s"DPLL\n${cnfString(clauses)}") + val satisfiableWithModel = findTseitinModel0((java.util.Arrays.copyOf(clauses, clauses.length), Nil) :: Nil) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.patmatAnaDPLL, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.patmatAnaDPLL, start) satisfiableWithModel } @@ -535,104 +522,85 @@ trait Solving extends Logic { * */ private def findTseitinModel0(state: TseitinSearch): TseitinModel = { - val pos = new util.BitSet() - val neg = new util.BitSet() - - @annotation.tailrec - def loop(state: TseitinSearch): TseitinModel ={ - state match { - case Nil => NoTseitinModel - case (clauses, assignments) :: rest => - if (clauses.isEmpty || clauses.head == null) assignments - else { - var i = 0 - var emptyIndex = -1 - var unitIndex = -1 - while (i < clauses.length && emptyIndex == -1) { - val clause = clauses(i) - if (clause != null) { - clause.size match { - case 0 => emptyIndex = i - case 1 if unitIndex == -1 => - unitIndex = i - case _ => - } + val pos = new java.util.BitSet() + val neg = new java.util.BitSet() + @tailrec def loop(state: TseitinSearch): TseitinModel = state match { + case Nil => NoTseitinModel + case (clauses, assignments) :: rest => + if (clauses.isEmpty || clauses.head == null) assignments + else { + var i = 0 + var emptyIndex = -1 + var unitIndex = -1 + while (i < clauses.length && emptyIndex == -1) { + val clause = clauses(i) + if (clause != null) { + clause.size match { + case 0 => emptyIndex = i + case 1 if unitIndex == -1 => + unitIndex = i + case _ => } - i += 1 } - if (emptyIndex != -1) - loop(rest) - else if (unitIndex != -1) { - val unitLit = clauses(unitIndex).head - dropUnit(clauses, unitLit) - val tuples: TseitinSearch = (clauses, unitLit :: assignments) :: rest - loop(tuples) - } else { - // partition symbols according to whether they appear in positive and/or negative literals - pos.clear() - neg.clear() - for (clause <- clauses) { - if (clause != null) { - clause.foreach { lit: Lit => - if (lit.positive) pos.set(lit.variable) else neg.set(lit.variable) - } + i += 1 + } + if (emptyIndex != -1) + loop(rest) + else if (unitIndex != -1) { + val unitLit = clauses(unitIndex).head + dropUnit(clauses, unitLit) + val tuples: TseitinSearch = (clauses, unitLit :: assignments) :: rest + loop(tuples) + } else { + // partition symbols according to whether they appear in positive and/or negative literals + pos.clear() + neg.clear() + for (clause <- clauses) { + if (clause != null) { + clause.foreach { lit: Lit => + if (lit.positive) pos.set(lit.variable) else neg.set(lit.variable) } } + } - // appearing only in either positive/negative positions - - pos.xor(neg) - val pures = pos - - if (!pures.isEmpty) { - val pureVar = pures.nextSetBit(0) - // turn it back into a literal - // (since equality on literals is in terms of equality - // of the underlying symbol and its positivity, simply construct a new Lit) - val pureLit: Lit = Lit(if (neg.get(pureVar)) -pureVar else pureVar) - // debug.patmat("pure: "+ pureLit +" pures: "+ pures) - val simplified = clauses.filterNot(clause => clause != null && clause.contains(pureLit)) - loop((simplified, pureLit :: assignments) :: rest) - } else { - val split = clauses.find(_ != null).get.head - // debug.patmat("split: "+ split) - var i = 0 - var nullIndex = -1 - while (i < clauses.length && nullIndex == -1) { - if (clauses(i) eq null) nullIndex = i - i += 1 - } + // appearing only in either positive/negative positions - val effectiveLength = if (nullIndex == -1) clauses.length else nullIndex - val posClauses = util.Arrays.copyOf(clauses, effectiveLength + 1) - val negClauses = util.Arrays.copyOf(clauses, effectiveLength + 1) - posClauses(effectiveLength) = Set.empty[Lit] + split - negClauses(effectiveLength) = Set.empty[Lit] + (-split) + pos.xor(neg) + val pures = pos - val pos = (posClauses, assignments) - val neg = (negClauses, assignments) - loop(pos :: neg :: rest) + if (!pures.isEmpty) { + val pureVar = pures.nextSetBit(0) + // turn it back into a literal + // (since equality on literals is in terms of equality + // of the underlying symbol and its positivity, simply construct a new Lit) + val pureLit: Lit = Lit(if (neg.get(pureVar)) -pureVar else pureVar) + // debug.patmat("pure: "+ pureLit +" pures: "+ pures) + val simplified = clauses.filterNot(clause => clause != null && clause.contains(pureLit)) + loop((simplified, pureLit :: assignments) :: rest) + } else { + val split = clauses.find(_ != null).get.head + // debug.patmat("split: "+ split) + var i = 0 + var nullIndex = -1 + while (i < clauses.length && nullIndex == -1) { + if (clauses(i) eq null) nullIndex = i + i += 1 } + + val effectiveLength = if (nullIndex == -1) clauses.length else nullIndex + val posClauses = java.util.Arrays.copyOf(clauses, effectiveLength + 1) + val negClauses = java.util.Arrays.copyOf(clauses, effectiveLength + 1) + posClauses(effectiveLength) = Set.empty[Lit] + split + negClauses(effectiveLength) = Set.empty[Lit] + (-split) + + val pos = (posClauses, assignments) + val neg = (negClauses, assignments) + loop(pos :: neg :: rest) } } - } + } } loop(state) } - - private def projectToModel(model: TseitinModel, symForVar: Map[Int, Sym]): Model = - if (model == NoTseitinModel) NoModel - else if (model == EmptyTseitinModel) EmptyModel - else { - val mappedModels = model.iterator.toList collect { - case lit if symForVar isDefinedAt lit.variable => (symForVar(lit.variable), lit.positive) - } - if (mappedModels.isEmpty) { - // could get an empty model if mappedModels is a constant like `True` - EmptyModel - } else { - mappedModels.toMap - } - } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala index 133b299e5412..4fc3c1fdddd8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala @@ -36,6 +36,10 @@ trait Adaptations { case Apply(_, arg :: Nil) => arg case _ => EmptyTree } + def isInfix = t match { + case Apply(_, arg :: Nil) => t.hasAttachment[MultiargInfixAttachment.type] + case _ => false + } def callString = ( ( if (t.symbol.isConstructor) "new " else "" ) + ( t.symbol.owner.decodedName ) + @@ -86,15 +90,17 @@ trait Adaptations { true // keep adaptation } @inline def warnAdaptation = { - if (settings.warnAdaptedArgs) context.warning(t.pos, adaptWarningMessage( + if (settings.warnAdaptedArgs && !isInfix) context.warning(t.pos, adaptWarningMessage( s"adapted the argument list to the expected ${args.size}-tuple: add additional parens instead"), WarningCategory.LintAdaptedArgs) true // keep adaptation } - if (args.isEmpty) { - if (currentRun.isScala3) noAdaptation else deprecatedAdaptation - } else + if (args.nonEmpty) warnAdaptation + else if (currentRun.isScala3) + noAdaptation + else + deprecatedAdaptation } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index af54b2a51182..a48dad7c960c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -13,8 +13,6 @@ package scala.tools.nsc package typechecker -import scala.reflect.internal.util.StatisticsStatics - /** Defines the sub-components for the namer, packageobjects, and typer phases. */ trait Analyzer extends AnyRef @@ -96,7 +94,7 @@ trait Analyzer extends AnyRef // compiler run). This is good enough for the resident compiler, which was the most affected. undoLog.clear() override def run(): Unit = { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.typerNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.typerNanos) else null global.echoPhaseSummary(this) val units = currentRun.units while (units.hasNext) { @@ -106,13 +104,13 @@ trait Analyzer extends AnyRef finishComputeParamAlias() // defensive measure in case the bookkeeping in deferred macro expansion is buggy clearDelayed() - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.typerNanos, start) - runReporting.reportSuspendedMessages() + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.typerNanos, start) } def apply(unit: CompilationUnit): Unit = { try { val typer = newTyper(rootContext(unit)) unit.body = typer.typed(unit.body) + // interactive typed may finish by throwing a `TyperResult` if (!settings.Youtline) { for (workItem <- unit.toCheck) workItem() if (settings.warnUnusedImport) @@ -122,6 +120,7 @@ trait Analyzer extends AnyRef } } finally { + runReporting.reportSuspendedMessages(unit) unit.toCheck.clear() } } diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala index a86f2c409151..2557867ea966 100644 --- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -16,7 +16,7 @@ package typechecker /** * @author Lukas Rytz */ -trait AnalyzerPlugins { self: Analyzer => +trait AnalyzerPlugins { self: Analyzer with splain.SplainData => import global._ trait AnalyzerPlugin { @@ -179,6 +179,16 @@ trait AnalyzerPlugins { self: Analyzer => * @param result The result to a given implicit search. */ def pluginsNotifyImplicitSearchResult(result: SearchResult): Unit = () + + /** + * Construct a custom error message for implicit parameters that could not be resolved. + * + * @param param The implicit parameter that was resolved + * @param errors The chain of intermediate implicits that lead to this error + * @param previous The error message constructed by the previous analyzer plugin, or the builtin default + */ + def noImplicitFoundError(param: Symbol, errors: List[ImplicitError], previous: String): String = + previous } /** @@ -390,6 +400,13 @@ trait AnalyzerPlugins { self: Analyzer => def accumulate = (_, p) => p.pluginsNotifyImplicitSearchResult(result) }) + /** @see AnalyzerPlugin.noImplicitFoundError */ + def pluginsNoImplicitFoundError(param: Symbol, errors: List[ImplicitError], initial: String): String = + invoke(new CumulativeOp[String] { + def default = initial + def accumulate = (previous, p) => p.noImplicitFoundError(param, errors, previous) + }) + /** A list of registered macro plugins */ private var macroPlugins: List[MacroPlugin] = Nil diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 57888bf6d3cc..cb5e3889b190 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -16,6 +16,7 @@ package typechecker import scala.reflect.internal.util.StringOps.{countAsString, countElementsAsString} import java.lang.System.{lineSeparator => EOL} +import scala.PartialFunction.cond import scala.annotation.tailrec import scala.reflect.runtime.ReflectionUtils import scala.reflect.macros.runtime.AbortMacroException @@ -24,7 +25,7 @@ import scala.tools.nsc.util.stackTraceString import scala.reflect.io.NoAbstractFile import scala.reflect.internal.util.NoSourceFile -trait ContextErrors { +trait ContextErrors extends splain.SplainErrors { self: Analyzer => import global._ @@ -107,7 +108,7 @@ trait ContextErrors { def issueTypeError(err: AbsTypeError)(implicit context: Context): Unit = { context.issue(err) } def typeErrorMsg(context: Context, found: Type, req: Type) = - if (context.openImplicits.nonEmpty && !settings.XlogImplicits.value && currentRun.isScala213) + if (context.openImplicits.nonEmpty && !settings.Vimplicits) // OPT: avoid error string creation for errors that won't see the light of day, but predicate // this on -Xsource:2.13 for bug compatibility with https://github.com/scala/scala/pull/7147#issuecomment-418233611 "type mismatch" @@ -151,8 +152,26 @@ trait ContextErrors { def MacroCantExpandIncompatibleMacrosError(internalMessage: String) = MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage) + /** The implicit not found message from the annotation, and whether it's a supplement message or not. */ + def NoImplicitFoundAnnotation(tree: Tree, param: Symbol): (Boolean, String) = { + param match { + case ImplicitNotFoundMsg(msg) => (false, msg.formatParameterMessage(tree)) + case _ => + val paramTp = param.tpe + paramTp.typeSymbolDirect match { + case ImplicitNotFoundMsg(msg) => (false, msg.formatDefSiteMessage(paramTp)) + case _ => + val supplement = param.baseClasses.collectFirst { + case ImplicitNotFoundMsg(msg) => s" (${msg.formatDefSiteMessage(paramTp)})" + }.getOrElse("") + true -> supplement + } + } + } + def NoImplicitFoundError(tree: Tree, param: Symbol)(implicit context: Context): Unit = { - def errMsg = { + val (isSupplement, annotationMsg) = NoImplicitFoundAnnotation(tree, param) + def defaultErrMsg = { val paramName = param.name val paramTp = param.tpe def evOrParam = @@ -160,21 +179,11 @@ trait ContextErrors { "evidence parameter of type" else s"parameter $paramName:" - - param match { - case ImplicitNotFoundMsg(msg) => msg.formatParameterMessage(tree) - case _ => - paramTp.typeSymbolDirect match { - case ImplicitNotFoundMsg(msg) => msg.formatDefSiteMessage(paramTp) - case _ => - val supplement = param.baseClasses.collectFirst { - case ImplicitNotFoundMsg(msg) => s" (${msg.formatDefSiteMessage(paramTp)})" - }.getOrElse("") - s"could not find implicit value for $evOrParam $paramTp$supplement" - } - } + if (isSupplement) s"could not find implicit value for $evOrParam $paramTp$annotationMsg" + else annotationMsg } - issueNormalTypeError(tree, errMsg) + val errMsg = splainPushOrReportNotFound(tree, param, annotationMsg) + issueNormalTypeError(tree, if (errMsg.isEmpty) defaultErrMsg else errMsg) } trait TyperContextErrors { @@ -1166,9 +1175,15 @@ trait ContextErrors { val proscription = if (tree.symbol.isConstructor) " cannot be invoked with " else " cannot be applied to " + val junkNames = { + val bads = argtpes.collect { + case NamedType(name, _) if !alts.exists(cond(_) { case MethodType(params, _) => params.exists(_.name == name) }) => name.decoded + } + if (bads.isEmpty) "" else bads.mkString(" [which have no such parameter ", ",", "]") + } issueNormalTypeError(tree, - applyErrorMsg(tree, proscription, widenedArgtpes, pt)) + applyErrorMsg(tree, junkNames + proscription, widenedArgtpes, pt)) // since inferMethodAlternative modifies the state of the tree // we have to set the type of tree to ErrorType only in the very last // fallback action that is done in the inference. @@ -1346,9 +1361,6 @@ trait ContextErrors { def ParentSealedInheritanceError(parent: Tree, psym: Symbol) = NormalTypeError(parent, "illegal inheritance from sealed " + psym ) - def RootImportError(tree: Tree) = - issueNormalTypeError(tree, "_root_ cannot be imported") - def SymbolValidationError(sym: Symbol, errKind: SymValidateErrors.Value): Unit = { val msg = errKind match { case ImplicitConstr => "`implicit` modifier not allowed for constructors" diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index f35afefcf558..5c7e3128b8ed 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -822,8 +822,9 @@ trait Contexts { self: Analyzer => // TODO: buffer deprecations under silent (route through ContextReporter, store in BufferingReporter) def deprecationWarning(pos: Position, sym: Symbol, msg: String, since: String): Unit = runReporting.deprecationWarning(fixPosition(pos), sym, owner, msg, since) + def deprecationWarning(pos: Position, sym: Symbol): Unit = - runReporting.deprecationWarning(fixPosition(pos), sym, owner) // TODO: allow this to escalate to an error, and implicit search will ignore deprecated implicits + runReporting.deprecationWarning(fixPosition(pos), sym, owner) def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit = runReporting.featureWarning(fixPosition(pos), featureName, featureDesc, featureTrait, construct, required, owner) @@ -1057,7 +1058,7 @@ trait Contexts { self: Analyzer => ) && !(imported && { val e = scope.lookupEntry(name) - (e ne null) && (e.owner == scope) && (!currentRun.isScala212 || e.sym.exists) + (e ne null) && (e.owner == scope) && e.sym.exists }) /** Do something with the symbols with name `name` imported via the import in `imp`, diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 396cec2e53cc..4d18d7b86953 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -83,8 +83,8 @@ abstract class Duplicators extends Analyzer { val sym1 = ( context.scope lookup sym.name orElse { // try harder (look in outer scopes) - // with virtpatmat, this can happen when the sym is referenced in the scope of a LabelDef but - // is defined in the scope of an outer DefDef (e.g., in AbstractPartialFunction's andThen) + // with virtpatmat, this could happen when the sym was referenced in the scope of a LabelDef but + // was defined in the scope of an outer DefDef (e.g., in AbstractPartialFunction's andThen) BodyDuplicator.super.silent(_ typedType Ident(sym.name)).fold(NoSymbol: Symbol)(_.symbol) } filter (_ ne sym) ) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 1ed5bfd55f52..fe3a8549c5d0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -24,7 +24,7 @@ import scala.collection.mutable import mutable.{LinkedHashMap, ListBuffer} import scala.util.matching.Regex import symtab.Flags._ -import scala.reflect.internal.util.{ReusableInstance, Statistics, StatisticsStatics, TriState} +import scala.reflect.internal.util.{ReusableInstance, Statistics, TriState} import scala.reflect.internal.TypesStats import scala.language.implicitConversions import scala.tools.nsc.Reporting.WarningCategory @@ -33,7 +33,7 @@ import scala.tools.nsc.Reporting.WarningCategory * * @author Martin Odersky */ -trait Implicits { +trait Implicits extends splain.SplainData { self: Analyzer => import global._ @@ -99,18 +99,20 @@ trait Implicits { // Note that the isInvalidConversionTarget seems to make a lot more sense right here, before all the // work is performed, than at the point where it presently exists. val shouldPrint = printTypings && !context.undetparams.isEmpty - val findMemberStart = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startCounter(findMemberImpl) else null - val subtypeStart = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startCounter(subtypeImpl) else null - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(implicitNanos) else null + val findMemberStart = if (settings.areStatisticsEnabled) statistics.startCounter(findMemberImpl) else null + val subtypeStart = if (settings.areStatisticsEnabled) statistics.startCounter(subtypeImpl) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(implicitNanos) else null if (shouldPrint) typingStack.printTyping(tree, "typing implicit: %s %s".format(tree, context.undetparamsString)) val implicitSearchContext = context.makeImplicit(reportAmbiguous) + ImplicitErrors.startSearch(pt) val dpt = if (isView) pt else dropByName(pt) val isByName = dpt ne pt val search = new ImplicitSearch(tree, dpt, isView, implicitSearchContext, pos, isByName) pluginsNotifyImplicitSearch(search) val result = search.bestImplicit pluginsNotifyImplicitSearchResult(result) + ImplicitErrors.finishSearch(result.isSuccess, pt) if (result.isFailure && saveAmbiguousDivergent && implicitSearchContext.reporter.hasErrors) implicitSearchContext.reporter.propagateImplicitTypeErrorsTo(context.reporter) @@ -121,9 +123,9 @@ trait Implicits { // and then filter out any which *were* inferred and are part of the substitutor in the implicit search result. context.undetparams = ((context.undetparams ++ result.undetparams) filterNot result.subst.from.contains).distinct - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(implicitNanos, start) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopCounter(findMemberImpl, findMemberStart) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopCounter(subtypeImpl, subtypeStart) + if (settings.areStatisticsEnabled) statistics.stopTimer(implicitNanos, start) + if (settings.areStatisticsEnabled) statistics.stopCounter(findMemberImpl, findMemberStart) + if (settings.areStatisticsEnabled) statistics.stopCounter(subtypeImpl, subtypeStart) if (result.isSuccess && settings.lintImplicitRecursion && result.tree.symbol != null) { val s = @@ -146,7 +148,7 @@ trait Implicits { if (result.isFailure && !silent) { val err = context.reporter.firstError val errPos = err.map(_.errPos).getOrElse(pos) - val errMsg = err.map(_.errMsg).getOrElse("implicit search has failed. to find out the reason, turn on -Xlog-implicits") + val errMsg = err.map(_.errMsg).getOrElse("implicit search has failed. to find out the reason, turn on -Vimplicits") onError(errPos, errMsg) } result.tree @@ -420,7 +422,7 @@ trait Implicits { } import infer._ - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(implicitSearchCount) + if (settings.areStatisticsEnabled) statistics.incCounter(implicitSearchCount) /** The type parameters to instantiate */ val undetParams = if (isView) Nil else context.outer.undetparams @@ -443,19 +445,19 @@ trait Implicits { def pos = if (pos0 != NoPosition) pos0 else tree.pos @inline final def failure(what: Any, reason: => String, pos: Position = this.pos): SearchResult = { - if (settings.XlogImplicits) + if (settings.debug) reporter.echo(pos, s"$what is not a valid implicit value for $pt because:\n$reason") SearchFailure } /** Is implicit info `info1` better than implicit info `info2`? */ def improves(info1: ImplicitInfo, info2: ImplicitInfo) = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(improvesCount) + if (settings.areStatisticsEnabled) statistics.incCounter(improvesCount) (info2 == NoImplicitInfo) || (info1 != NoImplicitInfo) && { if (info1.sym.isStatic && info2.sym.isStatic) { improvesCache get ((info1, info2)) match { - case Some(b) => if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(improvesCachedCount); b + case Some(b) => if (settings.areStatisticsEnabled) statistics.incCounter(improvesCachedCount); b case None => val result = isStrictlyMoreSpecific(info1.tpe, info2.tpe, info1.sym, info2.sym) improvesCache((info1, info2)) = result @@ -648,14 +650,14 @@ trait Implicits { * This method is performance critical: 5-8% of typechecking time. */ private def matchesPt(tp: Type, pt: Type, undet: List[Symbol]): Boolean = { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(matchesPtNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(matchesPtNanos) else null val result = normSubType(tp, pt) || isView && { pt match { case Function1(arg1, arg2) => matchesPtView(tp, arg1, arg2, undet) case _ => false } } - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(matchesPtNanos, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(matchesPtNanos, start) result } private def matchesPt(info: ImplicitInfo): Boolean = ( @@ -682,7 +684,7 @@ trait Implicits { } private def matchesPtInst(info: ImplicitInfo): Boolean = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(matchesPtInstCalls) + if (settings.areStatisticsEnabled) statistics.incCounter(matchesPtInstCalls) info.tpe match { case PolyType(tparams, restpe) => try { @@ -691,7 +693,7 @@ trait Implicits { val tp = ApproximateDependentMap(restpe) val tpInstantiated = tp.instantiateTypeParams(allUndetparams, tvars) if(!matchesPt(tpInstantiated, wildPt, allUndetparams)) { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(matchesPtInstMismatch1) + if (settings.areStatisticsEnabled) statistics.incCounter(matchesPtInstMismatch1) false } else { // we can't usefully prune views any further because we would need to type an application @@ -701,7 +703,7 @@ trait Implicits { val adjusted = adjustTypeArgs(allUndetparams, tvars, targs) val tpSubst = deriveTypeWithWildcards(adjusted.undetParams)(tp.instantiateTypeParams(adjusted.okParams, adjusted.okArgs)) if(!matchesPt(tpSubst, wildPt, adjusted.undetParams)) { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(matchesPtInstMismatch2) + if (settings.areStatisticsEnabled) statistics.incCounter(matchesPtInstMismatch2) false } else true } @@ -799,7 +801,7 @@ trait Implicits { } private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean, isLocalToCallsite: Boolean): SearchResult = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(plausiblyCompatibleImplicits) + if (settings.areStatisticsEnabled) statistics.incCounter(plausiblyCompatibleImplicits) val ok = ptChecked || matchesPt(info) && { def word = if (isLocalToCallsite) "local " else "" typingLog("match", s"$word$info") @@ -809,7 +811,7 @@ trait Implicits { } private def typedImplicit1(info: ImplicitInfo, isLocalToCallsite: Boolean): SearchResult = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(matchingImplicits) + if (settings.areStatisticsEnabled) statistics.incCounter(matchingImplicits) // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints val isScaladoc = context.tree == EmptyTree @@ -865,7 +867,7 @@ trait Implicits { case None => } - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(typedImplicits) + if (settings.areStatisticsEnabled) statistics.incCounter(typedImplicits) val itree3 = if (isView) treeInfo.dissectApplied(itree2).callee else adapt(itree2, EXPRmode, wildPt) @@ -906,7 +908,9 @@ trait Implicits { // bounds check on the expandee tree itree3.attachments.get[MacroExpansionAttachment] match { case Some(MacroExpansionAttachment(exp @ TypeApply(fun, targs), _)) => - checkBounds(exp, NoPrefix, NoSymbol, fun.symbol.typeParams, targs.map(_.tpe), "inferred ") + val targTpes = mapList(targs)(_.tpe) + val withinBounds = checkBounds(exp, NoPrefix, NoSymbol, fun.symbol.typeParams, targTpes, "inferred ") + if (!withinBounds) splainPushNonconformantBonds(pt, tree, targTpes, undetParams, None) case _ => () } @@ -953,10 +957,11 @@ trait Implicits { context.reporter.firstError match { case Some(err) => + splainPushImplicitSearchFailure(itree3, pt, err) fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) case None => val result = new SearchResult(unsuppressMacroExpansion(itree3), subst, context.undetparams) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(foundImplicits) + if (settings.areStatisticsEnabled) statistics.incCounter(foundImplicits) typingLog("success", s"inferred value of type $ptInstantiated is $result") result } @@ -1087,19 +1092,12 @@ trait Implicits { /** Sorted list of eligible implicits. */ - private def eligibleOld = Shadower.using(isLocalToCallsite){ shadower => - val matches = iss flatMap { is => + private def eligibleOld = Shadower.using(isLocalToCallsite) { shadower => + iss flatMap { is => val result = is filter (info => checkValid(info.sym) && survives(info, shadower)) shadower addInfos is result } - - if (currentRun.isScala213) matches - else { - // most frequent one first under Scala 2.12 mode. We've turned this optimization off to avoid - // compilation order variation in whether a search succeeds or diverges. - matches sortBy (x => if (isView) -x.useCountView else -x.useCountArg) - } } /** Sorted list of eligible implicits. @@ -1173,16 +1171,7 @@ trait Implicits { } } - val eligible: List[ImplicitInfo] = { - val matches = if (shadowerUseOldImplementation) eligibleOld else eligibleNew - if (currentRun.isScala213) matches - else { - // most frequent one first under Scala 2.12 mode. We've turned this optimization off to avoid - // compilation order variation in whether a search succeeds or diverges. - matches sortBy (x => if (isView) -x.useCountView else -x.useCountArg) - } - } - + val eligible: List[ImplicitInfo] = if (shadowerUseOldImplementation) eligibleOld else eligibleNew if (eligible.nonEmpty) printTyping(tree, "" + eligible.size + s" eligible for pt=$pt at ${fullSiteString(context)}") @@ -1222,7 +1211,7 @@ trait Implicits { foreach2(undetParams, savedInfos){ (up, si) => up.setInfo(si) } } } - if (typedFirstPending.isFailure && currentRun.isScala213) + if (typedFirstPending.isFailure) undoLog.undoTo(mark) // Don't accumulate constraints from typechecking or type error message creation for failed candidates // Pass the errors to `DivergentImplicitRecovery` so that it can note @@ -1291,11 +1280,11 @@ trait Implicits { * @return map from infos to search results */ def applicableInfos(iss: Infoss, isLocalToCallsite: Boolean): mutable.LinkedHashMap[ImplicitInfo, SearchResult] = { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startCounter(subtypeAppInfos) else null + val start = if (settings.areStatisticsEnabled) statistics.startCounter(subtypeAppInfos) else null val computation = new ImplicitComputation(iss, isLocalToCallsite) { } val applicable = computation.findAll() - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopCounter(subtypeAppInfos, start) + if (settings.areStatisticsEnabled) statistics.stopCounter(subtypeAppInfos, start) applicable } @@ -1439,13 +1428,13 @@ trait Implicits { * such that some part of `tp` has C as one of its superclasses. */ private def implicitsOfExpectedType: Infoss = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(implicitCacheAccs) + if (settings.areStatisticsEnabled) statistics.incCounter(implicitCacheAccs) implicitsCache get pt match { case Some(implicitInfoss) => - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(implicitCacheHits) + if (settings.areStatisticsEnabled) statistics.incCounter(implicitCacheHits) implicitInfoss case None => - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(subtypeETNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(subtypeETNanos) else null // val implicitInfoss = companionImplicits(pt) val implicitInfoss1 = companionImplicitMap(pt).valuesIterator.toList // val is1 = implicitInfoss.flatten.toSet @@ -1454,7 +1443,7 @@ trait Implicits { // if (!(is2 contains i)) println("!!! implicit infos of "+pt+" differ, new does not contain "+i+",\nold: "+implicitInfoss+",\nnew: "+implicitInfoss1) // for (i <- is2) // if (!(is1 contains i)) println("!!! implicit infos of "+pt+" differ, old does not contain "+i+",\nold: "+implicitInfoss+",\nnew: "+implicitInfoss1) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(subtypeETNanos, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(subtypeETNanos, start) implicitsCache(pt) = implicitInfoss1 if (implicitsCache.size >= sizeLimit) implicitsCache -= implicitsCache.keysIterator.next() @@ -1508,17 +1497,17 @@ trait Implicits { // so that if we find one, we could convert it to whatever universe we need by the means of the `in` method // if no tag is found in scope, we end up here, where we ask someone to materialize the tag for us // however, since the original search was about a tag with no particular prefix, we cannot proceed - // this situation happens very often, so emitting an error message here (even if only for -Xlog-implicits) would be too much + // this situation happens very often, so emitting an error message here (even if only for -Vimplicits) would be too much //return failure(tp, "tag error: unsupported prefix type %s (%s)".format(pre, pre.kind)) return SearchFailure } ) // todo. migrate hardcoded materialization in Implicits to corresponding implicit macros val materializer = atPos(pos.focus)(gen.mkMethodCall(TagMaterializers(tagClass), List(tp), if (prefix != EmptyTree) List(prefix) else List())) - if (settings.XlogImplicits) reporter.echo(pos, "materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) + if (settings.debug) reporter.echo(pos, "materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) if (context.macrosEnabled) success(materializer) // don't call `failure` here. if macros are disabled, we just fail silently - // otherwise -Xlog-implicits will spam the long with zillions of "macros are disabled" + // otherwise -Vimplicits/-Vdebug will spam the long with zillions of "macros are disabled" // this is ugly but temporary, since all this code will be removed once I fix implicit macros else SearchFailure } @@ -1536,7 +1525,7 @@ trait Implicits { if (args contains EmptyTree) EmptyTree else typedPos(tree.pos.focus) { val mani = gen.mkManifestFactoryCall(full, constructor, tparg, args.toList) - if (settings.debug) println("generated manifest: "+mani) // DEBUG + if (settings.isDebug) println("generated manifest: "+mani) // DEBUG mani } @@ -1700,7 +1689,7 @@ trait Implicits { * If all fails return SearchFailure */ def bestImplicit: SearchResult = { - val stats = StatisticsStatics.areSomeColdStatsEnabled + val stats = settings.areStatisticsEnabled val failstart = if (stats) statistics.startTimer(inscopeFailNanos) else null val succstart = if (stats) statistics.startTimer(inscopeSucceedNanos) else null @@ -1773,7 +1762,7 @@ trait Implicits { } } - if (result.isFailure && settings.debug) // debuglog is not inlined for some reason + if (result.isFailure && settings.isDebug) // debuglog is not inlined for some reason log(s"no implicits found for ${pt} ${pt.typeSymbol.info.baseClasses} ${implicitsOfExpectedType}") result diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 094dc1032487..3457e2326bc5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -239,7 +239,7 @@ trait Infer extends Checkable { // When filtering sym down to the accessible alternatives leaves us empty handed. private def checkAccessibleError(tree: Tree, sym: Symbol, pre: Type, site: Tree): Tree = { - if (settings.debug) { + if (settings.isDebug) { Console.println(context) Console.println(tree) Console.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType)) diff --git a/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala b/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala index 31eeedf2853e..267501f23175 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MacroAnnotationNamers.scala @@ -166,7 +166,17 @@ trait MacroAnnotationNamers { self: Analyzer => protected def weakEnsureCompanionObject(cdef: ClassDef, creator: ClassDef => Tree = companionModuleDef(_)): Symbol = { val m = patchedCompanionSymbolOf(cdef.symbol, context) if (m != NoSymbol && currentRun.compiles(m)) m - else { val mdef = atPos(cdef.pos.focus)(creator(cdef)); enterSym(mdef); markWeak(mdef.symbol) } + else { + val existsVal = context.tree.children.find { + case ValDef(_, term, _, _) if cdef.getterName == term => true + case _ => false + } + if (existsVal.isDefined) NoSymbol else { + val mdef = atPos(cdef.pos.focus)(creator(cdef)) + enterSym(mdef) + markWeak(mdef.symbol) + } + } } protected def finishSymbol(tree: Tree): Unit = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index b7bf7a219dcb..073cf5e13968 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -18,7 +18,7 @@ import java.lang.Math.min import symtab.Flags._ import scala.reflect.internal.util.ScalaClassLoader import scala.reflect.runtime.ReflectionUtils -import scala.reflect.internal.util.{Statistics, StatisticsStatics} +import scala.reflect.internal.util.Statistics import scala.reflect.internal.TypesStats import scala.reflect.macros.util._ import scala.util.control.ControlThrowable @@ -562,8 +562,8 @@ trait Macros extends MacroRuntimes with Traces with Helpers { if (macroDebugVerbose) println(s"macroExpand: ${summary()}") linkExpandeeAndDesugared(expandee, desugared) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.macroExpandNanos) else null - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(statistics.macroExpandCount) + val start = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.macroExpandNanos) else null + if (settings.areStatisticsEnabled) statistics.incCounter(statistics.macroExpandCount) try { withInfoLevel(nodePrinters.InfoLevel.Quiet) { // verbose printing might cause recursive macro expansions if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { @@ -596,7 +596,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers { } } } finally { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.macroExpandNanos, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.macroExpandNanos, start) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 7af86c768f66..bdda512b6dbd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -632,7 +632,7 @@ trait Namers extends MethodSynthesis { def assignParamTypes(copyDef: DefDef, sym: Symbol): Unit = { val clazz = sym.owner val constructorType = clazz.primaryConstructor.tpe - val subst = new SubstSymMap(clazz.typeParams, copyDef.tparams map (_.symbol)) + val subst = SubstSymMap(clazz.typeParams, copyDef.tparams.map(_.symbol)) val classParamss = constructorType.paramss foreach2(copyDef.vparamss, classParamss)((copyParams, classParams) => @@ -1724,7 +1724,7 @@ trait Namers extends MethodSynthesis { val valOwner = owner.owner // there's no overriding outside of classes, and we didn't use to do this in 2.11, so provide opt-out - if (!currentRun.isScala212 || !valOwner.isClass) WildcardType + if (!valOwner.isClass) WildcardType else { // normalize to getter so that we correctly consider a val overriding a def // (a val's name ends in a " ", so can't compare to def) @@ -1825,9 +1825,6 @@ trait Namers extends MethodSynthesis { val Import(expr, selectors) = imp val expr1 = typer.typedQualifier(expr) - if (expr1.symbol != null && expr1.symbol.isRootPackage) - RootImportError(imp) - if (expr1.isErrorTyped) ErrorType else { diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 1eaca48723a8..d69f02710abc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -53,8 +53,8 @@ abstract class RefChecks extends Transform { def newTransformer(unit: CompilationUnit): RefCheckTransformer = new RefCheckTransformer(unit) - val toJavaRepeatedParam = new SubstSymMap(RepeatedParamClass -> JavaRepeatedParamClass) - val toScalaRepeatedParam = new SubstSymMap(JavaRepeatedParamClass -> RepeatedParamClass) + val toJavaRepeatedParam = SubstSymMap(RepeatedParamClass -> JavaRepeatedParamClass) + val toScalaRepeatedParam = SubstSymMap(JavaRepeatedParamClass -> RepeatedParamClass) def accessFlagsToString(sym: Symbol) = flagsToString( sym getFlag (PRIVATE | PROTECTED), @@ -148,7 +148,7 @@ abstract class RefChecks extends Transform { } // This has become noisy with implicit classes. - if (settings.warnPolyImplicitOverload && settings.developer) { + if (settings.isDeveloper && settings.warnPolyImplicitOverload) { clazz.info.decls.foreach(sym => if (sym.isImplicit && sym.typeParams.nonEmpty) { // implicit classes leave both a module symbol and a method symbol as residue val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule) @@ -303,7 +303,7 @@ abstract class RefChecks extends Transform { def isNeitherInClass = memberClass != clazz && otherClass != clazz val indent = " " - def overriddenWithAddendum(msg: String, foundReq: Boolean = settings.debug.value): String = { + def overriddenWithAddendum(msg: String, foundReq: Boolean = settings.isDebug): String = { val isConcreteOverAbstract = (otherClass isSubClass memberClass) && other.isDeferred && !member.isDeferred val addendum = @@ -383,7 +383,7 @@ abstract class RefChecks extends Transform { def isOverrideAccessOK = member.isPublic || { // member is public, definitely same or relaxed access (!other.isProtected || member.isProtected) && // if o is protected, so is m ((!isRootOrNone(ob) && ob.hasTransOwner(mb)) || // m relaxes o's access boundary - other.isJavaDefined) // overriding a protected java member, see #3946 + (other.isJavaDefined && other.isProtected)) // overriding a protected java member, see #3946 #12349 } if (!isOverrideAccessOK) { overrideAccessError() @@ -1404,14 +1404,14 @@ abstract class RefChecks extends Transform { false } - private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean) = tp match { + private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean): Unit = tp match { case TypeRef(pre, sym, args) => tree match { case tt: TypeTree if tt.original == null => // scala/bug#7783 don't warn about inferred types // FIXME: reconcile this check with one in resetAttrs case _ => checkUndesiredProperties(sym, tree.pos) } - if(sym.isJavaDefined) + if (sym.isJavaDefined) sym.typeParams foreach (_.cookJavaRawInfo()) if (!tp.isHigherKinded && !skipBounds) checkBounds(tree, pre, sym.owner, sym.typeParams, args) @@ -1434,8 +1434,18 @@ abstract class RefChecks extends Transform { } private def applyRefchecksToAnnotations(tree: Tree): Unit = { + def checkVarArgs(tp: Type, tree: Tree): Unit = tp match { + case TypeRef(_, VarargsClass, _) => + tree match { + case tt: TypeTree if tt.original == null => // same exception as in checkTypeRef + case _: DefDef => + case _ => reporter.error(tree.pos, s"Only methods can be marked @varargs") + } + case _ => + } def applyChecks(annots: List[AnnotationInfo]): List[AnnotationInfo] = if (annots.isEmpty) Nil else { annots.foreach { ann => + checkVarArgs(ann.atp, tree) checkTypeRef(ann.atp, tree, skipBounds = false) checkTypeRefBounds(ann.atp, tree) if (ann.original != null && ann.original.hasExistingSymbol) @@ -1486,7 +1496,7 @@ abstract class RefChecks extends Transform { reporter.error(sym.pos, s"${sym.name}: Only concrete methods can be marked @elidable.$rest") } } - if (currentRun.isScala213) checkIsElidable(tree.symbol) + checkIsElidable(tree.symbol) def checkMember(sym: Symbol): Unit = { sym.setAnnotations(applyChecks(sym.annotations)) @@ -1868,7 +1878,7 @@ abstract class RefChecks extends Transform { result1 } catch { case ex: TypeError => - if (settings.debug) ex.printStackTrace() + if (settings.isDebug) ex.printStackTrace() reporter.error(tree.pos, ex.getMessage()) tree } finally { diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index ccdbabaff4c4..ef168e5926c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -356,6 +356,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT && !sym.owner.isTrait && sym.owner.enclosingPackageClass != currentClass.enclosingPackageClass && qual.symbol.info.member(sym.name).exists + && !(currentClass.typeOfThis.typeSymbol.isSubClass(sym.owner)) // scala/bug#11924 && !needsProtectedAccessor(sym, tree.pos) ) if (shouldEnsureAccessor) { diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index bda816b31af0..b4e0d5339c01 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -130,7 +130,7 @@ abstract class TreeCheckers extends Analyzer { // new symbols if (newSyms.nonEmpty) { informFn("" + newSyms.size + " new symbols.") - val toPrint = if (settings.debug) sortedNewSyms mkString " " else "" + val toPrint = if (settings.isDebug) sortedNewSyms mkString " " else "" newSyms.clear() if (toPrint != "") @@ -177,7 +177,7 @@ abstract class TreeCheckers extends Analyzer { def errorFn(msg: Any): Unit = errorFn(NoPosition, msg) def informFn(msg: Any): Unit = { - if (settings.verbose || settings.debug) + if (settings.verbose || settings.isDebug) println("[check: %s] %s".format(phase.prev, msg)) } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 2ee0a2efba1f..cef28da57f62 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -40,7 +40,7 @@ import scala.tools.nsc.Reporting.WarningCategory * * @author Paul Phillips */ -trait TypeDiagnostics { +trait TypeDiagnostics extends splain.SplainDiagnostics { self: Analyzer with StdAttachments => import global._ @@ -310,7 +310,7 @@ trait TypeDiagnostics { // when the message will never be seen. I though context.reportErrors // being false would do that, but if I return "" under // that condition, I see it. - def foundReqMsg(found: Type, req: Type): String = { + def builtinFoundReqMsg(found: Type, req: Type): String = { val foundWiden = found.widen val reqWiden = req.widen val sameNamesDifferentPrefixes = @@ -340,6 +340,11 @@ trait TypeDiagnostics { } } + def foundReqMsg(found: Type, req: Type): String = { + val errMsg = splainFoundReqMsg(found, req) + if (errMsg.isEmpty) builtinFoundReqMsg(found, req) else errMsg + } + def typePatternAdvice(sym: Symbol, ptSym: Symbol) = { val clazz = if (sym.isModuleClass) sym.companionClass else sym val caseString = @@ -815,7 +820,7 @@ trait TypeDiagnostics { // but it seems that throwErrors excludes some of the errors that should actually be // buffered, causing TypeErrors to fly around again. This needs some more investigation. if (!context0.reportErrors) throw ex - if (settings.debug) ex.printStackTrace() + if (settings.isDebug) ex.printStackTrace() ex match { case CyclicReference(sym, info: TypeCompleter) => diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala index 1290964fdffd..48b7b7c45bae 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala @@ -59,7 +59,7 @@ trait StructuredTypeStrings extends DestructureTypes { else block(level, grouping)(name, nodes) } private def shortClass(x: Any) = { - if (settings.debug) { + if (settings.isDebug) { val name = (x.getClass.getName split '.').last val str = if (TypeStrings.isAnonClass(x.getClass)) name else (name split '$').last diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index cdf51fa5836b..3efe38df1519 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -17,7 +17,7 @@ package typechecker import scala.annotation.tailrec import scala.collection.mutable import scala.reflect.internal.{Chars, TypesStats} -import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics, StatisticsStatics} +import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics} import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory} import scala.util.chaining._ import mutable.ListBuffer @@ -232,7 +232,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // (it erases in TypeTrees, but not in, e.g., the type a Function node) def phasedAppliedType(sym: Symbol, args: List[Type]) = { val tp = appliedType(sym, args) - if (phase.erasedTypes) erasure.specialScalaErasure(tp) else tp + if (phase.erasedTypes) erasure.specialScalaErasureFor(sym)(tp) else tp } def typedDocDef(docDef: DocDef, mode: Mode, pt: Type): Tree = @@ -672,13 +672,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def silent[T](op: Typer => T, reportAmbiguousErrors: Boolean = context.ambiguousErrors, newtree: Tree = context.tree): SilentResult[T] = { - val findMemberStart = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startCounter(findMemberFailed) else null - val subtypeStart = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startCounter(subtypeFailed) else null - val failedSilentStart = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(failedSilentNanos) else null + val findMemberStart = if (settings.areStatisticsEnabled) statistics.startCounter(findMemberFailed) else null + val subtypeStart = if (settings.areStatisticsEnabled) statistics.startCounter(subtypeFailed) else null + val failedSilentStart = if (settings.areStatisticsEnabled) statistics.startTimer(failedSilentNanos) else null def stopStats() = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopCounter(findMemberFailed, findMemberStart) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopCounter(subtypeFailed, subtypeStart) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(failedSilentNanos, failedSilentStart) + if (settings.areStatisticsEnabled) statistics.stopCounter(findMemberFailed, findMemberStart) + if (settings.areStatisticsEnabled) statistics.stopCounter(subtypeFailed, subtypeStart) + if (settings.areStatisticsEnabled) statistics.stopTimer(failedSilentNanos, failedSilentStart) } @inline def wrapResult(reporter: ContextReporter, result: T) = if (reporter.hasErrors) { @@ -1100,7 +1100,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def adaptExprNotFunMode(): Tree = { def lastTry(err: AbsTypeError = null): Tree = { debuglog("error tree = " + tree) - if (settings.debug && settings.explaintypes) explainTypes(tree.tpe, pt) + if (settings.isDebug && settings.explaintypes) explainTypes(tree.tpe, pt) if (err ne null) context.issue(err) if (tree.tpe.isErroneous || pt.isErroneous) setError(tree) else adaptMismatchedSkolems() @@ -1803,10 +1803,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!ps.isEmpty && !superclazz.isSubClass(ps.head.typeSymbol)) pending += ParentSuperSubclassError(parent, superclazz, ps.head.typeSymbol, psym) if (!clazzIsTrait) { + def hasTraitParams(sym: Symbol) = + sym.isScala3Defined && sym.isTrait && sym.hasAttachment[DottyParameterisedTrait] // TODO perhaps there can be a flag to skip this when we know there can be no Scala 3 definitions // or otherwise use an optimised representation for trait parameters (parent.tpe :: ps).collectFirst { - case p if p.typeSymbol.hasAttachment[DottyParameterisedTrait] => + case p if hasTraitParams(p.typeSymbol) => p.typeSymbol.attachments.get[DottyParameterisedTrait].foreach( attach => pending += ParentIsScala3TraitError(parent, p.typeSymbol, attach.params, psym) ) @@ -3582,7 +3584,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case _ if currentRun.runDefinitions.isPolymorphicSignature(fun.symbol) => // Mimic's Java's treatment of polymorphic signatures as described in - // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.3 + // https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.3 // // One can think of these methods as being infinitely overloaded. We create // a fictitious new cloned method symbol for each call site that takes on a signature @@ -3590,7 +3592,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val args1 = typedArgs(args, forArgMode(fun, mode)) val clone = fun.symbol.cloneSymbol.withoutAnnotations val cloneParams = args1.map(arg => clone.newValueParameter(freshTermName()).setInfo(arg.tpe.deconst)) - val resultType = if (isFullyDefined(pt)) pt else ObjectTpe + val resultType = + if (fun.symbol.tpe.resultType.typeSymbol != ObjectClass) fun.symbol.tpe.resultType + else if (isFullyDefined(pt)) pt + else ObjectTpe clone.modifyInfo(mt => copyMethodType(mt, cloneParams, resultType)) val fun1 = fun.setSymbol(clone).setType(clone.info) doTypedApply(tree, fun1, args1, mode, resultType).setType(resultType) @@ -4202,9 +4207,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def isCapturedExistential(sym: Symbol) = (sym hasAllFlags EXISTENTIAL | CAPTURED) && { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(isReferencedNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(isReferencedNanos) else null try !isReferencedFrom(context, sym) - finally if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(isReferencedNanos, start) + finally if (settings.areStatisticsEnabled) statistics.stopTimer(isReferencedNanos, start) } def packCaptured(tpe: Type): Type = { @@ -4718,7 +4723,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway // in the special (though common) case where the types are equal, it pays to pack before comparing - // especially virtpatmat needs more aggressive unification of skolemized types + // especially virtpatmat needed more aggressive unification of skolemized types // this breaks src/library/scala/collection/immutable/TrieIterator.scala (which as of 2.13 doesn't actually exist anymore) // annotated types need to be lubbed regardless (at least, continuations break if you bypass them like this) def samePackedTypes = ( @@ -4743,8 +4748,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } - // When there's a suitable __match in scope, virtualize the pattern match - // otherwise, type the Match and leave it until phase `patmat` (immediately after typer) + // Type the Match and leave it until phase `patmat` // empty-selector matches are transformed into synthetic PartialFunction implementations when the expected type demands it def typedVirtualizedMatch(tree: Match): Tree = { val selector = tree.selector @@ -4928,10 +4932,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * insert an implicit conversion. */ def tryTypedApply(fun: Tree, args: List[Tree]): Tree = { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(failedApplyNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.startTimer(failedApplyNanos) else null def onError(typeErrors: Seq[AbsTypeError], warnings: Seq[(Position, String, WarningCategory, Symbol)]): Tree = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(failedApplyNanos, start) + if (settings.areStatisticsEnabled) statistics.stopTimer(failedApplyNanos, start) // If the problem is with raw types, convert to existentials and try again. // See #4712 for a case where this situation arises, @@ -5012,8 +5016,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // TODO: replace `fun.symbol.isStable` by `treeInfo.isStableIdentifierPattern(fun)` val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable val funpt = if (mode.inPatternMode) pt else WildcardType - val appStart = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(failedApplyNanos) else null - val opeqStart = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(failedOpEqNanos) else null + val appStart = if (settings.areStatisticsEnabled) statistics.startTimer(failedApplyNanos) else null + val opeqStart = if (settings.areStatisticsEnabled) statistics.startTimer(failedOpEqNanos) else null def isConversionCandidate(qual: Tree, name: Name): Boolean = !mode.inPatternMode && nme.isOpAssignmentName(TermName(name.decode)) && !qual.exists(_.isErroneous) @@ -5043,7 +5047,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case Select(qual, name) if isConversionCandidate(qual, name) => val qual1 = typedQualifier(qual) if (treeInfo.isVariableOrGetter(qual1)) { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(failedOpEqNanos, opeqStart) + if (settings.areStatisticsEnabled) statistics.stopTimer(failedOpEqNanos, opeqStart) val erred = qual1.exists(_.isErroneous) || args.exists(_.isErroneous) if (erred) reportError(error) else { val convo = convertToAssignment(fun, qual1, name, args) @@ -5055,7 +5059,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } } else { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(failedApplyNanos, appStart) + if (settings.areStatisticsEnabled) statistics.stopTimer(failedApplyNanos, appStart) val Apply(Select(qual2, _), args2) = tree: @unchecked val erred = qual2.exists(_.isErroneous) || args2.exists(_.isErroneous) reportError { @@ -5063,7 +5067,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } case _ => - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(failedApplyNanos, appStart) + if (settings.areStatisticsEnabled) statistics.stopTimer(failedApplyNanos, appStart) reportError(error) } val silentResult = silent( @@ -5074,7 +5078,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper silentResult match { case SilentResultValue(fun1) => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(typedApplyCount) + if (settings.areStatisticsEnabled) statistics.incCounter(typedApplyCount) val noSecondTry = ( isPastTyper || context.inSecondTry @@ -5402,7 +5406,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper else UnstableTreeError(qualTyped) typedSelect(tree, qualStableOrError, name) } else { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(typedSelectCount) + if (settings.areStatisticsEnabled) statistics.incCounter(typedSelectCount) val qualTyped = checkDead(context, typedQualifier(qual, mode)) val tree1 = typedSelect(tree, qualTyped, name) @@ -5509,7 +5513,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def typedIdentOrWildcard(tree: Ident) = { val name = tree.name - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(typedIdentCount) + if (settings.areStatisticsEnabled) statistics.incCounter(typedIdentCount) if (!tree.isBackquoted && ((name == nme.WILDCARD && mode.typingPatternNotConstructor) || (name == tpnme.WILDCARD && mode.inTypeMode))) @@ -5611,7 +5615,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper AppliedTypeNoParametersError(tree, tpt1.tpe) } else { //Console.println("\{tpt1}:\{tpt1.symbol}:\{tpt1.symbol.info}") - if (settings.debug) Console.println(s"$tpt1:${tpt1.symbol}:${tpt1.symbol.info}")//debug + if (settings.isDebug) Console.println(s"$tpt1:${tpt1.symbol}:${tpt1.symbol.info}")//debug AppliedTypeWrongNumberOfArgsError(tree, tpt1, tparams) } } @@ -5652,33 +5656,47 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper treeCopy.Star(tree, typed(tree.elem, mode, pt)) setType makeFullyDefined(pt) } - def issueTryWarnings(tree: Try): Try = { - def checkForCatchAll(cdef: CaseDef): Unit = { - def unbound(t: Tree) = t.symbol == null || t.symbol == NoSymbol - def warn(name: Name) = { - val msg = s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning." - context.warning(cdef.pat.pos, msg, WarningCategory.Other) + + def typedTry(tree: Try) = { + def warn(pos: Position, name: Name) = { + val msg = s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning." + context.warning(pos, msg, WarningCategory.Other) + } + def issueTryWarnings(tree: Try): Try = { + def checkForCatchAll(cdef: CaseDef): Unit = { + def unbound(t: Tree) = t.symbol == null || t.symbol == NoSymbol + if (cdef.guard.isEmpty) cdef.pat match { + case Bind(name, i @ Ident(_)) if unbound(i) => warn(cdef.pat.pos, name) + case i @ Ident(name) if unbound(i) => warn(cdef.pat.pos, name) + case _ => + } } - if (cdef.guard.isEmpty) cdef.pat match { - case Bind(name, i @ Ident(_)) if unbound(i) => warn(name) - case i @ Ident(name) if unbound(i) => warn(name) - case _ => + if (!isPastTyper) tree match { + case Try(_, Nil, fin) => + if (fin eq EmptyTree) + context.warning(tree.pos, "A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.", WarningCategory.Other) + case Try(_, catches, _) => + catches foreach checkForCatchAll } + tree } - if (!isPastTyper) tree match { - case Try(_, Nil, fin) => - if (fin eq EmptyTree) - context.warning(tree.pos, "A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.", WarningCategory.Other) - case Try(_, catches, _) => - catches foreach checkForCatchAll - } - tree - } - def typedTry(tree: Try) = { + val Try(block, catches, fin) = tree val block1 = typed(block, pt) - val catches1 = typedCases(catches, ThrowableTpe, pt) + val cases = catches match { + case CaseDef(EmptyTree, EmptyTree, catchExpr) :: Nil => + val e = typed(catchExpr, functionType(List(ThrowableTpe), pt)) + val catcher = + if (isPartialFunctionType(e.tpe)) treeBuilder.makeCatchFromExpr(e) + else { + warn(e.pos, nme.WILDCARD) + treeBuilder.makeCatchFromFunc(e) + } + catcher :: Nil + case _ => catches + } + val catches1 = typedCases(cases, ThrowableTpe, pt) val fin1 = if (fin.isEmpty) fin else typed(fin, UnitTpe) def finish(ownType: Type) = treeCopy.Try(tree, block1, catches1, fin1) setType ownType @@ -5859,11 +5877,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (refTyped.isErrorTyped) setError(tree) else { - // .resultType unwraps NullaryMethodType (accessor of a path) + // .resultType unwraps NullaryMethodType (accessor of a path) // .deconst unwraps the ConstantType to a LiteralType (for literal-based singleton types) - tree setType refTyped.tpe.resultType.deconst if (!treeInfo.admitsTypeSelection(refTyped)) UnstableTreeError(tree) - else tree + else treeCopy.SingletonTypeTree(tree, refTyped).setType(refTyped.tpe.resultType.deconst) } } @@ -5988,9 +6005,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def typed(tree: Tree, mode: Mode, pt: Type): Tree = { lastTreeToTyper = tree - val statsEnabled = StatisticsStatics.areSomeHotStatsEnabled() && statistics.areHotStatsLocallyEnabled - val startByType = if (statsEnabled) statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null - if (statsEnabled) statistics.incCounter(visitsByType, tree.getClass) + val startByType = if (settings.areHotStatisticsEnabled) statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null + if (settings.areHotStatisticsEnabled) statistics.incCounter(visitsByType, tree.getClass) val shouldPrintTyping = printTypings && !phase.erasedTypes && !noPrintTyping(tree) val shouldPopTypingStack = shouldPrintTyping && typingStack.beforeNextTyped(tree, mode, pt, context) @@ -6076,7 +6092,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper throw ex } finally { if (shouldPopTypingStack) typingStack.pop(tree) - if (statsEnabled) statistics.popTimer(byTypeStack, startByType) + if (settings.areHotStatisticsEnabled) statistics.popTimer(byTypeStack, startByType) if (shouldInsertStabilizers) context.pendingStabilizers = savedPendingStabilizer } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala index 8ffa6cbe0b40..95512297b20d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala @@ -29,7 +29,7 @@ trait TypersTracking { def fullSiteString(context: Context): String = { def owner_long_s = ( - if (settings.debug.value) { + if (settings.isDebug) { def flags_s = context.owner.debugFlagString match { case "" => "" case s => " with flags " + inLightMagenta(s) @@ -70,7 +70,7 @@ trait TypersTracking { private def truncAndOneLine(s: String): String = { val s1 = s.replaceAll("\\s+", " ") - if (s1.length < 60 || settings.debug.value) s1 else s1.take(57) + "..." + if (s1.length < 60 || settings.isDebug) s1 else s1.take(57) + "..." } private class Frame(val tree: Tree) { } @@ -160,7 +160,7 @@ trait TypersTracking { // Some trees which are typed with mind-numbing frequency and // which add nothing by being printed. Did () type to Unit? Let's // gamble on yes. - def printingOk(t: Tree) = printTypings && (settings.debug.value || !noPrint(t)) + def printingOk(t: Tree) = printTypings && (settings.isDebug || !noPrint(t)) def noPrintTyping(t: Tree) = (t.tpe ne null) || !printingOk(t) def noPrintAdapt(tree1: Tree, tree2: Tree) = !printingOk(tree1) || ( (tree1.tpe == tree2.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index b63f8c0e7b55..cb6356103af9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -168,7 +168,7 @@ trait Unapplies extends ast.TreeDSL { case _ => nme.unapply } val cparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), unapplyParamName, classType(cdef, tparams), EmptyTree)) - val resultType = if (!currentRun.isScala212) TypeTree() else { // fix for scala/bug#6541 under -Xsource:2.12 + val resultType = { // fix for scala/bug#6541 under -Xsource:2.12 def repeatedToSeq(tp: Tree) = tp match { case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS_NAME), tps) => AppliedTypeTree(gen.rootScalaDot(tpnme.Seq), tps) case _ => tp diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala new file mode 100644 index 000000000000..7c438a2d202d --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainData.scala @@ -0,0 +1,94 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.tools.nsc +package typechecker +package splain + +import scala.util.matching.Regex + +trait SplainData { + self: Analyzer => + + import global._ + + sealed trait ImplicitErrorSpecifics + + object ImplicitErrorSpecifics { + case class NotFound(param: Symbol) extends ImplicitErrorSpecifics + + case class NonconformantBounds( + targs: List[Type], tparams: List[Symbol], originalError: Option[AbsTypeError], + ) extends ImplicitErrorSpecifics + } + + object ImplicitErrors { + var stack: List[Type] = Nil + var errors: List[ImplicitError] = Nil + + def push(error: ImplicitError): Unit = errors ::= error + def nesting: Int = stack.length - 1 + def nested: Boolean = stack.nonEmpty + def removeErrorsFor(tpe: Type): Unit = errors = errors.dropWhile(_.tpe == tpe) + + def startSearch(expectedType: Type): Unit = { + if (settings.Vimplicits) { + if (!nested) errors = List() + stack = expectedType :: stack + } + } + + def finishSearch(success: Boolean, expectedType: Type): Unit = { + if (settings.Vimplicits) { + if (success) removeErrorsFor(expectedType) + stack = stack.drop(1) + } + } + } + + case class ImplicitError(tpe: Type, candidate: Tree, nesting: Int, specifics: ImplicitErrorSpecifics) { + import ImplicitError._ + + override def equals(other: Any) = other match { + case o: ImplicitError => o.tpe.toString == tpe.toString && candidateName(this) == candidateName(o) + case _ => false + } + + override def hashCode = (tpe.toString.##, ImplicitError.candidateName(this).##).## + override def toString = s"ImplicitError(${shortName(tpe.toString)}, ${shortName(candidate.toString)}), $nesting, $specifics)" + } + + object ImplicitError { + def unapplyCandidate(e: ImplicitError): Tree = + e.candidate match { + case TypeApply(fun, _) => fun + case a => a + } + + def candidateName(e: ImplicitError): String = + unapplyCandidate(e) match { + case Select(_, name) => name.toString + case Ident(name) => name.toString + case a => a.toString + } + + val candidateRegex: Regex = """.*\.this\.(.*)""".r + + def cleanCandidate(e: ImplicitError): String = + unapplyCandidate(e).toString match { + case candidateRegex(suf) => suf + case a => a + } + + def shortName(ident: String): String = ident.substring(ident.lastIndexOf(".") + 1) + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainDiagnostics.scala new file mode 100644 index 000000000000..ca0caa642286 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainDiagnostics.scala @@ -0,0 +1,26 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.tools.nsc +package typechecker +package splain + +trait SplainDiagnostics extends splain.SplainFormatting { + self: Analyzer => + + import global._ + + def splainFoundReqMsg(found: Type, req: Type): String = { + if (settings.VtypeDiffs) ";\n" + showFormattedL(formatDiff(found, req, top = true), break = true).indent.joinLines + else "" + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainErrors.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainErrors.scala new file mode 100644 index 000000000000..41a96c5403b3 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainErrors.scala @@ -0,0 +1,64 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.tools.nsc +package typechecker +package splain + +trait SplainErrors { self: Analyzer with SplainFormatting => + import global._ + + def splainPushNotFound(tree: Tree, param: Symbol): Unit = + ImplicitErrors.stack.headOption.foreach { pt => + val specifics = ImplicitErrorSpecifics.NotFound(param) + ImplicitErrors.push(ImplicitError(pt, tree, ImplicitErrors.nesting, specifics)) + } + + def splainPushOrReportNotFound(tree: Tree, param: Symbol, annotationMsg: String): String = + if (settings.Vimplicits) + if (ImplicitErrors.nested) { + splainPushNotFound(tree, param) + "" + } + else pluginsNoImplicitFoundError(param, ImplicitErrors.errors, formatImplicitError(param, ImplicitErrors.errors, annotationMsg)) + else "" + + def splainPushNonconformantBonds( + tpe: Type, + candidate: Tree, + targs: List[Type], + tparams: List[Symbol], + originalError: Option[AbsTypeError], + ): Unit = { + if (settings.Vimplicits) { + val specifics = ImplicitErrorSpecifics.NonconformantBounds(targs, tparams, originalError) + ImplicitErrors.push(ImplicitError(tpe, candidate, ImplicitErrors.nesting, specifics)) + } + } + + def splainPushImplicitSearchFailure(implicitTree: Tree, expectedType: Type, originalError: AbsTypeError): Unit = { + def pushImpFailure(fun: Tree, args: List[Tree]): Unit = { + fun.tpe match { + case PolyType(tparams, restpe) if tparams.nonEmpty && sameLength(tparams, args) => + splainPushNonconformantBonds(expectedType, implicitTree, mapList(args)(_.tpe), tparams, Some(originalError)) + case _ => + } + } + if (settings.Vimplicits) { + implicitTree match { + case TypeApply(fun, args) => pushImpFailure(fun, args) + case Apply(TypeApply(fun, args), _) => pushImpFailure(fun, args) + case _ => + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatData.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatData.scala new file mode 100644 index 000000000000..0b473cdd57ad --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatData.scala @@ -0,0 +1,88 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.tools.nsc +package typechecker +package splain + +import scala.annotation.tailrec + +object Formatted { + @tailrec def comparator(formatted: Formatted): String = formatted match { + case Infix(left, _, _, _) => comparator(left) + case Simple(tpe) => tpe + case Qualified(Nil, tpe) => tpe + case Qualified(path, tpe) => s"${path.mkString}$tpe" + case UnitForm => "()" + case Applied(cons, _) => comparator(cons) + case TupleForm(Nil) => "()" + case TupleForm(h :: _) => comparator(h) + case FunctionForm(Nil, ret, _) => comparator(ret) + case FunctionForm(h :: _, _, _) => comparator(h) + case RefinedForm(Nil, _) => "()" + case RefinedForm(h :: _, _) => comparator(h) + case Diff(l, _) => comparator(l) + case Decl(sym, _) => comparator(sym) + case DeclDiff(sym, _, _) => comparator(sym) + case ByName(tpe) => comparator(tpe) + } + + implicit val Ord: Ordering[Formatted] = (x, y) => Ordering[String].compare(comparator(x), comparator(y)) +} + +sealed trait Formatted { + def length: Int = this match { + case Infix(infix, left, right, top) => infix.length + left.length + right.length + 2 + case Simple(tpe) => tpe.length + case Qualified(path, tpe) => path.map(_.length).sum + path.length + tpe.length + case UnitForm => 4 + case Applied(cons, args) => args.map(_.length).sum + ( args.length - 1) * 2 + cons.length + 2 + case TupleForm(elems) => elems.map(_.length).sum + (elems.length - 1) + 2 + case FunctionForm(args, ret, top) => args.map(_.length).sum + ( args.length - 1) + 2 + ret.length + 4 + case RefinedForm(elems, decls) => elems.map(_.length).sum + (elems.length - 1) * 6 + case Diff(lhs, rhs) => lhs.length + rhs.length + 1 + case Decl(sym, rhs) => sym.length + rhs.length + 8 + case DeclDiff(sym, lhs, rhs) => sym.length + lhs.length + rhs.length + 9 + case ByName(tpe) => tpe.length + 5 + } +} + +case class Infix(infix: Formatted, left: Formatted, right: Formatted, top: Boolean) extends Formatted +case class Simple(tpe: String) extends Formatted +case class Qualified(path: List[String], tpe: String) extends Formatted +case object UnitForm extends Formatted +case class Applied(cons: Formatted, args: List[Formatted]) extends Formatted +case class TupleForm(elems: List[Formatted]) extends Formatted +case class FunctionForm(args: List[Formatted], ret: Formatted, top: Boolean) extends Formatted +case class RefinedForm(elems: List[Formatted], decls: List[Formatted]) extends Formatted +case class Diff(left: Formatted, right: Formatted) extends Formatted +case class Decl(sym: Formatted, rhs: Formatted) extends Formatted +case class DeclDiff(sym: Formatted, left: Formatted, right: Formatted) extends Formatted +case class ByName(tpe: Formatted) extends Formatted + +sealed trait TypeRepr { + def flat: String + def lines: List[String] + def tokenize: String = lines.mkString(" ") + def joinLines: String = lines.mkString("\n") + def indent: TypeRepr +} + +case class BrokenType(lines: List[String]) extends TypeRepr { + def flat = lines.mkString(" ") + def indent = BrokenType(lines.map(" " + _)) +} + +case class FlatType(flat: String) extends TypeRepr { + def lines = List(flat) + def indent = FlatType(s" $flat") +} diff --git a/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala b/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala new file mode 100644 index 000000000000..4665bb0cd67f --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/splain/SplainFormatting.scala @@ -0,0 +1,531 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.tools.nsc +package typechecker +package splain + +import scala.collection.mutable +import scala.reflect.internal.TypeDebugging.AnsiColor._ + +class FormatCache[K, V](cache: mutable.Map[K, V]) { + def apply(k: K, orElse: => V): V = cache.getOrElseUpdate(k, orElse) +} + +object FormatCache { + def apply[K, V]() = new FormatCache[K, V](mutable.Map()) +} + +trait SplainFormatters { + self: Analyzer => + + import global._, definitions._ + + def formatType(tpe: Type, top: Boolean): Formatted + + object Refined { + def unapply(tpe: Type): Option[(List[Type], Scope)] = tpe match { + case RefinedType(parents, decls) => Some((parents, decls)) + case t @ SingleType(_, _) => unapply(t.underlying) + case _ => None + } + } + + trait SpecialFormatter { + def apply[A]( + tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean, + )(rec: (A, Boolean) => Formatted): Option[Formatted] + + def diff(left: Type, right: Type, top: Boolean): Option[Formatted] + } + + object FunctionFormatter extends SpecialFormatter { + def apply[A]( + tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean, + )(rec: (A, Boolean) => Formatted) = { + if (simple.startsWith("Function")) { + val fmtArgs = formattedArgs + val (params, returnt) = fmtArgs.splitAt(fmtArgs.length - 1) + Some(FunctionForm(params, returnt.headOption.getOrElse(UnitForm), top)) + } else None + } + + def diff(left: Type, right: Type, top: Boolean) = None + } + + object TupleFormatter extends SpecialFormatter { + def apply[A]( + tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean + )(rec: (A, Boolean) => Formatted) = { + if (simple.startsWith("Tuple")) Some(TupleForm(formattedArgs)) + else None + } + + def diff(left: Type, right: Type, top: Boolean) = None + } + + object RefinedFormatter extends SpecialFormatter { + object DeclSymbol { + def unapply(sym: Symbol): Option[(Formatted, Formatted)] = + if (sym.hasRawInfo) Some((Simple(sym.simpleName.toString), formatType(sym.rawInfo, true))) + else None + } + + def ignoredTypes: List[Type] = List(typeOf[Object], typeOf[Any], typeOf[AnyRef]) + + def sanitizeParents: List[Type] => List[Type] = { + case List(tpe) => List(tpe) + case tpes => tpes.filter(t => !ignoredTypes.exists(_ =:= t)) + } + + def formatDecl: Symbol => Formatted = { + case DeclSymbol(n, t) => Decl(n, t) + case sym => Simple(sym.toString) + } + + def apply[A]( + tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean, + )(rec: (A, Boolean) => Formatted): Option[Formatted] = tpe match { + case Refined(parents, decls) => + Some(RefinedForm(sanitizeParents(parents).map(formatType(_, top)), decls.toList.map(formatDecl))) + case _ => None + } + + val none: Formatted = Simple("") + + def separate[A](left: List[A], right: List[A]): (List[A], List[A], List[A]) = { + val leftS = Set(left: _*) + val rightS = Set(right: _*) + val common = leftS.intersect(rightS) + val uniqueLeft = leftS -- common + val uniqueRight = rightS -- common + (common.toList, uniqueLeft.toList, uniqueRight.toList) + } + + def matchTypes(left: List[Type], right: List[Type]): List[Formatted] = { + val (common, uniqueLeft, uniqueRight) = separate(left.map(formatType(_, true)), right.map(formatType(_, true))) + val diffs = uniqueLeft.zipAll(uniqueRight, none, none).map { case (l, r) => Diff(l, r) } + common ::: diffs + } + + def filterDecls(syms: List[Symbol]): List[(Formatted, Formatted)] = + syms.collect { case DeclSymbol(sym, rhs) => (sym, rhs) } + + def matchDecls(left: List[Symbol], right: List[Symbol]): List[Formatted] = { + val (common, uniqueLeft, uniqueRight) = separate(filterDecls(left), filterDecls(right)) + val diffs = uniqueLeft + .map(Some(_)) + .zipAll(uniqueRight.map(Some(_)), None, None) + .collect { + case (Some((sym, l)), Some((_, r))) => DeclDiff(sym, l, r) + case (None, Some((sym, r))) => DeclDiff(sym, none, r) + case (Some((sym, l)), None) => DeclDiff(sym, l, none) + } + common.map { case (sym, rhs) => Decl(sym, rhs) } ++ diffs + } + + def diff(left: Type, right: Type, top: Boolean): Option[Formatted] = + (left, right) match { + case (Refined(leftParents, leftDecls), Refined(rightParents, rightDecls)) => + val parents = matchTypes(sanitizeParents(leftParents), sanitizeParents(rightParents)).sorted + val decls = matchDecls(leftDecls.toList, rightDecls.toList).sorted + Some(RefinedForm(parents, decls)) + case _ => None + } + } + + object ByNameFormatter extends SpecialFormatter { + def apply[A]( + tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean, + )(rec: (A, Boolean) => Formatted): Option[Formatted] = tpe match { + case TypeRef(_, ByNameParamClass, List(a)) => Some(ByName(formatType(a, true))) + case _ => None + } + + def diff(left: Type, right: Type, top: Boolean): Option[Formatted] = None + } +} + +trait SplainFormatting extends SplainFormatters { + self: Analyzer => + + import global._ + + val breakInfixLength: Int = 70 + + def dealias(tpe: Type) = + if (isAux(tpe)) tpe + else (tpe match { + case ExistentialType(_, t) => t + case _ => tpe + }).dealias + + def extractArgs(tpe: Type) = tpe match { + case PolyType(params, result) => result.typeArgs.map { + case t if params.contains(t.typeSymbol) => WildcardType + case a => a + } + case t: AliasTypeRef if !isAux(tpe) => + t.betaReduce.typeArgs.map(a => if (a.typeSymbolDirect.isTypeParameter) WildcardType else a) + case _ => tpe.typeArgs + } + + def isRefined(tpe: Type) = tpe.dealias match { + case RefinedType(_, _) => true + case _ => false + } + + def isSymbolic(tpe: Type) = { + val n = tpe.typeConstructor.typeSymbol.name + !isRefined(tpe) && n.encodedName.toString != n.decodedName.toString + } + + def ctorNames(tpe: Type): List[String] = + scala.util.Try(tpe.typeConstructor.toString) + .map(_.split('.').toList) + .getOrElse(List(tpe.toString)) + + def isAux(tpe: Type) = ctorNames(tpe).lastOption.contains("Aux") + + def formatRefinement(sym: Symbol) = { + if (sym.hasRawInfo) s"$sym = ${showType(sym.rawInfo)}" + else sym.toString + } + + def formatAuxSimple(tpe: Type): (List[String], String) = { + val names = ctorNames(tpe) + (names.dropRight(2), ctorNames(tpe).takeRight(2).mkString(".")) + } + + def symbolPath(sym: Symbol): List[String] = + sym + .ownerChain + .takeWhile(sym => sym.isType && !sym.isPackageClass) + .map(_.name.decodedName.toString) + .reverse + + def sanitizePath(path: List[String]): List[String] = + path.takeWhile(_ != "type").filter(!_.contains("$")) + + def pathPrefix: List[String] => String = { + case Nil => "" + case List("") => "" + case a => a.mkString("", ".", ".") + } + + def qualifiedName(path: List[String], name: String): String = s"${pathPrefix(path)}$name" + + def stripModules(path: List[String], name: String): Option[Int] => String = { + case Some(keep) => qualifiedName(path.takeRight(keep), name) + case None => name + } + + case class TypeParts(sym: Symbol, tt: Type) { + def modulePath: List[String] = (tt, sym) match { + case (TypeRef(pre, _, _), _) if !pre.toString.isEmpty => sanitizePath(pre.toString.split("\\.").toList) + case (SingleType(_, _), sym) => symbolPath(sym).dropRight(1) + case (_, _) => Nil + } + + def ownerPath: List[String] = { + val parts = sym.ownerChain.reverse.map(_.name.decodedName.toString) + parts.splitAt(Math.max(0, parts.size - 1))._1 + } + + def shortName: String = tt.safeToString.stripPrefix(tt.prefixString.split('.').dropRight(1).mkString(".") + ".") + } + + def stripType(tpe: Type): (List[String], String) = tpe match { + case tt: SingletonType => + val parts = TypeParts(tt.termSymbol, tt) + parts.modulePath -> parts.shortName + + case tt: RefinedType => + val parts = TypeParts(tt.typeSymbol, tt) + parts.modulePath -> parts.shortName + + case _ => + // TODO: should this also use TypeParts ? + val sym = if (tpe.takesTypeArgs) tpe.typeSymbolDirect else tpe.typeSymbol + val symName = sym.name.decodedName.toString + val parts = TypeParts(sym, tpe) + (parts.modulePath, if (sym.isModuleClass) s"$symName.type" else symName) + } + + def formatNormalSimple(tpe: Type): (List[String], String) = tpe match { + case a @ WildcardType => (Nil, a.toString) + case a => stripType(a) + } + + def formatSimpleType(tpe: Type): (List[String], String) = + if (isAux(tpe)) formatAuxSimple(tpe) + else formatNormalSimple(tpe) + + def indentLine(line: String, n: Int = 1, prefix: String = " ") = (prefix * n) + line + def indent(lines: List[String], n: Int = 1, prefix: String = " ") = lines.map(indentLine(_, n, prefix)) + + /** If the args of an applied type constructor are multiline, + * create separate lines for the constructor name and the closing bracket; + * else return a single line. */ + def showTypeApply(cons: String, args: List[TypeRepr], break: Boolean): TypeRepr = { + val flatArgs = bracket(args.map(_.flat)) + val flat = FlatType(s"$cons$flatArgs") + def brokenArgs = args match { + case head :: tail => tail.foldLeft(head.lines)((z, a) => z ::: "," :: a.lines) + case _ => Nil + } + def broken = BrokenType(s"$cons[" :: indent(brokenArgs) ::: List("]")) + if (break) decideBreak(flat, broken) else flat + } + + def showTuple(args: List[String]) = args match { + case head :: Nil => s"Tuple1[$head]" + case _ => args.mkString("(", ",", ")") + } + + def showFuncParams(args: List[String]) = args match { + case head :: Nil => head + case _ => args.mkString("(", ",", ")") + } + + def showRefined(parents: List[String], decls: List[String]) = { + val p = parents.mkString(" with ") + val d = if (decls.isEmpty) "" else decls.mkString(" {", "; ", "}") + s"$p$d" + } + + def bracket[A](params: List[A]) = params.mkString("[", ", ", "]") + + def formatFunction(args: List[String]) = { + val (params, returnt) = args.splitAt(args.length - 1) + s"${showTuple(params)} => ${showTuple(returnt)}" + } + + def decideBreak(flat: FlatType, broken: => BrokenType): TypeRepr = + if (flat.flat.length > breakInfixLength) broken + else flat + + /** Turn a nested infix type structure into a flat list + * {{{ + * ::[A, ::[B, C]]] => List(A, ::, B, ::, C) + * }}} + */ + def flattenInfix(tpe: Infix): List[Formatted] = { + def step(tpe: Formatted): List[Formatted] = tpe match { + case Infix(infix, left, right, _) => left :: infix :: step(right) + case a => List(a) + } + step(tpe) + } + + /** Break a list produced by [[flattenInfix]] into lines by taking two + * elements at a time, then appending the terminal. + * If the expression's length is smaller than the threshold specified via + * plugin parameter, return a single line. */ + def breakInfix(types: List[Formatted]): TypeRepr = { + val form = types.map(showFormattedL(_, break = true)) + def broken = form.sliding(2, 2).flatMap { + case FlatType(tpe) :: FlatType(infix) :: Nil => List(s"$tpe $infix") + case left :: right :: Nil => left.lines ++ right.lines + case last :: Nil => last.lines + case _ => Nil + }.toList + decideBreak(FlatType(form.flatMap(_.lines).mkString(" ")), BrokenType(broken)) + } + + val showFormattedLCache = FormatCache[(Formatted, Boolean), TypeRepr]() + val formatTypeCache = FormatCache[(Type, Boolean), Formatted]() + val formatDiffCache = FormatCache[(Type, Type, Boolean), Formatted]() + + val specialFormatters: List[SpecialFormatter] = + List(FunctionFormatter, TupleFormatter, RefinedFormatter, ByNameFormatter) + + def truncateDecls(decls: List[Formatted]): Boolean = settings.VimplicitsMaxRefined.value < decls.map(_.length).sum + + def formattedDiff(left: Formatted, right: Formatted): String = (left, right) match { + case (Qualified(lpath, lname), Qualified(rpath, rname)) if lname == rname => + val prefix = lpath.reverseIterator.zip(rpath.reverseIterator).takeWhile { case (l, r) => l == r }.size + 1 + s"${qualifiedName(lpath.takeRight(prefix), lname).red}|${qualifiedName(rpath.takeRight(prefix), rname).green}" + case (left, right) => + val l = showFormatted(left) + val r = showFormatted(right) + s"${l.red}|${r.green}" + } + + def showFormattedLImpl(tpe: Formatted, break: Boolean): TypeRepr = tpe match { + case Simple(name) => FlatType(name) + case Qualified(_, name) => FlatType(name) + case Applied(cons, args) => showTypeApply(showFormatted(cons), args.map(showFormattedL(_, break)), break) + case tpe @ Infix(_, _, _, top) => wrapParensRepr(if (break) breakInfix(flattenInfix(tpe)) else FlatType(flattenInfix(tpe).map(showFormatted).mkString(" ")), top) + case UnitForm => FlatType("Unit") + case FunctionForm(args, ret, top) => FlatType(wrapParens(s"${showFuncParams(args.map(showFormatted))} => ${showFormatted(ret)}", top)) + case TupleForm(elems) => FlatType(showTuple(elems.map(showFormatted))) + case RefinedForm(elems, decls) => FlatType(showRefined(elems.map(showFormatted), if (truncateDecls(decls)) List("...") else decls.map(showFormatted))) + case Diff(left, right) => FlatType(formattedDiff(left, right)) + case Decl(sym, rhs) => FlatType(s"type ${showFormatted(sym)} = ${showFormatted(rhs)}") + case DeclDiff(sym, left, right) => FlatType(s"type ${showFormatted(sym)} = ${formattedDiff(left, right)}") + case ByName(tpe) => FlatType(s"(=> ${showFormatted(tpe)})") + } + + def showFormattedL(tpe: Formatted, break: Boolean): TypeRepr = showFormattedLCache((tpe, break), showFormattedLImpl(tpe, break)) + def showFormatted(tpe: Formatted): String = showFormattedL(tpe, break = false).tokenize + def showType(tpe: Type): String = showFormattedL(formatType(tpe, top = true), break = false).joinLines + def showTypeBreakL(tpe: Type): List[String] = showFormattedL(formatType(tpe, top = true), break = true).lines + + def wrapParens(expr: String, top: Boolean): String = if (top) expr else s"($expr)" + + def wrapParensRepr(tpe: TypeRepr, top: Boolean): TypeRepr = tpe match { + case FlatType(tpe) => FlatType(wrapParens(tpe, top)) + case BrokenType(lines) => if (top) tpe else BrokenType("(" :: indent(lines) ::: List(")")) + } + + def formatSpecial[A]( + tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean, + )(rec: (A, Boolean) => Formatted): Option[Formatted] = + specialFormatters.iterator.map(_.apply(tpe, simple, args, formattedArgs, top)(rec)).collectFirst { case Some(a) => a } + + def formatInfix[A]( + path: List[String], simple: String, left: A, right: A, top: Boolean, + )(rec: (A, Boolean) => Formatted): Formatted = + Infix(Qualified(path, simple), rec(left, false), rec(right, false), top) + + def formatWithInfix[A](tpe: Type, args: List[A], top: Boolean)(rec: (A, Boolean) => Formatted): Formatted = { + val (path, simple) = formatSimpleType(tpe) + lazy val formattedArgs = args.map(rec(_, true)) + formatSpecial(tpe, simple, args, formattedArgs, top)(rec).getOrElse { + args match { + case left :: right :: Nil if isSymbolic(tpe) => formatInfix(path, simple, left, right, top)(rec) + case _ :: _ => Applied(Qualified(path, simple), formattedArgs) + case _ => Qualified(path, simple) + } + } + } + + def formatTypeImpl(tpe: Type, top: Boolean): Formatted = { + val dtpe = dealias(tpe) + formatWithInfix(dtpe, extractArgs(dtpe), top)(formatType) + } + + def formatType(tpe: Type, top: Boolean): Formatted = formatTypeCache((tpe, top), formatTypeImpl(tpe, top)) + + def formatDiffInfix(left: Type, right: Type, top: Boolean): Formatted = + formatWithInfix(left, extractArgs(left).zip(extractArgs(right)), top) { case ((l, r), t) => formatDiff(l, r, t) } + + def formatDiffSpecial(left: Type, right: Type, top: Boolean): Option[Formatted] = + specialFormatters.iterator.map(_.diff(left, right, top)).collectFirst { case Some(a) => a } + + def formatDiffSimple(left: Type, right: Type): Formatted = + Diff(formatType(left, true), formatType(right, true)) + + def formatDiffImpl(found: Type, req: Type, top: Boolean): Formatted = { + val (left, right) = dealias(found) -> dealias(req) + if (left =:= right) formatType(left, top) + else if (left.typeSymbol == right.typeSymbol) formatDiffInfix(left, right, top) + else formatDiffSpecial(left, right, top).getOrElse(formatDiffSimple(left, right)) + } + + def formatDiff(left: Type, right: Type, top: Boolean): Formatted = + formatDiffCache((left, right, top), formatDiffImpl(left, right, top)) + + def formatNonConfBounds(err: ImplicitErrorSpecifics.NonconformantBounds): List[String] = { + val params = bracket(err.tparams.map(_.defString)) + val types = bracket(err.targs.map(showType)) + List("nonconformant bounds;", types.red, params.green) + } + + def formatNestedImplicit(err: ImplicitError): (String, List[String], Int) = { + val candidate = ImplicitError.cleanCandidate(err) + val problem = s"${candidate.red} invalid because" + val reason = err.specifics match { + case e: ImplicitErrorSpecifics.NotFound => implicitMessage(e.param, NoImplicitFoundAnnotation(err.candidate, e.param)._2) + case e: ImplicitErrorSpecifics.NonconformantBounds => formatNonConfBounds(e) + } + (problem, reason, err.nesting) + } + + def hideImpError(error: ImplicitError): Boolean = error.specifics match { + case ImplicitErrorSpecifics.NonconformantBounds(_, _, _) => true + case ImplicitErrorSpecifics.NotFound(_) => false + } + + def indentTree(tree: List[(String, List[String], Int)], baseIndent: Int): List[String] = { + val nestings = tree.map(_._3).distinct.sorted + tree.flatMap { case (head, tail, nesting) => + val ind = baseIndent + nestings.indexOf(nesting).abs + indentLine(head, ind, "――") :: indent(tail, ind) + } + } + + def formatIndentTree(chain: List[ImplicitError], baseIndent: Int) = + indentTree(chain.map(formatNestedImplicit), baseIndent) + + def deepestLevel(chain: List[ImplicitError]) = + chain.foldLeft(0)((z, a) => if (a.nesting > z) a.nesting else z) + + def formatImplicitChainTreeCompact(chain: List[ImplicitError]): Option[List[String]] = { + chain.headOption.map { head => + val max = deepestLevel(chain) + val leaves = chain.drop(1).dropWhile(_.nesting < max) + val base = if (head.nesting == 0) 0 else 1 + val (fhh, fht, fhn) = formatNestedImplicit(head) + val spacer = if (leaves.nonEmpty && leaves.length < chain.length) List("⋮".blue) else Nil + val fh = (fhh, fht ++ spacer, fhn) + val ft = leaves.map(formatNestedImplicit) + indentTree(fh :: ft, base) + } + } + + def formatImplicitChainTreeFull(chain: List[ImplicitError]): List[String] = + formatIndentTree(chain, chain.headOption.map(_.nesting).getOrElse(0)) + + def formatImplicitChainFlat(chain: List[ImplicitError]): List[String] = + chain.map(formatNestedImplicit).flatMap { case (h, t, _) => h :: t } + + def formatImplicitChain(chain: List[ImplicitError]): List[String] = { + val compact = if (settings.VimplicitsVerboseTree) None else formatImplicitChainTreeCompact(chain) + compact.getOrElse(formatImplicitChainTreeFull(chain)) + } + + /** Remove duplicates and special cases that should not be shown. + * In some cases, candidates are reported twice, once as `Foo.f` and once as + * `f`. `ImplicitError.equals` checks the simple names for identity, which + * is suboptimal, but works for 99% of cases. + * Special cases are handled in [[hideImpError]] */ + def formatNestedImplicits(errors: List[ImplicitError]) = { + val visible = errors.filterNot(hideImpError) + val chains = splitChains(visible).map(_.distinct).distinct + chains.map(formatImplicitChain).flatMap("" :: _).drop(1) + } + + def implicitMessage(param: Symbol, annotationMsg: String): List[String] = { + val tpe = param.tpe + val msg = if (annotationMsg.isEmpty) Nil else annotationMsg.split("\n").toList.map(_.blue) :+ "" + val head = s"${"!".red}${"I".blue} ${param.name.toString.yellow}:" + val lines = showTypeBreakL(tpe).map(_.green) match { + case single :: Nil => List(s"$head $single") + case l => head :: indent(l) + } + lines ::: indent(msg) + } + + def splitChains(errors: List[ImplicitError]): List[List[ImplicitError]] = { + errors.foldRight(Nil: List[List[ImplicitError]]) { + case (a, chains @ ((chain @ (prev :: _)) :: tail)) => + if (a.nesting > prev.nesting) List(a) :: chains + else (a :: chain) :: tail + case (a, _) => List(List(a)) + } + } + + def formatImplicitError(param: Symbol, errors: List[ImplicitError], annotationMsg: String) = + ("implicit error;" :: implicitMessage(param, annotationMsg) ::: formatNestedImplicits(errors)).mkString("\n") +} diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala index 1630e44d250d..158ba29c88ad 100644 --- a/src/compiler/scala/tools/reflect/FormatInterpolator.scala +++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala @@ -69,7 +69,7 @@ abstract class FormatInterpolator { * 5) "...\${smth}%%" => okay, equivalent to "...\${smth}%s%%" * 6) "...\${smth}[%legalJavaConversion]" => okay* * 7) "...\${smth}[%illegalJavaConversion]" => error - * *Legal according to [[https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html]] + * *Legal according to [[java.util.Formatter]] */ def interpolated(parts: List[Tree], args: List[Tree]) = { val fstring = new StringBuilder diff --git a/src/compiler/scala/tools/reflect/ToolBox.scala b/src/compiler/scala/tools/reflect/ToolBox.scala index a8aaf53b9622..fa77e7341c4c 100644 --- a/src/compiler/scala/tools/reflect/ToolBox.scala +++ b/src/compiler/scala/tools/reflect/ToolBox.scala @@ -84,7 +84,7 @@ trait ToolBox[U <: scala.reflect.api.Universe] { * * If `silent` is false, `ToolBoxError` will be thrown in case of an inference error. * If `silent` is true, the typecheck is silent and will return `EmptyTree` if an error occurs. - * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. + * Such errors don't vanish and can be inspected by turning on -Vimplicits. * Unlike in `typecheck`, `silent` is true by default. */ def inferImplicitValue(pt: u.Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: u.Position = u.NoPosition): u.Tree @@ -98,7 +98,7 @@ trait ToolBox[U <: scala.reflect.api.Universe] { * * If `silent` is false, `ToolBoxError` will be thrown in case of an inference error. * If `silent` is true, the typecheck is silent and will return `EmptyTree` if an error occurs. - * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. + * Such errors don't vanish and can be inspected by turning on -Vimplicits. * Unlike in `typecheck`, `silent` is true by default. */ def inferImplicitView(tree: u.Tree, from: u.Type, to: u.Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: u.Position = u.NoPosition): u.Tree diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 912c27ee6da5..0af5efeed818 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -53,7 +53,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => extends ReflectGlobal(settings, reporter0, toolBoxSelf.classLoader) { import definitions._ - private val trace = scala.tools.nsc.util.trace when settings.debug.value + private val trace = scala.tools.nsc.util.trace when settings.isDebug private var wrapCount = 0 @@ -268,7 +268,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val msym = wrapInPackageAndCompile(mdef.name, mdef) val className = msym.fullName - if (settings.debug) println("generated: "+className) + if (settings.isDebug) println("generated: "+className) def moduleFileName(className: String) = className + "$" val jclazz = jClass.forName(moduleFileName(className), true, classLoader) val jmeth = jclazz.getDeclaredMethods.find(_.getName == wrapperMethodName).get diff --git a/src/compiler/scala/tools/tasty/TastyFlags.scala b/src/compiler/scala/tools/tasty/TastyFlags.scala index 0041a3e3f632..f4e66b066c50 100644 --- a/src/compiler/scala/tools/tasty/TastyFlags.scala +++ b/src/compiler/scala/tools/tasty/TastyFlags.scala @@ -47,8 +47,7 @@ object TastyFlags { final val Deferred = Param.next final val Method = Deferred.next final val Erased = Method.next - final val Internal = Erased.next - final val Inline = Internal.next + final val Inline = Erased.next final val InlineProxy = Inline.next final val Opaque = InlineProxy.next final val Extension = Opaque.next @@ -60,8 +59,7 @@ object TastyFlags { final val Open = Enum.next final val ParamAlias = Open.next final val Infix = ParamAlias.next - - private[TastyFlags] final val maxFlag: Long = ParamAlias.shift + final val Invisible = Infix.next def optFlag(cond: Boolean)(flag: TastyFlagSet): TastyFlagSet = if (cond) flag else EmptyTastyFlags @@ -125,7 +123,6 @@ object TastyFlags { if (is(Deferred)) sb += "Deferred" if (is(Method)) sb += "Method" if (is(Erased)) sb += "Erased" - if (is(Internal)) sb += "Internal" if (is(Inline)) sb += "Inline" if (is(InlineProxy)) sb += "InlineProxy" if (is(Opaque)) sb += "Opaque" @@ -138,24 +135,10 @@ object TastyFlags { if (is(Open)) sb += "Open" if (is(ParamAlias)) sb += "ParamAlias" if (is(Infix)) sb += "Infix" + if (is(Invisible)) sb += "Invisible" sb.mkString(" | ") } } } - case class SingletonSets(val toLong: Long) extends AnyVal { - def map[A](f: TastyFlagSet => A): Iterable[A] = { - val buf = Iterable.newBuilder[A] - val orig = TastyFlagSet(toLong) - var flag = EmptyTastyFlags - while (flag.shift <= maxFlag) { - flag = flag.next - if (orig.is(flag)) { - buf += f(flag) - } - } - buf.result() - } - } - } diff --git a/src/compiler/scala/tools/tasty/TastyFormat.scala b/src/compiler/scala/tools/tasty/TastyFormat.scala index 7aae96aebc15..858579cf8ac2 100644 --- a/src/compiler/scala/tools/tasty/TastyFormat.scala +++ b/src/compiler/scala/tools/tasty/TastyFormat.scala @@ -25,7 +25,7 @@ object TastyFormat { /**Natural number. Each increment of the `MajorVersion` begins a * new series of backward compatible TASTy versions. * - * A TASTy file in either the preceeding or succeeding series is + * A TASTy file in either the preceding or succeeding series is * incompatible with the current value. */ final val MajorVersion: Int = 28 @@ -33,7 +33,7 @@ object TastyFormat { /**Natural number. Each increment of the `MinorVersion`, within * a series declared by the `MajorVersion`, breaks forward * compatibility, but remains backwards compatible, with all - * preceeding `MinorVersion`. + * preceding `MinorVersion`. */ final val MinorVersion: Int = 0 @@ -51,14 +51,31 @@ object TastyFormat { * is able to read final TASTy documents if the file's * `MinorVersion` is strictly less than the current value. */ - final val ExperimentalVersion: Int = 1 + final val ExperimentalVersion: Int = 0 /**This method implements a binary relation (`<:<`) between two TASTy versions. + * * We label the lhs `file` and rhs `compiler`. * if `file <:< compiler` then the TASTy file is valid to be read. * - * TASTy versions have a partial order, - * for example `a <:< b` and `b <:< a` are both false if `a` and `b` have different major versions. + * A TASTy version, e.g. `v := 28.0-3` is composed of three fields: + * - v.major == 28 + * - v.minor == 0 + * - v.experimental == 3 + * + * TASTy versions have a partial order, for example, + * `a <:< b` and `b <:< a` are both false if + * - `a` and `b` have different `major` fields. + * - `a` and `b` have the same `major` & `minor` fields, + * but different `experimental` fields, both non-zero. + * + * A TASTy version with a zero value for its `experimental` field + * is considered to be stable. Files with a stable TASTy version + * can be read by a compiler with an unstable TASTy version, + * (where the compiler's TASTy version has a higher `minor` field). + * + * A compiler with a stable TASTy version can never read a file + * with an unstable TASTy version. * * We follow the given algorithm: * ``` @@ -187,7 +204,6 @@ object TastyFormat { final val TRUEconst = 4 final val NULLconst = 5 final val PRIVATE = 6 - final val INTERNAL = 7 final val PROTECTED = 8 final val ABSTRACT = 9 final val FINAL = 10 @@ -223,8 +239,9 @@ object TastyFormat { final val PARAMalias = 41 final val TRANSPARENT = 42 final val INFIX = 43 - final val EMPTYCLAUSE = 44 - final val SPLITCLAUSE = 45 + final val INVISIBLE = 44 + final val EMPTYCLAUSE = 45 + final val SPLITCLAUSE = 46 // Cat. 2: tag Nat @@ -351,7 +368,6 @@ object TastyFormat { def isModifierTag(tag: Int): Boolean = tag match { case PRIVATE - | INTERNAL | PROTECTED | ABSTRACT | FINAL @@ -387,6 +403,7 @@ object TastyFormat { | PARAMalias | EXPORTED | OPEN + | INVISIBLE | ANNOTATION | PRIVATEqualified | PROTECTEDqualified => true @@ -414,7 +431,6 @@ object TastyFormat { case TRUEconst => "TRUEconst" case NULLconst => "NULLconst" case PRIVATE => "PRIVATE" - case INTERNAL => "INTERNAL" case PROTECTED => "PROTECTED" case ABSTRACT => "ABSTRACT" case FINAL => "FINAL" @@ -449,6 +465,7 @@ object TastyFormat { case PARAMsetter => "PARAMsetter" case EXPORTED => "EXPORTED" case OPEN => "OPEN" + case INVISIBLE => "INVISIBLE" case PARAMalias => "PARAMalias" case EMPTYCLAUSE => "EMPTYCLAUSE" case SPLITCLAUSE => "SPLITCLAUSE" diff --git a/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala b/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala index 783fc41bb5c5..546cdc15e23c 100644 --- a/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala +++ b/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala @@ -69,8 +69,6 @@ class TastyHeaderUnpickler(reader: TastyReader) { } } - def isAtEnd: Boolean = reader.isAtEnd - private def check(cond: Boolean, msg: => String): Unit = { if (!cond) throw new UnpickleException(msg) } diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE index b545ddd3f227..797c804d9fcf 100644 --- a/src/intellij/scala.ipr.SAMPLE +++ b/src/intellij/scala.ipr.SAMPLE @@ -325,13 +325,13 @@ - + - + - - + + @@ -355,7 +355,7 @@ - + @@ -369,8 +369,8 @@ - - + + @@ -382,13 +382,13 @@ - + - + @@ -402,16 +402,16 @@ - + - + - + - + @@ -435,18 +435,18 @@ - - + + - + - + - + diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 3f3bff6c8482..00743ffb8f7a 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -655,6 +655,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") private def parseAndEnter(unit: RichCompilationUnit): Unit = if (unit.status == NotLoaded) { debugLog("parsing: "+unit) + runReporting.clearSuppressionsComplete(unit.source) currentTyperRun.compileLate(unit) if (debugIDE && !reporter.hasErrors) validatePositions(unit.body) if (!unit.isJava) syncTopLevelSyms(unit) @@ -1196,54 +1197,36 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") override def positionDelta = 0 override def forImport: Boolean = false } - private val CamelRegex = "([A-Z][^A-Z]*)".r - private def camelComponents(s: String, allowSnake: Boolean): List[String] = { - if (allowSnake && s.forall(c => c.isUpper || c == '_')) s.split('_').toList.filterNot(_.isEmpty) - else CamelRegex.findAllIn("X" + s).toList match { case head :: tail => head.drop(1) :: tail; case Nil => Nil } - } - def camelMatch(entered: Name): Name => Boolean = { - val enteredS = entered.toString - val enteredLowercaseSet = enteredS.toLowerCase().toSet - val allowSnake = !enteredS.contains('_') - - { - candidate: Name => - def candidateChunks = camelComponents(candidate.dropLocal.toString, allowSnake) - // Loosely based on IntelliJ's autocompletion: the user can just write everything in - // lowercase, as we'll let `isl` match `GenIndexedSeqLike` or `isLovely`. - def lenientMatch(entered: String, candidate: List[String], matchCount: Int): Boolean = { - candidate match { - case Nil => entered.isEmpty && matchCount > 0 - case head :: tail => - val enteredAlternatives = Set(entered, entered.capitalize) - val n = head.toIterable.lazyZip(entered).count {case (c, e) => c == e || (c.isUpper && c == e.toUpper)} - head.take(n).inits.exists(init => - enteredAlternatives.exists(entered => - lenientMatch(entered.stripPrefix(init), tail, matchCount + (if (init.isEmpty) 0 else 1)) - ) - ) - } - } - val containsAllEnteredChars = { - // Trying to rule out some candidates quickly before the more expensive `lenientMatch` - val candidateLowercaseSet = candidate.toString.toLowerCase().toSet - enteredLowercaseSet.diff(candidateLowercaseSet).isEmpty - } - containsAllEnteredChars && lenientMatch(enteredS, candidateChunks, 0) - } - } } final def completionsAt(pos: Position): CompletionResult = { val focus1: Tree = typedTreeAt(pos) def typeCompletions(tree: Tree, qual: Tree, nameStart: Int, name: Name): CompletionResult = { val qualPos = qual.pos - val allTypeMembers = typeMembers(qualPos).last + val saved = tree.tpe + // Force `typeMembers` to complete via the prefix, not the type of the Select itself. + tree.setType(ErrorType) + val allTypeMembers = try { + typeMembers(qualPos).last + } finally { + tree.setType(saved) + } val positionDelta: Int = pos.start - nameStart val subName: Name = name.newName(new String(pos.source.content, nameStart, pos.start - nameStart)).encodedName CompletionResult.TypeMembers(positionDelta, qual, tree, allTypeMembers, subName) } focus1 match { + case Apply(Select(qual, name), _) if qual.hasAttachment[InterpolatedString.type] => + // This special case makes CompletionTest.incompleteStringInterpolation work. + // In incomplete code, the parser treats `foo""` as a nested string interpolation, even + // though it is likely that the user wanted to complete `fooBar` before adding the closing brace. + // val fooBar = 42; s"abc ${foo" + // + // TODO: We could also complete the selection here to expand `ra"..."` to `raw"..."`. + val allMembers = scopeMembers(pos) + val positionDelta: Int = pos.start - focus1.pos.start + val subName = name.subName(0, positionDelta) + CompletionResult.ScopeMembers(positionDelta, allMembers, subName, forImport = false) case imp@Import(i @ Ident(name), head :: Nil) if head.name == nme.ERROR => val allMembers = scopeMembers(pos) val nameStart = i.pos.start @@ -1258,9 +1241,13 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } case sel@Select(qual, name) => val qualPos = qual.pos - def fallback = qualPos.end + 2 + val effectiveQualEnd = if (qualPos.isRange) qualPos.end else qualPos.point - 1 + def fallback = { + effectiveQualEnd + 2 + } val source = pos.source - val nameStart: Int = (focus1.pos.end - 1 to qualPos.end by -1).find(p => + + val nameStart: Int = (focus1.pos.end - 1 to effectiveQualEnd by -1).find(p => source.identifier(source.position(p)).exists(_.length == 0) ).map(_ + 1).getOrElse(fallback) typeCompletions(sel, qual, nameStart, name) diff --git a/src/interactive/scala/tools/nsc/interactive/REPL.scala b/src/interactive/scala/tools/nsc/interactive/REPL.scala index 8fb23516e734..300cf38b3ad0 100644 --- a/src/interactive/scala/tools/nsc/interactive/REPL.scala +++ b/src/interactive/scala/tools/nsc/interactive/REPL.scala @@ -57,7 +57,7 @@ object REPL { } } catch { case ex @ FatalError(msg) => - if (true || command.settings.debug) // !!! + if (true || command.settings.isDebug) // !!! ex.printStackTrace() reporter.error(null, "fatal error: " + msg) } diff --git a/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala b/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala index 41e4be51afcb..e1f201533148 100644 --- a/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala +++ b/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala @@ -113,10 +113,8 @@ private[tests] trait CoreTestDefs reporter.println("\naskHyperlinkPos for `" + tree.symbol.name + "` at " + format(pos) + " " + pos.source.file.name) val r = new Response[Position] val sourceFile = tree.symbol.sourceFile - // `tree.symbol.sourceFile` was discovered to be null when testing using virtpatmat on the akka presentation test, where a position had shifted to point to `Int` - // askHyperlinkPos for `Int` at (73,19) pi.scala --> class Int in package scala has null sourceFile! - val treePath = if (sourceFile ne null) sourceFile.path else null - val treeName = if (sourceFile ne null) sourceFile.name else null + val treePath = sourceFile.path + val treeName = sourceFile.name sourceFiles.find(_.path == treePath) match { case Some(source) => diff --git a/src/library/scala/Array.scala b/src/library/scala/Array.scala index 1fb94b527097..14cdabd38555 100644 --- a/src/library/scala/Array.scala +++ b/src/library/scala/Array.scala @@ -17,8 +17,9 @@ import scala.collection.{Factory, immutable, mutable} import mutable.ArrayBuilder import immutable.ArraySeq import scala.language.implicitConversions -import scala.reflect.ClassTag +import scala.reflect.{ClassTag, classTag} import scala.runtime.BoxedUnit +import scala.runtime.ScalaRunTime import scala.runtime.ScalaRunTime.{array_apply, array_update} /** Utility methods for operating on arrays. @@ -182,13 +183,23 @@ object Array { // Subject to a compiler optimization in Cleanup. // Array(e0, ..., en) is translated to { val a = new Array(3); a(i) = ei; a } def apply[T: ClassTag](xs: T*): Array[T] = { - val array = new Array[T](xs.length) - val iterator = xs.iterator - var i = 0 - while (iterator.hasNext) { - array(i) = iterator.next(); i += 1 + val len = xs.length + xs match { + case wa: immutable.ArraySeq[_] if wa.unsafeArray.getClass.getComponentType == classTag[T].runtimeClass => + // We get here in test/files/run/sd760a.scala, `Array[T](t)` for + // a specialized type parameter `T`. While we still pay for two + // copies of the array it is better than before when we also boxed + // each element when populating the result. + ScalaRunTime.array_clone(wa.unsafeArray).asInstanceOf[Array[T]] + case _ => + val array = new Array[T](len) + val iterator = xs.iterator + var i = 0 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array } - array } /** Creates an array of `Boolean` objects */ diff --git a/src/library/scala/Enumeration.scala b/src/library/scala/Enumeration.scala index b99a2311810b..7b6d77827e72 100644 --- a/src/library/scala/Enumeration.scala +++ b/src/library/scala/Enumeration.scala @@ -58,22 +58,22 @@ import scala.util.matching.Regex * @example {{{ * // Example of adding attributes to an enumeration by extending the Enumeration.Val class * object Planet extends Enumeration { - * protected case class Val(mass: Double, radius: Double) extends super.Val { + * protected case class PlanetVal(mass: Double, radius: Double) extends super.Val { * def surfaceGravity: Double = Planet.G * mass / (radius * radius) * def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity * } * import scala.language.implicitConversions - * implicit def valueToPlanetVal(x: Value): Val = x.asInstanceOf[Val] + * implicit def valueToPlanetVal(x: Value): PlanetVal = x.asInstanceOf[PlanetVal] * * val G: Double = 6.67300E-11 - * val Mercury = Val(3.303e+23, 2.4397e6) - * val Venus = Val(4.869e+24, 6.0518e6) - * val Earth = Val(5.976e+24, 6.37814e6) - * val Mars = Val(6.421e+23, 3.3972e6) - * val Jupiter = Val(1.9e+27, 7.1492e7) - * val Saturn = Val(5.688e+26, 6.0268e7) - * val Uranus = Val(8.686e+25, 2.5559e7) - * val Neptune = Val(1.024e+26, 2.4746e7) + * val Mercury = PlanetVal(3.303e+23, 2.4397e6) + * val Venus = PlanetVal(4.869e+24, 6.0518e6) + * val Earth = PlanetVal(5.976e+24, 6.37814e6) + * val Mars = PlanetVal(6.421e+23, 3.3972e6) + * val Jupiter = PlanetVal(1.9e+27, 7.1492e7) + * val Saturn = PlanetVal(5.688e+26, 6.0268e7) + * val Uranus = PlanetVal(8.686e+25, 2.5559e7) + * val Neptune = PlanetVal(1.024e+26, 2.4746e7) * } * * println(Planet.values.filter(_.radius > 7.0e6)) diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala index 6577d5d8e403..fa46286d494a 100644 --- a/src/library/scala/Predef.scala +++ b/src/library/scala/Predef.scala @@ -137,9 +137,8 @@ object Predef extends LowPriorityImplicits { @inline def valueOf[T](implicit vt: ValueOf[T]): T = vt.value /** The `String` type in Scala has all the methods of the underlying - * `java.lang.String`, of which it is just an alias. - * (See the documentation corresponding to your Java version, - * for example [[https://docs.oracle.com/javase/8/docs/api/java/lang/String.html]].) + * [[java.lang.String]], of which it is just an alias. + * * In addition, extension methods in [[scala.collection.StringOps]] * are added implicitly through the conversion [[augmentString]]. * @group aliases diff --git a/src/library/scala/SerialVersionUID.scala b/src/library/scala/SerialVersionUID.scala index e92e0d9fbd78..7a0b08f6fa23 100644 --- a/src/library/scala/SerialVersionUID.scala +++ b/src/library/scala/SerialVersionUID.scala @@ -20,7 +20,7 @@ package scala * which the JVM's serialization mechanism uses to determine serialization * compatibility between different versions of a class. * - * @see [[https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html `java.io.Serializable`]] + * @see [[java.io.Serializable]] * @see [[Serializable]] */ @deprecatedInheritance("Scheduled for being final in the future", "2.13.0") diff --git a/src/library/scala/annotation/elidable.scala b/src/library/scala/annotation/elidable.scala index 7f8db33d9c4b..9d15449fac18 100644 --- a/src/library/scala/annotation/elidable.scala +++ b/src/library/scala/annotation/elidable.scala @@ -76,7 +76,7 @@ package scala.annotation * } * }}} */ -final class elidable(final val level: Int) extends scala.annotation.StaticAnnotation +final class elidable(final val level: Int) extends scala.annotation.ConstantAnnotation /** This useless appearing code was necessary to allow people to use * named constants for the elidable annotation. This is what it takes diff --git a/src/library/scala/annotation/implicitAmbiguous.scala b/src/library/scala/annotation/implicitAmbiguous.scala index dbe8d2ab936d..87788588c5af 100644 --- a/src/library/scala/annotation/implicitAmbiguous.scala +++ b/src/library/scala/annotation/implicitAmbiguous.scala @@ -39,4 +39,4 @@ package scala.annotation * }}} */ @meta.getter -final class implicitAmbiguous(msg: String) extends scala.annotation.StaticAnnotation +final class implicitAmbiguous(msg: String) extends scala.annotation.ConstantAnnotation diff --git a/src/library/scala/annotation/implicitNotFound.scala b/src/library/scala/annotation/implicitNotFound.scala index e3833bcd428b..9eba5c2c9f3a 100644 --- a/src/library/scala/annotation/implicitNotFound.scala +++ b/src/library/scala/annotation/implicitNotFound.scala @@ -53,4 +53,4 @@ package scala.annotation * ^ * */ -final class implicitNotFound(msg: String) extends scala.annotation.StaticAnnotation {} +final class implicitNotFound(msg: String) extends scala.annotation.ConstantAnnotation diff --git a/src/library/scala/annotation/migration.scala b/src/library/scala/annotation/migration.scala index 99e6dc253bbc..37b2a9edfda0 100644 --- a/src/library/scala/annotation/migration.scala +++ b/src/library/scala/annotation/migration.scala @@ -27,4 +27,4 @@ package scala.annotation * @param changedIn The version, in which the behaviour change was * introduced. */ -private[scala] final class migration(message: String, changedIn: String) extends scala.annotation.StaticAnnotation +private[scala] final class migration(message: String, changedIn: String) extends scala.annotation.ConstantAnnotation diff --git a/src/library/scala/collection/ArrayOps.scala b/src/library/scala/collection/ArrayOps.scala index 370acfce2f1a..aec8156599be 100644 --- a/src/library/scala/collection/ArrayOps.scala +++ b/src/library/scala/collection/ArrayOps.scala @@ -1569,18 +1569,18 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * ''n'' times in `that`, then the first ''n'' occurrences of `x` will not form * part of the result, but any following occurrences will. */ - def diff[B >: A](that: Seq[B]): Array[A] = mutable.ArraySeq.make(xs).diff(that).array.asInstanceOf[Array[A]] + def diff[B >: A](that: Seq[B]): Array[A] = mutable.ArraySeq.make(xs).diff(that).toArray[A] /** Computes the multiset intersection between this array and another sequence. - * - * @param that the sequence of elements to intersect with. - * @return a new array which contains all elements of this array - * which also appear in `that`. - * If an element value `x` appears - * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained - * in the result, but any following occurrences will be omitted. - */ - def intersect[B >: A](that: Seq[B]): Array[A] = mutable.ArraySeq.make(xs).intersect(that).array.asInstanceOf[Array[A]] + * + * @param that the sequence of elements to intersect with. + * @return a new array which contains all elements of this array + * which also appear in `that`. + * If an element value `x` appears + * ''n'' times in `that`, then the first ''n'' occurrences of `x` will be retained + * in the result, but any following occurrences will be omitted. + */ + def intersect[B >: A](that: Seq[B]): Array[A] = mutable.ArraySeq.make(xs).intersect(that).toArray[A] /** Groups elements in fixed size blocks by passing a "sliding window" * over them (as opposed to partitioning them, as is done in grouped.) @@ -1592,7 +1592,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * last element (which may be the only element) will be truncated * if there are fewer than `size` elements remaining to be grouped. */ - def sliding(size: Int, step: Int = 1): Iterator[Array[A]] = mutable.ArraySeq.make(xs).sliding(size, step).map(_.array.asInstanceOf[Array[A]]) + def sliding(size: Int, step: Int = 1): Iterator[Array[A]] = mutable.ArraySeq.make(xs).sliding(size, step).map(_.toArray[A]) /** Iterates over combinations. A _combination_ of length `n` is a subsequence of * the original array, with the elements taken in order. Thus, `Array("x", "y")` and `Array("y", "y")` @@ -1609,7 +1609,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * Array("a", "b", "b", "b", "c").combinations(2) == Iterator(Array(a, b), Array(a, c), Array(b, b), Array(b, c)) * }}} */ - def combinations(n: Int): Iterator[Array[A]] = mutable.ArraySeq.make(xs).combinations(n).map(_.array.asInstanceOf[Array[A]]) + def combinations(n: Int): Iterator[Array[A]] = mutable.ArraySeq.make(xs).combinations(n).map(_.toArray[A]) /** Iterates over distinct permutations. * @@ -1618,7 +1618,7 @@ final class ArrayOps[A](private val xs: Array[A]) extends AnyVal { * Array("a", "b", "b").permutations == Iterator(Array(a, b, b), Array(b, a, b), Array(b, b, a)) * }}} */ - def permutations: Iterator[Array[A]] = mutable.ArraySeq.make(xs).permutations.map(_.array.asInstanceOf[Array[A]]) + def permutations: Iterator[Array[A]] = mutable.ArraySeq.make(xs).permutations.map(_.toArray[A]) // we have another overload here, so we need to duplicate this method /** Tests whether this array contains the given sequence at a given index. diff --git a/src/library/scala/collection/Map.scala b/src/library/scala/collection/Map.scala index 5f929fe82e1f..59e1b5db0651 100644 --- a/src/library/scala/collection/Map.scala +++ b/src/library/scala/collection/Map.scala @@ -29,11 +29,44 @@ trait Map[K, +V] def canEqual(that: Any): Boolean = true + /** + * Equality of maps is implemented using the lookup method [[get]]. This method returns `true` if + * - the argument `o` is a `Map`, + * - the two maps have the same [[size]], and + * - for every `(key, value)` pair in this map, `other.get(key) == Some(value)`. + * + * The implementation of `equals` checks the [[canEqual]] method, so subclasses of `Map` can narrow down the equality + * to specific map types. The `Map` implementations in the standard library can all be compared, their `canEqual` + * methods return `true`. + * + * Note: The `equals` method only respects the equality laws (symmetry, transitivity) if the two maps use the same + * key equivalence function in their lookup operation. For example, the key equivalence operation in a + * [[scala.collection.immutable.TreeMap]] is defined by its ordering. Comparing a `TreeMap` with a `HashMap` leads + * to unexpected results if `ordering.equiv(k1, k2)` (used for lookup in `TreeMap`) is different from `k1 == k2` + * (used for lookup in `HashMap`). + * + * {{{ + * scala> import scala.collection.immutable._ + * scala> val ord: Ordering[String] = _ compareToIgnoreCase _ + * + * scala> TreeMap("A" -> 1)(ord) == HashMap("a" -> 1) + * val res0: Boolean = false + * + * scala> HashMap("a" -> 1) == TreeMap("A" -> 1)(ord) + * val res1: Boolean = true + * }}} + * + * + * @param o The map to which this map is compared + * @return `true` if the two maps are equal according to the description + */ override def equals(o: Any): Boolean = (this eq o.asInstanceOf[AnyRef]) || (o match { case map: Map[K, _] if map.canEqual(this) => - (this.size == map.size) && - this.forall(kv => map.getOrElse(kv._1, Map.DefaultSentinelFn()) == kv._2) + (this.size == map.size) && { + try this.forall(kv => map.getOrElse(kv._1, Map.DefaultSentinelFn()) == kv._2) + catch { case _: ClassCastException => false } // PR #9565 / scala/bug#12228 + } case _ => false }) diff --git a/src/library/scala/collection/Seq.scala b/src/library/scala/collection/Seq.scala index 3f98f877413e..19dc0b3377b9 100644 --- a/src/library/scala/collection/Seq.scala +++ b/src/library/scala/collection/Seq.scala @@ -857,12 +857,16 @@ trait SeqOps[+A, +CC[_], +C] extends Any def diff[B >: A](that: Seq[B]): C = { val occ = occCounts(that) fromSpecific(iterator.filter { x => - val ox = occ(x) // Avoid multiple map lookups - if (ox == 0) true - else { - occ(x) = ox - 1 - false + var include = false + occ.updateWith(x) { + case None => { + include = true + None + } + case Some(1) => None + case Some(n) => Some(n - 1) } + include }) } @@ -878,11 +882,16 @@ trait SeqOps[+A, +CC[_], +C] extends Any def intersect[B >: A](that: Seq[B]): C = { val occ = occCounts(that) fromSpecific(iterator.filter { x => - val ox = occ(x) // Avoid multiple map lookups - if (ox > 0) { - occ(x) = ox - 1 - true - } else false + var include = true + occ.updateWith(x) { + case None => { + include = false + None + } + case Some(1) => None + case Some(n) => Some(n - 1) + } + include }) } @@ -920,8 +929,11 @@ trait SeqOps[+A, +CC[_], +C] extends Any } protected[collection] def occCounts[B](sq: Seq[B]): mutable.Map[B, Int] = { - val occ = new mutable.HashMap[B, Int]().withDefaultValue(0) - for (y <- sq) occ(y) += 1 + val occ = new mutable.HashMap[B, Int]() + for (y <- sq) occ.updateWith(y) { + case None => Some(1) + case Some(n) => Some(n + 1) + } occ } diff --git a/src/library/scala/collection/Set.scala b/src/library/scala/collection/Set.scala index 188a96e78326..d35494cd1eb5 100644 --- a/src/library/scala/collection/Set.scala +++ b/src/library/scala/collection/Set.scala @@ -28,10 +28,44 @@ trait Set[A] def canEqual(that: Any) = true + /** + * Equality of sets is implemented using the lookup method [[contains]]. This method returns `true` if + * - the argument `that` is a `Set`, + * - the two sets have the same [[size]], and + * - for every `element` this set, `other.contains(element) == true`. + * + * The implementation of `equals` checks the [[canEqual]] method, so subclasses of `Set` can narrow down the equality + * to specific set types. The `Set` implementations in the standard library can all be compared, their `canEqual` + * methods return `true`. + * + * Note: The `equals` method only respects the equality laws (symmetry, transitivity) if the two sets use the same + * element equivalence function in their lookup operation. For example, the element equivalence operation in a + * [[scala.collection.immutable.TreeSet]] is defined by its ordering. Comparing a `TreeSet` with a `HashSet` leads + * to unexpected results if `ordering.equiv(e1, e2)` (used for lookup in `TreeSet`) is different from `e1 == e2` + * (used for lookup in `HashSet`). + * + * {{{ + * scala> import scala.collection.immutable._ + * scala> val ord: Ordering[String] = _ compareToIgnoreCase _ + * + * scala> TreeSet("A")(ord) == HashSet("a") + * val res0: Boolean = false + * + * scala> HashSet("a") == TreeSet("A")(ord) + * val res1: Boolean = true + * }}} + * + * + * @param that The set to which this set is compared + * @return `true` if the two sets are equal according to the description + */ override def equals(that: Any): Boolean = (this eq that.asInstanceOf[AnyRef]) || (that match { case set: Set[A] if set.canEqual(this) => - (this.size == set.size) && this.subsetOf(set) + (this.size == set.size) && { + try this.subsetOf(set) + catch { case _: ClassCastException => false } // PR #9565 / scala/bug#12228 + } case _ => false }) diff --git a/src/library/scala/collection/SortedMap.scala b/src/library/scala/collection/SortedMap.scala index 570b09a12b3c..29ebc304678c 100644 --- a/src/library/scala/collection/SortedMap.scala +++ b/src/library/scala/collection/SortedMap.scala @@ -30,14 +30,17 @@ trait SortedMap[K, +V] override def equals(that: Any): Boolean = that match { case _ if this eq that.asInstanceOf[AnyRef] => true - case sm: SortedMap[k, v] if sm.ordering == this.ordering => + case sm: SortedMap[K, _] if sm.ordering == this.ordering => (sm canEqual this) && (this.size == sm.size) && { val i1 = this.iterator val i2 = sm.iterator var allEqual = true - while (allEqual && i1.hasNext) - allEqual = i1.next() == i2.next() + while (allEqual && i1.hasNext) { + val kv1 = i1.next() + val kv2 = i2.next() + allEqual = ordering.equiv(kv1._1, kv2._1) && kv1._2 == kv2._2 + } allEqual } case _ => super.equals(that) diff --git a/src/library/scala/collection/SortedSet.scala b/src/library/scala/collection/SortedSet.scala index 77f62dc15e98..6dc3ed6242e6 100644 --- a/src/library/scala/collection/SortedSet.scala +++ b/src/library/scala/collection/SortedSet.scala @@ -29,14 +29,14 @@ trait SortedSet[A] extends Set[A] override def equals(that: Any): Boolean = that match { case _ if this eq that.asInstanceOf[AnyRef] => true - case ss: SortedSet[_] if ss.ordering == this.ordering => + case ss: SortedSet[A] if ss.ordering == this.ordering => (ss canEqual this) && (this.size == ss.size) && { val i1 = this.iterator val i2 = ss.iterator var allEqual = true while (allEqual && i1.hasNext) - allEqual = i1.next() == i2.next() + allEqual = ordering.equiv(i1.next(), i2.next()) allEqual } case _ => diff --git a/src/library/scala/collection/StrictOptimizedSeqOps.scala b/src/library/scala/collection/StrictOptimizedSeqOps.scala index 73f89fa46897..396e53885081 100644 --- a/src/library/scala/collection/StrictOptimizedSeqOps.scala +++ b/src/library/scala/collection/StrictOptimizedSeqOps.scala @@ -75,27 +75,38 @@ trait StrictOptimizedSeqOps [+A, +CC[_], +C] b.result() } - override def diff[B >: A](that: Seq[B]): C = { - val occ = occCounts(that) - val b = newSpecificBuilder - for (x <- this) { - val ox = occ(x) // Avoid multiple map lookups - if (ox == 0) b += x - else occ(x) = ox - 1 + override def diff[B >: A](that: Seq[B]): C = + if (isEmpty || that.isEmpty) coll + else { + val occ = occCounts(that) + val b = newSpecificBuilder + for (x <- this) { + occ.updateWith(x) { + case None => { + b.addOne(x) + None + } + case Some(1) => None + case Some(n) => Some(n - 1) + } + } + b.result() } - b.result() - } - override def intersect[B >: A](that: Seq[B]): C = { - val occ = occCounts(that) - val b = newSpecificBuilder - for (x <- this) { - val ox = occ(x) // Avoid multiple map lookups - if (ox > 0) { - b += x - occ(x) = ox - 1 + override def intersect[B >: A](that: Seq[B]): C = + if (isEmpty || that.isEmpty) empty + else { + val occ = occCounts(that) + val b = newSpecificBuilder + for (x <- this) { + occ.updateWith(x) { + case None => None + case Some(n) => { + b.addOne(x) + if (n == 1) None else Some(n - 1) + } + } } + b.result() } - b.result() - } } diff --git a/src/library/scala/collection/immutable/HashMap.scala b/src/library/scala/collection/immutable/HashMap.scala index 4ba5208aad5a..d59841853476 100644 --- a/src/library/scala/collection/immutable/HashMap.scala +++ b/src/library/scala/collection/immutable/HashMap.scala @@ -254,7 +254,7 @@ final class HashMap[K, +V] private[immutable] (private[immutable] val rootNode: override def equals(that: Any): Boolean = that match { - case map: HashMap[K, V] => (this eq map) || (this.rootNode == map.rootNode) + case map: HashMap[_, _] => (this eq map) || (this.rootNode == map.rootNode) case _ => super.equals(that) } @@ -628,11 +628,11 @@ private final class BitmapIndexedMapNode[K, +V]( if ((dataMap & bitpos) != 0) { val index = indexFrom(dataMap, mask, bitpos) - if (key == getKey(index)) getValue(index) else throw new NoSuchElementException + if (key == getKey(index)) getValue(index) else throw new NoSuchElementException(s"key not found: $key") } else if ((nodeMap & bitpos) != 0) { getNode(indexFrom(nodeMap, mask, bitpos)).apply(key, originalHash, keyHash, shift + BitPartitionSize) } else { - throw new NoSuchElementException + throw new NoSuchElementException(s"key not found: $key") } } diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 67bcb2924fda..1c08da18023b 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -176,7 +176,7 @@ final class HashSet[A] private[immutable](private[immutable] val rootNode: Bitma override def equals(that: Any): Boolean = that match { - case set: HashSet[A] => (this eq set) || (this.rootNode == set.rootNode) + case set: HashSet[_] => (this eq set) || (this.rootNode == set.rootNode) case _ => super.equals(that) } diff --git a/src/library/scala/collection/immutable/LazyList.scala b/src/library/scala/collection/immutable/LazyList.scala index 58ff4a8970a2..db0e9d180b22 100644 --- a/src/library/scala/collection/immutable/LazyList.scala +++ b/src/library/scala/collection/immutable/LazyList.scala @@ -68,7 +68,7 @@ import scala.runtime.Statics * val fibs: LazyList[BigInt] = * BigInt(0) #:: BigInt(1) #:: * fibs.zip(fibs.tail).map{ n => - * println(s"Adding ${n._1} and ${n._2}") + * println(s"Adding \${n._1} and \${n._2}") * n._1 + n._2 * } * fibs.take(5).foreach(println) diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index cc39450f3c95..dc117a0bdb72 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -614,6 +614,39 @@ sealed abstract class List[+A] } } + // TODO: uncomment once bincompat allows (reference: scala/scala#9365) + /* + // Override for performance: traverse only as much as needed + // and share tail when nothing needs to be filtered out anymore + override def diff[B >: A](that: collection.Seq[B]): AnyRef = { + if (that.isEmpty || this.isEmpty) this + else if (tail.isEmpty) if (that.contains(head)) Nil else this + else { + val occ = occCounts(that) + val b = new ListBuffer[A]() + @tailrec + def rec(remainder: List[A]): List[A] = { + if(occ.isEmpty) b.prependToList(remainder) + else remainder match { + case Nil => b.result() + case head :: next => { + occ.updateWith(head){ + case None => { + b.append(head) + None + } + case Some(1) => None + case Some(n) => Some(n - 1) + } + rec(next) + } + } + } + rec(this) + } + } + */ + } // Internal code that mutates `next` _must_ call `Statics.releaseFence()` if either immediately, or diff --git a/src/library/scala/collection/immutable/LongMap.scala b/src/library/scala/collection/immutable/LongMap.scala index aed44f57a966..c418dc7616ac 100644 --- a/src/library/scala/collection/immutable/LongMap.scala +++ b/src/library/scala/collection/immutable/LongMap.scala @@ -63,9 +63,9 @@ object LongMap { private[immutable] case object Nil extends LongMap[Nothing] { // Important, don't remove this! See IntMap for explanation. override def equals(that : Any) = that match { - case (that: AnyRef) if (this eq that) => true - case (that: LongMap[_]) => false // The only empty LongMaps are eq Nil - case that => super.equals(that) + case _: this.type => true + case _: LongMap[_] => false // The only empty LongMaps are eq Nil + case _ => super.equals(that) } } diff --git a/src/library/scala/collection/immutable/Queue.scala b/src/library/scala/collection/immutable/Queue.scala index ae90826cd2bf..9c8a32d95a3e 100644 --- a/src/library/scala/collection/immutable/Queue.scala +++ b/src/library/scala/collection/immutable/Queue.scala @@ -97,6 +97,11 @@ sealed class Queue[+A] protected(protected val in: List[A], protected val out: L else if (in.nonEmpty) new Queue(Nil, in.reverse.tail) else throw new NoSuchElementException("tail on empty queue") + override def last: A = + if (in.nonEmpty) in.head + else if (out.nonEmpty) out.last + else throw new NoSuchElementException("last on empty queue") + /* This is made to avoid inefficient implementation of iterator. */ override def forall(p: A => Boolean): Boolean = in.forall(p) && out.forall(p) diff --git a/src/library/scala/collection/immutable/RedBlackTree.scala b/src/library/scala/collection/immutable/RedBlackTree.scala index bf4cc7e3a0ed..2e7aa7b472ad 100644 --- a/src/library/scala/collection/immutable/RedBlackTree.scala +++ b/src/library/scala/collection/immutable/RedBlackTree.scala @@ -204,19 +204,25 @@ private[collection] object RedBlackTree { def tail[A, B](tree: Tree[A, B]): Tree[A, B] = { def _tail(tree: Tree[A, B]): Tree[A, B] = - if(tree eq null) throw new NoSuchElementException("empty tree") - else if(tree.left eq null) tree.right - else if(isBlackTree(tree.left)) balLeft(tree.key, tree.value, _tail(tree.left), tree.right) - else RedTree(tree.key, tree.value, _tail(tree.left), tree.right) + if (tree eq null) throw new NoSuchElementException("empty tree") + else { + val tl = tree.left + if (tl eq null) tree.right + else if (tl.isBlack) balLeft(tree, _tail(tl), tree.right) + else tree.redWithLeft(_tail(tree.left)) + } blacken(_tail(tree)) } def init[A, B](tree: Tree[A, B]): Tree[A, B] = { def _init(tree: Tree[A, B]): Tree[A, B] = - if(tree eq null) throw new NoSuchElementException("empty tree") - else if(tree.right eq null) tree.left - else if(isBlackTree(tree.right)) balRight(tree.key, tree.value, tree.left, _init(tree.right)) - else RedTree(tree.key, tree.value, tree.left, _init(tree.right)) + if (tree eq null) throw new NoSuchElementException("empty tree") + else { + val tr = tree.right + if (tr eq null) tree.left + else if (tr.isBlack) balRight(tree, tree.left, _init(tr)) + else tree.redWithRight(_init(tr)) + } blacken(_init(tree)) } @@ -299,7 +305,7 @@ private[collection] object RedBlackTree { else tree } - def isBlack(tree: Tree[_, _]) = (tree eq null) || isBlackTree(tree) + def isBlack(tree: Tree[_, _]) = (tree eq null) || tree.isBlack @`inline` private[this] def isRedTree(tree: Tree[_, _]) = (tree ne null) && tree.isRed @`inline` private[this] def isBlackTree(tree: Tree[_, _]) = (tree ne null) && tree.isBlack @@ -311,8 +317,10 @@ private[collection] object RedBlackTree { private[this] def maybeBlacken[A, B](t: Tree[A, B]): Tree[A, B] = if(isBlack(t)) t else if(isRedTree(t.left) || isRedTree(t.right)) t.black else t - private[this] def mkTree[A, B](isBlack: Boolean, k: A, v: B, l: Tree[A, B], r: Tree[A, B]) = - if (isBlack) BlackTree(k, v, l, r) else RedTree(k, v, l, r) + private[this] def mkTree[A, B](isBlack: Boolean, key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = { + val sizeAndColour = sizeOf(left) + sizeOf(right) + 1 | (if(isBlack) initialBlackCount else initialRedCount) + new Tree(key, value.asInstanceOf[AnyRef], left, right, sizeAndColour) + } /** Create a new balanced tree where `newLeft` replaces `tree.left`. */ private[this] def balanceLeft[A, B1](tree: Tree[A, B1], newLeft: Tree[A, B1]): Tree[A, B1] = { @@ -587,7 +595,7 @@ private[collection] object RedBlackTree { } else new Tree(_key, _value, _left, _right, initialBlackCount) } -// private[NewRedBlackTree] def mutableRed: Tree[A, B] = { +// private[RedBlackTree] def mutableRed: Tree[A, B] = { // if (isRed) this // else if (mutable) { // _count = initialRedCount @@ -692,6 +700,15 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialBlackCount | size) } } + private[RedBlackTree] def redWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + if ((newLeft eq _left) && isRed) this + else { + val size = sizeOf(newLeft) + sizeOf(_right) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialRedCount | size) + } + } private[RedBlackTree] def blackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newRight) @@ -701,6 +718,15 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialBlackCount | size) } } + private[RedBlackTree] def redWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + if ((newRight eq _right) && isRed) this + else { + val size = sizeOf(_left) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialRedCount | size) + } + } private[RedBlackTree] def withLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) @@ -711,6 +737,26 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, (_count & colourBit) | size) } } + private[RedBlackTree] def redWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + //assertNotMutable(newRight) + if ((newLeft eq _left) && (newRight eq _right) && isRed) this + else { + val size = sizeOf(newLeft) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, initialRedCount | size) + } + } + private[RedBlackTree] def blackWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + //assertNotMutable(newRight) + if ((newLeft eq _left) && (newRight eq _right) && isBlack) this + else { + val size = sizeOf(newLeft) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, initialBlackCount | size) + } + } } //see #Tree docs "Colour, mutablity and size encoding" //we make these final vals because the optimiser inlines them, without reference to the enclosing module @@ -924,7 +970,7 @@ private[collection] object RedBlackTree { if((v2.asInstanceOf[AnyRef] eq v.asInstanceOf[AnyRef]) && (l2 eq l) && (r2 eq r)) t.asInstanceOf[Tree[A, C]] - else mkTree(isBlackTree(t), k, v2, l2, r2) + else mkTree(t.isBlack, k, v2, l2, r2) } def filterEntries[A, B](t: Tree[A, B], f: (A, B) => Boolean): Tree[A, B] = if(t eq null) null else { @@ -943,156 +989,119 @@ private[collection] object RedBlackTree { blacken(fk(t)) } - def filterKeys[A, B](t: Tree[A, B], f: A => Boolean): Tree[A, B] = if(t eq null) null else { - def fk(t: Tree[A, B]): Tree[A, B] = { - val k = t.key - val l = t.left - val r = t.right - val l2 = if(l eq null) null else fk(l) - val keep = f(k) - val r2 = if(r eq null) null else fk(r) - if(!keep) join2(l2, r2) - else if((l2 eq l) && (r2 eq r)) t - else join(l2, k, t.value, r2) - } - blacken(fk(t)) - } + private[this] val null2 = (null, null) def partitionEntries[A, B](t: Tree[A, B], p: (A, B) => Boolean): (Tree[A, B], Tree[A, B]) = if(t eq null) (null, null) else { - var tmpk, tmpd = null: Tree[A, B] // shared vars to avoid returning tuples from fk - def fk(t: Tree[A, B]): Unit = { - val k = t.key - val v = t.value - val l = t.left - val r = t.right - var l2k, l2d, r2k, r2d = null: Tree[A, B] - if(l ne null) { - fk(l) - l2k = tmpk - l2d = tmpd - } - val keep = p(k, v) - if(r ne null) { - fk(r) - r2k = tmpk - r2d = tmpd + if (t eq null) null2 + else { + object partitioner { + var tmpk, tmpd = null: Tree[A, B] // shared vars to avoid returning tuples from fk + def fk(t: Tree[A, B]): Unit = { + val k = t.key + val v = t.value + val l = t.left + val r = t.right + var l2k, l2d, r2k, r2d = null: Tree[A, B] + if (l ne null) { + fk(l) + l2k = tmpk + l2d = tmpd + } + val keep = p(k, v) + if (r ne null) { + fk(r) + r2k = tmpk + r2d = tmpd + } + val jk = + if (!keep) join2(l2k, r2k) + else if ((l2k eq l) && (r2k eq r)) t + else join(l2k, k, v, r2k) + val jd = + if (keep) join2(l2d, r2d) + else if ((l2d eq l) && (r2d eq r)) t + else join(l2d, k, v, r2d) + tmpk = jk + tmpd = jd + } } - val jk = - if(!keep) join2(l2k, r2k) - else if((l2k eq l) && (r2k eq r)) t - else join(l2k, k, v, r2k) - val jd = - if(keep) join2(l2d, r2d) - else if((l2d eq l) && (r2d eq r)) t - else join(l2d, k, v, r2d) - tmpk = jk - tmpd = jd - } - fk(t) - (blacken(tmpk), blacken(tmpd)) - } - def partitionKeys[A, B](t: Tree[A, B], p: A => Boolean): (Tree[A, B], Tree[A, B]) = if(t eq null) (null, null) else { - var tmpk, tmpd = null: Tree[A, B] // shared vars to avoid returning tuples from fk - def fk(t: Tree[A, B]): Unit = { - val k = t.key - val v = t.value - val l = t.left - val r = t.right - var l2k, l2d, r2k, r2d = null: Tree[A, B] - if(l ne null) { - fk(l) - l2k = tmpk - l2d = tmpd - } - val keep = p(k) - if(r ne null) { - fk(r) - r2k = tmpk - r2d = tmpd - } - val jk = - if(!keep) join2(l2k, r2k) - else if((l2k eq l) && (r2k eq r)) t - else join(l2k, k, v, r2k) - val jd = - if(keep) join2(l2d, r2d) - else if((l2d eq l) && (r2d eq r)) t - else join(l2d, k, v, r2d) - tmpk = jk - tmpd = jd + partitioner.fk(t) + (blacken(partitioner.tmpk), blacken(partitioner.tmpd)) } - fk(t) - (blacken(tmpk), blacken(tmpd)) } - // Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees // Constructing Red-Black Trees, Ralf Hinze: [[https://www.cs.ox.ac.uk/ralf.hinze/publications/WAAAPL99b.ps.gz]] // Red-Black Trees in a Functional Setting, Chris Okasaki: [[https://wiki.rice.edu/confluence/download/attachments/2761212/Okasaki-Red-Black.pdf]] */ private[this] def del[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { - def delLeft = - if (isBlackTree(tree.left)) balLeft(tree.key, tree.value, del(tree.left, k), tree.right) - else RedTree(tree.key, tree.value, del(tree.left, k), tree.right) - def delRight = - if (isBlackTree(tree.right)) balRight(tree.key, tree.value, tree.left, del(tree.right, k)) - else RedTree(tree.key, tree.value, tree.left, del(tree.right, k)) val cmp = ordering.compare(k, tree.key) - if (cmp < 0) delLeft - else if (cmp > 0) delRight - else append(tree.left, tree.right) + if (cmp < 0) { + val newLeft = del(tree.left, k) + if (newLeft eq tree.left) tree + else if (isBlackTree(tree.left)) balLeft(tree, newLeft, tree.right) + else tree.redWithLeft(newLeft) + } else if (cmp > 0) { + val newRight = del(tree.right, k) + if (newRight eq tree.right) tree + else if (isBlackTree(tree.right)) balRight(tree, tree.left, newRight) + else tree.redWithRight(newRight) + } else append(tree.left, tree.right) } - private[this] def balance[A, B](x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = + private[this] def balance[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = if (isRedTree(tl)) { - if (isRedTree(tr)) RedTree(x, xv, tl.black, tr.black) - else if (isRedTree(tl.left)) RedTree(tl.key, tl.value, tl.left.black, BlackTree(x, xv, tl.right, tr)) - else if (isRedTree(tl.right)) - RedTree(tl.right.key, tl.right.value, BlackTree(tl.key, tl.value, tl.left, tl.right.left), BlackTree(x, xv, tl.right.right, tr)) - else BlackTree(x, xv, tl, tr) + if (isRedTree(tr)) tree.redWithLeftRight(tl.black, tr.black) + else if (isRedTree(tl.left)) tl.withLeftRight(tl.left.black, tree.blackWithLeftRight(tl.right, tr)) + else if (isRedTree(tl.right)) tl.right.withLeftRight(tl.blackWithRight(tl.right.left), tree.blackWithLeftRight(tl.right.right, tr)) + else tree.blackWithLeftRight(tl, tr) } else if (isRedTree(tr)) { - if (isRedTree(tr.right)) RedTree(tr.key, tr.value, BlackTree(x, xv, tl, tr.left), tr.right.black) - else if (isRedTree(tr.left)) - RedTree(tr.left.key, tr.left.value, BlackTree(x, xv, tl, tr.left.left), BlackTree(tr.key, tr.value, tr.left.right, tr.right)) - else BlackTree(x, xv, tl, tr) - } else BlackTree(x, xv, tl, tr) - - private[this] def balLeft[A, B](x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = - if (isRedTree(tl)) RedTree(x, xv, tl.black, tr) - else if (isBlackTree(tr)) balance(x, xv, tl, tr.red) + if (isRedTree(tr.right)) tr.withLeftRight(tree.blackWithLeftRight(tl, tr.left), tr.right.black) + else if (isRedTree(tr.left)) tr.left.withLeftRight(tree.blackWithLeftRight(tl, tr.left.left), tr.blackWithLeftRight(tr.left.right, tr.right)) + else tree.blackWithLeftRight(tl, tr) + } else tree.blackWithLeftRight(tl, tr) + + private[this] def balLeft[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + if (isRedTree(tl)) tree.redWithLeftRight(tl.black, tr) + else if (isBlackTree(tr)) balance(tree, tl, tr.red) else if (isRedTree(tr) && isBlackTree(tr.left)) - RedTree(tr.left.key, tr.left.value, BlackTree(x, xv, tl, tr.left.left), balance(tr.key, tr.value, tr.left.right, tr.right.red)) + tr.left.redWithLeftRight(tree.blackWithLeftRight(tl, tr.left.left), balance(tr, tr.left.right, tr.right.red)) else sys.error("Defect: invariance violation") - private[this] def balRight[A, B](x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = - if (isRedTree(tr)) RedTree(x, xv, tl, tr.black) - else if (isBlackTree(tl)) balance(x, xv, tl.red, tr) + private[this] def balRight[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + if (isRedTree(tr)) tree.redWithLeftRight(tl, tr.black) + else if (isBlackTree(tl)) balance(tree, tl.red, tr) else if (isRedTree(tl) && isBlackTree(tl.right)) - RedTree(tl.right.key, tl.right.value, balance(tl.key, tl.value, tl.left.red, tl.right.left), BlackTree(x, xv, tl.right.right, tr)) + tl.right.redWithLeftRight(balance(tl, tl.left.red, tl.right.left), tree.blackWithLeftRight(tl.right.right, tr)) else sys.error("Defect: invariance violation") /** `append` is similar to `join2` but requires that both subtrees have the same black height */ - private[this] def append[A, B](tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + private[this] def append[A, B](tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = { if (tl eq null) tr else if (tr eq null) tl - else if (isRedTree(tl) && isRedTree(tr)) { - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) { - RedTree(bc.key, bc.value, RedTree(tl.key, tl.value, tl.left, bc.left), RedTree(tr.key, tr.value, bc.right, tr.right)) - } else { - RedTree(tl.key, tl.value, tl.left, RedTree(tr.key, tr.value, bc, tr.right)) - } - } else if (isBlackTree(tl) && isBlackTree(tr)) { - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) { - RedTree(bc.key, bc.value, BlackTree(tl.key, tl.value, tl.left, bc.left), BlackTree(tr.key, tr.value, bc.right, tr.right)) - } else { - balLeft(tl.key, tl.value, tl.left, BlackTree(tr.key, tr.value, bc, tr.right)) - } - } else if (isRedTree(tr)) RedTree(tr.key, tr.value, append(tl, tr.left), tr.right) - else if (isRedTree(tl)) RedTree(tl.key, tl.value, tl.left, append(tl.right, tr)) - else sys.error("unmatched tree on append: " + tl + ", " + tr) + else if (tl.isRed) { + if (tr.isRed) { + //tl is red, tr is red + val bc = append(tl.right, tr.left) + if (isRedTree(bc)) bc.withLeftRight(tl.withRight(bc.left), tr.withLeft(bc.right)) + else tl.withRight(tr.withLeft(bc)) + } else { + //tl is red, tr is black + tl.withRight(append(tl.right, tr)) + } + } else { + if (tr.isBlack) { + //tl is black tr is black + val bc = append(tl.right, tr.left) + if (isRedTree(bc)) bc.withLeftRight(tl.withRight(bc.left), tr.withLeft(bc.right)) + else balLeft(tl, tl.left, tr.withLeft(bc)) + } else { + //tl is black tr is red + tr.withLeft(append(tl, tr.left)) + } + } + } // Bulk operations based on "Just Join for Parallel Ordered Sets" (https://www.cs.cmu.edu/~guyb/papers/BFS16.pdf) @@ -1110,7 +1119,7 @@ private[collection] object RedBlackTree { /** Compute the rank from a tree and its black height */ @`inline` private[this] def rank(t: Tree[_, _], bh: Int): Int = { if(t eq null) 0 - else if(isBlackTree(t)) 2*(bh-1) + else if(t.isBlack) 2*(bh-1) else 2*bh-1 } @@ -1146,7 +1155,7 @@ private[collection] object RedBlackTree { private[this] def join[A, B](tl: Tree[A, B], k: A, v: B, tr: Tree[A, B]): Tree[A, B] = { @tailrec def h(t: Tree[_, _], i: Int): Int = - if(t eq null) i+1 else h(t.left, if(isBlackTree(t)) i+1 else i) + if(t eq null) i+1 else h(t.left, if(t.isBlack) i+1 else i) val bhtl = h(tl, 0) val bhtr = h(tr, 0) if(bhtl > bhtr) { diff --git a/src/library/scala/collection/immutable/TreeMap.scala b/src/library/scala/collection/immutable/TreeMap.scala index 81165b798580..90441e867052 100644 --- a/src/library/scala/collection/immutable/TreeMap.scala +++ b/src/library/scala/collection/immutable/TreeMap.scala @@ -283,7 +283,7 @@ final class TreeMap[K, +V] private (private val tree: RB.Tree[K, V])(implicit va } } override def equals(obj: Any): Boolean = obj match { - case that: TreeMap[K, V] if ordering == that.ordering => RB.entriesEqual(tree, that.tree) + case that: TreeMap[K, _] if ordering == that.ordering => RB.entriesEqual(tree, that.tree) case _ => super.equals(obj) } diff --git a/src/library/scala/collection/immutable/TreeSet.scala b/src/library/scala/collection/immutable/TreeSet.scala index f68d85421791..51e55782b19f 100644 --- a/src/library/scala/collection/immutable/TreeSet.scala +++ b/src/library/scala/collection/immutable/TreeSet.scala @@ -213,10 +213,10 @@ final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[ super.diff(that) } - override def filter(f: A => Boolean): TreeSet[A] = newSetOrSelf(RB.filterKeys(tree, f)) + override def filter(f: A => Boolean): TreeSet[A] = newSetOrSelf(RB.filterEntries[A, Any](tree, {(k, _) => f(k)})) override def partition(p: A => Boolean): (TreeSet[A], TreeSet[A]) = { - val (l, r) = RB.partitionKeys(tree, p) + val (l, r) = RB.partitionEntries(tree, {(a:A, _: Any) => p(a)}) (newSetOrSelf(l), newSetOrSelf(r)) } diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index 7f811c97834a..41dfbb609816 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -106,13 +106,13 @@ trait ExecutionContext { /** * An [[ExecutionContext]] that is also a - * Java [[https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html Executor]]. + * Java [[java.util.concurrent.Executor Executor]]. */ trait ExecutionContextExecutor extends ExecutionContext with Executor /** * An [[ExecutionContext]] that is also a - * Java [[https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html ExecutorService]]. + * Java [[java.util.concurrent.ExecutorService ExecutorService]]. */ trait ExecutionContextExecutorService extends ExecutionContextExecutor with ExecutorService @@ -287,7 +287,7 @@ object ExecutionContext { */ def fromExecutor(e: Executor): ExecutionContextExecutor = fromExecutor(e, defaultReporter) - /** The default reporter simply prints the stack trace of the `Throwable` to [[https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#err System.err]]. + /** The default reporter simply prints the stack trace of the `Throwable` to [[java.lang.System#err System.err]]. * * @return the function for error reporting */ diff --git a/src/library/scala/deprecated.scala b/src/library/scala/deprecated.scala index 0c22f549afb6..1459cd819220 100644 --- a/src/library/scala/deprecated.scala +++ b/src/library/scala/deprecated.scala @@ -58,4 +58,4 @@ import scala.annotation.meta._ */ @getter @setter @beanGetter @beanSetter @field @deprecatedInheritance("Scheduled for being final in the future", "2.13.0") -class deprecated(message: String = "", since: String = "") extends scala.annotation.StaticAnnotation +class deprecated(message: String = "", since: String = "") extends scala.annotation.ConstantAnnotation diff --git a/src/library/scala/deprecatedInheritance.scala b/src/library/scala/deprecatedInheritance.scala index 14ccdeabc340..21e3932d97df 100644 --- a/src/library/scala/deprecatedInheritance.scala +++ b/src/library/scala/deprecatedInheritance.scala @@ -47,4 +47,4 @@ import scala.annotation.meta._ * @see [[scala.deprecatedName]] */ @getter @setter @beanGetter @beanSetter -final class deprecatedInheritance(message: String = "", since: String = "") extends scala.annotation.StaticAnnotation +final class deprecatedInheritance(message: String = "", since: String = "") extends scala.annotation.ConstantAnnotation diff --git a/src/library/scala/deprecatedName.scala b/src/library/scala/deprecatedName.scala index 24b9ac4e6ad0..ee5eafd69b9b 100644 --- a/src/library/scala/deprecatedName.scala +++ b/src/library/scala/deprecatedName.scala @@ -14,7 +14,6 @@ package scala import scala.annotation.meta._ - /** An annotation that designates that the name of a parameter is deprecated. * * Using this name in a named argument generates a deprecation warning. @@ -43,6 +42,8 @@ import scala.annotation.meta._ @param @deprecatedInheritance("Scheduled for being final in the future", "2.13.0") class deprecatedName(name: String = "", since: String = "") extends scala.annotation.StaticAnnotation { + // at the time we remove these constructors, we should also change this from a StaticAnnotation to + // a ConstantAnnotation; for now, the presence of auxiliary constructors blocks that change @deprecated("The parameter name should be a String, not a symbol.", "2.13.0") def this(name: Symbol, since: String) = this(name.name, since) @deprecated("The parameter name should be a String, not a symbol.", "2.13.0") def this(name: Symbol) = this(name.name, "") } diff --git a/src/library/scala/deprecatedOverriding.scala b/src/library/scala/deprecatedOverriding.scala index d88f29e53a1c..b6c75819785a 100644 --- a/src/library/scala/deprecatedOverriding.scala +++ b/src/library/scala/deprecatedOverriding.scala @@ -49,4 +49,4 @@ import scala.annotation.meta._ */ @getter @setter @beanGetter @beanSetter @deprecatedInheritance("Scheduled for being final in the future", "2.13.0") -class deprecatedOverriding(message: String = "", since: String = "") extends scala.annotation.StaticAnnotation +class deprecatedOverriding(message: String = "", since: String = "") extends scala.annotation.ConstantAnnotation diff --git a/src/library/scala/jdk/Accumulator.scala b/src/library/scala/jdk/Accumulator.scala index da5f722df4d2..ca1b0215bcd8 100644 --- a/src/library/scala/jdk/Accumulator.scala +++ b/src/library/scala/jdk/Accumulator.scala @@ -54,7 +54,8 @@ import scala.language.implicitConversions * There are two possibilities to process elements of a primitive Accumulator without boxing: * specialized operations of the Accumulator, or the Stepper interface. The most common collection * operations are overloaded or overridden in the primitive Accumulator classes, for example - * [[IntAccumulator.map(f: Int => Int)* IntAccumulator.map]] or [[IntAccumulator.exists]]. Thanks to Scala's function specialization, + * [[IntAccumulator.map(f:Int=>Int)* IntAccumulator.map]] or [[IntAccumulator.exists]]. + * Thanks to Scala's function specialization, * `intAcc.exists(x => testOn(x))` does not incur boxing. * * The [[scala.collection.Stepper]] interface provides iterator-like `hasStep` and `nextStep` methods, and is diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala index 20cec9742ed2..6ea371328d9e 100644 --- a/src/library/scala/math/BigInt.scala +++ b/src/library/scala/math/BigInt.scala @@ -21,9 +21,23 @@ import scala.collection.immutable.NumericRange object BigInt { + private val longMinValueBigInteger = BigInteger.valueOf(Long.MinValue) + private val longMinValue = new BigInt(longMinValueBigInteger, Long.MinValue) + private[this] val minCached = -1024 private[this] val maxCached = 1024 private[this] val cache = new Array[BigInt](maxCached - minCached + 1) + + private[this] def getCached(i: Int): BigInt = { + val offset = i - minCached + var n = cache(offset) + if (n eq null) { + n = new BigInt(null, i.toLong) + cache(offset) = n + } + n + } + private val minusOne = BigInteger.valueOf(-1) /** Constructs a `BigInt` whose value is equal to that of the @@ -33,12 +47,7 @@ object BigInt { * @return the constructed `BigInt` */ def apply(i: Int): BigInt = - if (minCached <= i && i <= maxCached) { - val offset = i - minCached - var n = cache(offset) - if (n eq null) { n = new BigInt(BigInteger.valueOf(i.toLong)); cache(offset) = n } - n - } else new BigInt(BigInteger.valueOf(i.toLong)) + if (minCached <= i && i <= maxCached) getCached(i) else apply(i: Long) /** Constructs a `BigInt` whose value is equal to that of the * specified long value. @@ -47,14 +56,15 @@ object BigInt { * @return the constructed `BigInt` */ def apply(l: Long): BigInt = - if (minCached <= l && l <= maxCached) apply(l.toInt) - else new BigInt(BigInteger.valueOf(l)) + if (minCached <= l && l <= maxCached) getCached(l.toInt) else { + if (l == Long.MinValue) longMinValue else new BigInt(null, l) + } /** Translates a byte array containing the two's-complement binary * representation of a BigInt into a BigInt. */ def apply(x: Array[Byte]): BigInt = - new BigInt(new BigInteger(x)) + apply(new BigInteger(x)) /** Translates the sign-magnitude representation of a BigInt into a BigInt. * @@ -64,40 +74,44 @@ object BigInt { * the number. */ def apply(signum: Int, magnitude: Array[Byte]): BigInt = - new BigInt(new BigInteger(signum, magnitude)) + apply(new BigInteger(signum, magnitude)) /** Constructs a randomly generated positive BigInt that is probably prime, * with the specified bitLength. */ def apply(bitlength: Int, certainty: Int, rnd: scala.util.Random): BigInt = - new BigInt(new BigInteger(bitlength, certainty, rnd.self)) + apply(new BigInteger(bitlength, certainty, rnd.self)) /** Constructs a randomly generated BigInt, uniformly distributed over the * range `0` to `(2 ^ numBits - 1)`, inclusive. */ def apply(numbits: Int, rnd: scala.util.Random): BigInt = - new BigInt(new BigInteger(numbits, rnd.self)) + apply(new BigInteger(numbits, rnd.self)) /** Translates the decimal String representation of a BigInt into a BigInt. */ def apply(x: String): BigInt = - new BigInt(new BigInteger(x)) + apply(new BigInteger(x)) /** Translates the string representation of a `BigInt` in the * specified `radix` into a BigInt. */ def apply(x: String, radix: Int): BigInt = - new BigInt(new BigInteger(x, radix)) + apply(new BigInteger(x, radix)) /** Translates a `java.math.BigInteger` into a BigInt. */ - def apply(x: BigInteger): BigInt = - new BigInt(x) + def apply(x: BigInteger): BigInt = { + if (x.bitLength <= 63) { + val l = x.longValue + if (minCached <= l && l <= maxCached) getCached(l.toInt) else new BigInt(x, l) + } else new BigInt(x, Long.MinValue) + } /** Returns a positive BigInt that is probably prime, with the specified bitLength. */ def probablePrime(bitLength: Int, rnd: scala.util.Random): BigInt = - new BigInt(BigInteger.probablePrime(bitLength, rnd.self)) + apply(BigInteger.probablePrime(bitLength, rnd.self)) /** Implicit conversion from `Int` to `BigInt`. */ @@ -110,14 +124,103 @@ object BigInt { /** Implicit conversion from `java.math.BigInteger` to `scala.BigInt`. */ implicit def javaBigInteger2bigInt(x: BigInteger): BigInt = apply(x) + + // this method is adapted from Google Guava's version at + // https://github.com/google/guava/blob/master/guava/src/com/google/common/math/LongMath.java + // that code carries the following notice: + // * Copyright (C) 2011 The Guava Authors + // * + // * Licensed under the Apache License, Version 2.0 (the "License") + /** + * Returns the greatest common divisor of a and b. Returns 0 if a == 0 && b == 0. + */ + private def longGcd(a: Long, b: Long): Long = { + // both a and b must be >= 0 + if (a == 0) { // 0 % b == 0, so b divides a, but the converse doesn't hold. + // BigInteger.gcd is consistent with this decision. + return b + } + else if (b == 0) return a // similar logic + /* + * Uses the binary GCD algorithm; see http://en.wikipedia.org/wiki/Binary_GCD_algorithm. This is + * >60% faster than the Euclidean algorithm in benchmarks. + */ + val aTwos = java.lang.Long.numberOfTrailingZeros(a) + var a1 = a >> aTwos // divide out all 2s + + val bTwos = java.lang.Long.numberOfTrailingZeros(b) + var b1 = b >> bTwos + while (a1 != b1) { // both a, b are odd + // The key to the binary GCD algorithm is as follows: + // Both a1 and b1 are odd. Assume a1 > b1; then gcd(a1 - b1, b1) = gcd(a1, b1). + // But in gcd(a1 - b1, b1), a1 - b1 is even and b1 is odd, so we can divide out powers of two. + // We bend over backwards to avoid branching, adapting a technique from + // http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax + val delta = a1 - b1 // can't overflow, since a1 and b1 are nonnegative + val minDeltaOrZero = delta & (delta >> (java.lang.Long.SIZE - 1)) + // equivalent to Math.min(delta, 0) + a1 = delta - minDeltaOrZero - minDeltaOrZero // sets a to Math.abs(a - b) + + // a is now nonnegative and even + b1 += minDeltaOrZero // sets b to min(old a, b) + + a1 >>= java.lang.Long.numberOfTrailingZeros(a1) // divide out all 2s, since 2 doesn't divide b + + } + a1 << scala.math.min(aTwos, bTwos) + } + } -final class BigInt(val bigInteger: BigInteger) +/** A type with efficient encoding of arbitrary integers. + * + * It wraps `java.math.BigInteger`, with optimization for small values that can be encoded in a `Long`. + */ +final class BigInt private (private var _bigInteger: BigInteger, private val _long: Long) extends ScalaNumber with ScalaNumericConversions with Serializable with Ordered[BigInt] { + // The class has a special encoding for integer that fit in a Long *and* are not equal to Long.MinValue. + // + // The Long value Long.MinValue is a tag specifying that the integer is encoded in the BigInteger field. + // + // There are three possible states for the class fields (_bigInteger, _long) + // 1. (null, l) where l != Long.MinValue, encodes the integer "l" + // 2. (b, l) where l != Long.MinValue; then b is a BigInteger with value l, encodes "l" == "b" + // 3a. (b, Long.MinValue) where b == Long.MinValue, encodes Long.MinValue + // 3b. (b, Long.MinValue) where b does not fit in a Long, encodes "b" + // + // There is only one possible transition 1. -> 2., when the method .bigInteger is called, then the field + // _bigInteger caches the result. + // + // The case 3a. is the only one where the BigInteger could actually fit in a Long, but as its value is used as a + // tag, we'll take the slow path instead. + // + // Additionally, we know that if this.isValidLong is true, then _long is the encoded value. + + /** Public constructor present for compatibility. Use the BigInt.apply companion object method instead. */ + def this(bigInteger: BigInteger) = this( + bigInteger, // even if it is a short BigInteger, we cache the instance + if (bigInteger.bitLength <= 63) + bigInteger.longValue // if _bigInteger is actually equal to Long.MinValue, no big deal, its value acts as a tag + else Long.MinValue + ) + + /** Returns whether the integer is encoded in the Long. Returns true for all values fitting in a Long except + * Long.MinValue. */ + private def longEncoding: Boolean = _long != Long.MinValue + + def bigInteger: BigInteger = { + val read = _bigInteger + if (read ne null) read else { + val write = BigInteger.valueOf(_long) + _bigInteger = write // reference assignment is atomic; this is multi-thread safe (if possibly wasteful) + write + } + } + /** Returns the hash code for this BigInt. */ override def hashCode(): Int = if (isValidLong) unifiedPrimitiveHashcode @@ -132,11 +235,13 @@ final class BigInt(val bigInteger: BigInteger) case that: Float => isValidFloat && toFloat == that case x => isValidLong && unifiedPrimitiveEquals(x) } - override def isValidByte: Boolean = this >= Byte.MinValue && this <= Byte.MaxValue - override def isValidShort: Boolean = this >= Short.MinValue && this <= Short.MaxValue - override def isValidChar: Boolean = this >= Char.MinValue && this <= Char.MaxValue - override def isValidInt: Boolean = this >= Int.MinValue && this <= Int.MaxValue - def isValidLong: Boolean = this >= Long.MinValue && this <= Long.MaxValue + + override def isValidByte: Boolean = _long >= Byte.MinValue && _long <= Byte.MaxValue /* && longEncoding */ + override def isValidShort: Boolean = _long >= Short.MinValue && _long <= Short.MaxValue /* && longEncoding */ + override def isValidChar: Boolean = _long >= Char.MinValue && _long <= Char.MaxValue /* && longEncoding */ + override def isValidInt: Boolean = _long >= Int.MinValue && _long <= Int.MaxValue /* && longEncoding */ + def isValidLong: Boolean = longEncoding || _bigInteger == BigInt.longMinValueBigInteger // rhs of || tests == Long.MinValue + /** Returns `true` iff this can be represented exactly by [[scala.Float]]; otherwise returns `false`. */ def isValidFloat: Boolean = { @@ -178,151 +283,266 @@ final class BigInt(val bigInteger: BigInteger) /** Compares this BigInt with the specified BigInt for equality. */ - def equals (that: BigInt): Boolean = compare(that) == 0 + def equals(that: BigInt): Boolean = + if (this.longEncoding) + that.longEncoding && (this._long == that._long) + else + !that.longEncoding && (this._bigInteger == that._bigInteger) /** Compares this BigInt with the specified BigInt */ - def compare (that: BigInt): Int = this.bigInteger.compareTo(that.bigInteger) + def compare(that: BigInt): Int = + if (this.longEncoding) { + if (that.longEncoding) java.lang.Long.compare(this._long, that._long) else -that._bigInteger.signum() + } else { + if (that.longEncoding) _bigInteger.signum() else this._bigInteger.compareTo(that._bigInteger) + } /** Addition of BigInts */ - def + (that: BigInt): BigInt = new BigInt(this.bigInteger.add(that.bigInteger)) + def +(that: BigInt): BigInt = { + if (this.longEncoding && that.longEncoding) { // fast path + val x = this._long + val y = that._long + val z = x + y + if ((~(x ^ y) & (x ^ z)) >= 0L) return BigInt(z) + } + BigInt(this.bigInteger.add(that.bigInteger)) + } /** Subtraction of BigInts */ - def - (that: BigInt): BigInt = new BigInt(this.bigInteger.subtract(that.bigInteger)) + def -(that: BigInt): BigInt = { + if (this.longEncoding && that.longEncoding) { // fast path + val x = this._long + val y = that._long + val z = x - y + if (((x ^ y) & (x ^ z)) >= 0L) return BigInt(z) + } + BigInt(this.bigInteger.subtract(that.bigInteger)) + } /** Multiplication of BigInts */ - def * (that: BigInt): BigInt = new BigInt(this.bigInteger.multiply(that.bigInteger)) + def *(that: BigInt): BigInt = { + if (this.longEncoding && that.longEncoding) { // fast path + val x = this._long + val y = that._long + val z = x * y + // original code checks the y != Long.MinValue, but when longEncoding is true, that is never the case + // if (x == 0 || (y == z / x && !(x == -1 && y == Long.MinValue))) return BigInt(z) + if (x == 0 || y == z / x) return BigInt(z) + } + BigInt(this.bigInteger.multiply(that.bigInteger)) + } /** Division of BigInts */ - def / (that: BigInt): BigInt = new BigInt(this.bigInteger.divide(that.bigInteger)) + def /(that: BigInt): BigInt = + // in the fast path, note that the original code avoided storing -Long.MinValue in a long: + // if (this._long != Long.MinValue || that._long != -1) return BigInt(this._long / that._long) + // but we know this._long cannot be Long.MinValue, because Long.MinValue is the tag for bigger integers + if (this.longEncoding && that.longEncoding) BigInt(this._long / that._long) + else BigInt(this.bigInteger.divide(that.bigInteger)) /** Remainder of BigInts */ - def % (that: BigInt): BigInt = new BigInt(this.bigInteger.remainder(that.bigInteger)) + def %(that: BigInt): BigInt = + // see / for the original logic regarding Long.MinValue + if (this.longEncoding && that.longEncoding) BigInt(this._long % that._long) + else BigInt(this.bigInteger.remainder(that.bigInteger)) /** Returns a pair of two BigInts containing (this / that) and (this % that). */ - def /% (that: BigInt): (BigInt, BigInt) = { - val dr = this.bigInteger.divideAndRemainder(that.bigInteger) - (new BigInt(dr(0)), new BigInt(dr(1))) - } + def /%(that: BigInt): (BigInt, BigInt) = + if (this.longEncoding && that.longEncoding) { + val x = this._long + val y = that._long + // original line: if (x != Long.MinValue || y != -1) return (BigInt(x / y), BigInt(x % y)) + (BigInt(x / y), BigInt(x % y)) + } else { + val dr = this.bigInteger.divideAndRemainder(that.bigInteger) + (BigInt(dr(0)), BigInt(dr(1))) + } /** Leftshift of BigInt */ - def << (n: Int): BigInt = new BigInt(this.bigInteger.shiftLeft(n)) + def <<(n: Int): BigInt = + if (longEncoding && n <= 0) (this >> (-n)) else BigInt(this.bigInteger.shiftLeft(n)) /** (Signed) rightshift of BigInt */ - def >> (n: Int): BigInt = new BigInt(this.bigInteger.shiftRight(n)) - + def >>(n: Int): BigInt = + if (longEncoding && n >= 0) { + if (n < 64) BigInt(_long >> n) + else if (_long < 0) BigInt(-1) + else BigInt(0) // for _long >= 0 + } else BigInt(this.bigInteger.shiftRight(n)) + /** Bitwise and of BigInts */ - def & (that: BigInt): BigInt = new BigInt(this.bigInteger.and(that.bigInteger)) + def &(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) + BigInt(this._long & that._long) + else BigInt(this.bigInteger.and(that.bigInteger)) /** Bitwise or of BigInts */ - def | (that: BigInt): BigInt = new BigInt(this.bigInteger.or (that.bigInteger)) + def |(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) + BigInt(this._long | that._long) + else BigInt(this.bigInteger.or(that.bigInteger)) /** Bitwise exclusive-or of BigInts */ - def ^ (that: BigInt): BigInt = new BigInt(this.bigInteger.xor(that.bigInteger)) + def ^(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) + BigInt(this._long ^ that._long) + else BigInt(this.bigInteger.xor(that.bigInteger)) /** Bitwise and-not of BigInts. Returns a BigInt whose value is (this & ~that). */ - def &~ (that: BigInt): BigInt = new BigInt(this.bigInteger.andNot(that.bigInteger)) + def &~(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) + BigInt(this._long & ~that._long) + else BigInt(this.bigInteger.andNot(that.bigInteger)) /** Returns the greatest common divisor of abs(this) and abs(that) */ - def gcd (that: BigInt): BigInt = new BigInt(this.bigInteger.gcd(that.bigInteger)) + def gcd(that: BigInt): BigInt = + if (this.longEncoding) { + if (this._long == 0) return that.abs + // if (this._long == Long.MinValue) return (-this) gcd that + // this != 0 && this != Long.MinValue + if (that.longEncoding) { + if (that._long == 0) return this.abs + // if (that._long == Long.MinValue) return this gcd (-that) + BigInt(BigInt.longGcd(this._long.abs, that._long.abs)) + } else that gcd this // force the BigInteger on the left + } else { + // this is not a valid long + if (that.longEncoding) { + if (that._long == 0) return this.abs + // if (that._long == Long.MinValue) return this gcd (-that) + val red = (this._bigInteger mod BigInteger.valueOf(that._long.abs)).longValue() + if (red == 0) return that.abs + BigInt(BigInt.longGcd(that._long.abs, red)) + } else BigInt(this.bigInteger.gcd(that.bigInteger)) + } + /** Returns a BigInt whose value is (this mod that). * This method differs from `%` in that it always returns a non-negative BigInt. * @param that A positive number */ - def mod (that: BigInt): BigInt = new BigInt(this.bigInteger.mod(that.bigInteger)) + def mod(that: BigInt): BigInt = + if (this.longEncoding && that.longEncoding) { + val res = this._long % that._long + if (res >= 0) BigInt(res) else BigInt(res + that._long) + } else BigInt(this.bigInteger.mod(that.bigInteger)) /** Returns the minimum of this and that */ - def min (that: BigInt): BigInt = new BigInt(this.bigInteger.min(that.bigInteger)) + def min(that: BigInt): BigInt = + if (this <= that) this else that /** Returns the maximum of this and that */ - def max (that: BigInt): BigInt = new BigInt(this.bigInteger.max(that.bigInteger)) + def max(that: BigInt): BigInt = + if (this >= that) this else that /** Returns a BigInt whose value is (this raised to the power of exp). */ - def pow (exp: Int): BigInt = new BigInt(this.bigInteger.pow(exp)) + def pow(exp: Int): BigInt = BigInt(this.bigInteger.pow(exp)) /** Returns a BigInt whose value is * (this raised to the power of exp modulo m). */ - def modPow (exp: BigInt, m: BigInt): BigInt = - new BigInt(this.bigInteger.modPow(exp.bigInteger, m.bigInteger)) + def modPow(exp: BigInt, m: BigInt): BigInt = BigInt(this.bigInteger.modPow(exp.bigInteger, m.bigInteger)) /** Returns a BigInt whose value is (the inverse of this modulo m). */ - def modInverse (m: BigInt): BigInt = new BigInt(this.bigInteger.modInverse(m.bigInteger)) + def modInverse(m: BigInt): BigInt = BigInt(this.bigInteger.modInverse(m.bigInteger)) /** Returns a BigInt whose value is the negation of this BigInt */ - def unary_- : BigInt = new BigInt(this.bigInteger.negate()) + def unary_- : BigInt = if (longEncoding) BigInt(-_long) else BigInt(this.bigInteger.negate()) /** Returns the absolute value of this BigInt */ - def abs: BigInt = new BigInt(this.bigInteger.abs()) + def abs: BigInt = if (signum < 0) -this else this /** Returns the sign of this BigInt; * -1 if it is less than 0, * +1 if it is greater than 0, * 0 if it is equal to 0. */ - def signum: Int = this.bigInteger.signum() + def signum: Int = if (longEncoding) java.lang.Long.signum(_long) else _bigInteger.signum() /** Returns the sign of this BigInt; * -1 if it is less than 0, * +1 if it is greater than 0, * 0 if it is equal to 0. */ - def sign: BigInt = signum + def sign: BigInt = BigInt(signum) /** Returns the bitwise complement of this BigInt */ - def unary_~ : BigInt = new BigInt(this.bigInteger.not()) + def unary_~ : BigInt = + // it is equal to -(this + 1) + if (longEncoding && _long != Long.MaxValue) BigInt(-(_long + 1)) else BigInt(this.bigInteger.not()) /** Returns true if and only if the designated bit is set. */ - def testBit (n: Int): Boolean = this.bigInteger.testBit(n) + def testBit(n: Int): Boolean = + if (longEncoding) { + if (n <= 63) + (_long & (1L << n)) != 0 + else + _long < 0 // give the sign bit + } else _bigInteger.testBit(n) /** Returns a BigInt whose value is equivalent to this BigInt with the designated bit set. */ - def setBit (n: Int): BigInt = new BigInt(this.bigInteger.setBit(n)) + def setBit(n: Int): BigInt = // note that we do not operate on the Long sign bit #63 + if (longEncoding && n <= 62) BigInt(_long | (1L << n)) else BigInt(this.bigInteger.setBit(n)) /** Returns a BigInt whose value is equivalent to this BigInt with the designated bit cleared. */ - def clearBit(n: Int): BigInt = new BigInt(this.bigInteger.clearBit(n)) + def clearBit(n: Int): BigInt = // note that we do not operate on the Long sign bit #63 + if (longEncoding && n <= 62) BigInt(_long & ~(1L << n)) else BigInt(this.bigInteger.clearBit(n)) /** Returns a BigInt whose value is equivalent to this BigInt with the designated bit flipped. */ - def flipBit (n: Int): BigInt = new BigInt(this.bigInteger.flipBit(n)) + def flipBit(n: Int): BigInt = // note that we do not operate on the Long sign bit #63 + if (longEncoding && n <= 62) BigInt(_long ^ (1L << n)) else BigInt(this.bigInteger.flipBit(n)) /** Returns the index of the rightmost (lowest-order) one bit in this BigInt * (the number of zero bits to the right of the rightmost one bit). */ - def lowestSetBit: Int = this.bigInteger.getLowestSetBit() + def lowestSetBit: Int = + if (longEncoding) { + if (_long == 0) -1 else java.lang.Long.numberOfTrailingZeros(_long) + } else this.bigInteger.getLowestSetBit() /** Returns the number of bits in the minimal two's-complement representation of this BigInt, * excluding a sign bit. */ - def bitLength: Int = this.bigInteger.bitLength() + def bitLength: Int = + // bitLength is defined as ceil(log2(this < 0 ? -this : this + 1))) + // where ceil(log2(x)) = 64 - numberOfLeadingZeros(x - 1) + if (longEncoding) { + if (_long < 0) 64 - java.lang.Long.numberOfLeadingZeros(-(_long + 1)) // takes care of Long.MinValue + else 64 - java.lang.Long.numberOfLeadingZeros(_long) + } else _bigInteger.bitLength() /** Returns the number of bits in the two's complement representation of this BigInt * that differ from its sign bit. */ - def bitCount: Int = this.bigInteger.bitCount() + def bitCount: Int = + if (longEncoding) { + if (_long < 0) java.lang.Long.bitCount(-(_long + 1)) else java.lang.Long.bitCount(_long) + } else this.bigInteger.bitCount() /** Returns true if this BigInt is probably prime, false if it's definitely composite. * @param certainty a measure of the uncertainty that the caller is willing to tolerate: @@ -360,7 +580,7 @@ final class BigInt(val bigInteger: BigInteger) * overall magnitude of the BigInt value as well as return a result with * the opposite sign. */ - def intValue: Int = this.bigInteger.intValue + def intValue: Int = if (longEncoding) _long.toInt else this.bigInteger.intValue /** Converts this BigInt to a long. * If the BigInt is too big to fit in a long, only the low-order 64 bits @@ -368,7 +588,7 @@ final class BigInt(val bigInteger: BigInteger) * overall magnitude of the BigInt value as well as return a result with * the opposite sign. */ - def longValue: Long = this.bigInteger.longValue + def longValue: Long = if (longEncoding) _long else _bigInteger.longValue /** Converts this `BigInt` to a `float`. * If this `BigInt` has too great a magnitude to represent as a float, @@ -382,7 +602,9 @@ final class BigInt(val bigInteger: BigInteger) * it will be converted to `Double.NEGATIVE_INFINITY` or * `Double.POSITIVE_INFINITY` as appropriate. */ - def doubleValue: Double = this.bigInteger.doubleValue + def doubleValue: Double = + if (isValidLong && (-(1L << 53) <= _long && _long <= (1L << 53))) _long.toDouble + else this.bigInteger.doubleValue /** Create a `NumericRange[BigInt]` in range `[start;end)` * with the specified step, where start is the target BigInt. @@ -399,7 +621,7 @@ final class BigInt(val bigInteger: BigInteger) /** Returns the decimal String representation of this BigInt. */ - override def toString(): String = this.bigInteger.toString() + override def toString(): String = if (longEncoding) _long.toString() else _bigInteger.toString() /** Returns the String representation in the specified radix of this BigInt. */ diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 33faadc783ad..67551c7f6e80 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -12,7 +12,8 @@ package scala -import java.lang.reflect.{ AccessibleObject => jAccessibleObject } +import java.lang.reflect.{AccessibleObject => jAccessibleObject} +import scala.annotation.nowarn package object reflect { @@ -54,7 +55,10 @@ package object reflect { * attempt, it is caught and discarded. */ def ensureAccessible[T <: jAccessibleObject](m: T): T = { - if (!m.isAccessible) { + // This calls `setAccessible` unnecessarily, because `isAccessible` is only `true` if `setAccessible(true)` + // was called before, not if the reflected object is inherently accessible. + // TODO: replace by `canAccess` once we're on JDK 9+ + if (!m.isAccessible: @nowarn("cat=deprecation")) { try m setAccessible true catch { case _: SecurityException => } // does nothing } diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index d94fae987168..dfdb2a535c7b 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -64,7 +64,6 @@ object ScalaRunTime { case x: Array[Byte] => x(idx).asInstanceOf[Any] case x: Array[Short] => x(idx).asInstanceOf[Any] case x: Array[Boolean] => x(idx).asInstanceOf[Any] - case x: Array[Unit] => x(idx).asInstanceOf[Any] case null => throw new NullPointerException } } @@ -81,7 +80,6 @@ object ScalaRunTime { case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] case x: Array[Short] => x(idx) = value.asInstanceOf[Short] case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] - case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] case null => throw new NullPointerException } } @@ -132,7 +130,6 @@ object ScalaRunTime { case x: Array[Byte] => copy(x) case x: Array[Short] => copy(x) case x: Array[Boolean] => copy(x) - case x: Array[Unit] => copy(x) case null => throw new NullPointerException } } diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index ff9634e2cc7e..f10723cb4b1d 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -109,7 +109,7 @@ private[scala] trait PropertiesTrait { * or "version (unknown)" if it cannot be determined. */ val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") - val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2020, LAMP/EPFL and Lightbend, Inc.") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2021, LAMP/EPFL and Lightbend, Inc.") /** This is the encoding to use reading in source files, overridden with -encoding. * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 66ead3f03107..2b8bc69c07c6 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -33,7 +33,8 @@ import java.util.regex.{ Pattern, Matcher } * and, if it does, to extract or transform the parts that match. * * === Usage === - * This class delegates to the [[java.util.regex]] package of the Java Platform. + + * This class delegates to the [[https://docs.oracle.com/javase/8/docs/api/java/util/regex/package-summary.html java.util.regex]] package of the Java Platform. * See the documentation for [[java.util.regex.Pattern]] for details about * the regular expression syntax for pattern strings. * diff --git a/src/manual/scala/man1/scalac.scala b/src/manual/scala/man1/scalac.scala index 13ff1629b7dc..88788133debd 100644 --- a/src/manual/scala/man1/scalac.scala +++ b/src/manual/scala/man1/scalac.scala @@ -94,7 +94,7 @@ object scalac extends Command { "Specify character encoding used by source files.", "The default value is platform-specific (Linux: " & Mono("\"UTF8\"") & ", Windows: " & Mono("\"Cp1252\"") & "). Executing the following " & - "code in the Scala interpreter will return the default value " & + "code in the Scala REPL will return the default value " & "on your system:", MBold(" scala> ") & Mono("new java.io.InputStreamReader(System.in).getEncoding"))), @@ -220,9 +220,6 @@ object scalac extends Command { Definition( CmdOption("Xlog-implicit-conversions"), "Print a message whenever an implicit conversion is inserted."), - Definition( - CmdOption("Xlog-implicits"), - "Show more detail on why some implicits are not applicable."), Definition( CmdOption("Xlog-reflective-calls"), "Print a message when a reflective method call is generated."), @@ -262,9 +259,6 @@ object scalac extends Command { Definition( CmdOptionBound("Vprint:", Argument("phases")), "Print out program after " & Argument("phases") & " (see below)."), - Definition( - CmdOptionBound("Vprint-icode", "[:" & Argument("phases") & "]"), - "Log internal icode to *.icode files after" & Argument("phases") & " (default: icode)."), Definition( CmdOption("Vprint-pos"), "Print tree positions, as offsets."), @@ -387,9 +381,6 @@ object scalac extends Command { Definition( MItalic("delambdafy"), "remove lambdas"), - Definition( - MItalic("icode"), - "generate portable intermediate code"), Definition( MItalic("inliner"), "optimization: do inlining"), diff --git a/src/manual/scala/man1/scaladoc.scala b/src/manual/scala/man1/scaladoc.scala index e742c64cbd8c..675bb4ec01c1 100644 --- a/src/manual/scala/man1/scaladoc.scala +++ b/src/manual/scala/man1/scaladoc.scala @@ -124,7 +124,7 @@ object scaladoc extends Command { "Specify character encoding used by source files.", "The default value is platform-specific (Linux: " & Mono("\"UTF8\"") & ", Windows: " & Mono("\"Cp1252\"") & "). Executing the following " & - "code in the Scala interpreter will return the default value " & + "code in the Scala REPL will return the default value " & "on your system:", MBold(" scala> ") & Mono("new java.io.InputStreamReader(System.in).getEncoding")))))) diff --git a/src/manual/scala/tools/docutil/EmitManPage.scala b/src/manual/scala/tools/docutil/EmitManPage.scala index 0c748377c987..441883e62395 100644 --- a/src/manual/scala/tools/docutil/EmitManPage.scala +++ b/src/manual/scala/tools/docutil/EmitManPage.scala @@ -93,7 +93,7 @@ object EmitManPage { case BlockQuote(text) => out println ".TP" emitText(text) - out.println + out.println() case CodeSample(text) => out println "\n.nf" @@ -104,7 +104,7 @@ object EmitManPage { for (item <- lst.items) { out println ".IP" emitText(item) - out.println + out.println() } case lst:NumberedList => @@ -114,7 +114,7 @@ object EmitManPage { val item = lst.items(idx) out.println(".IP \" " + (idx+1) + ".\"") emitText(item) - out.println + out.println() } case TitledPara(title, text) => diff --git a/src/partest/scala/tools/partest/ConsoleLog.scala b/src/partest/scala/tools/partest/ConsoleLog.scala index 89feccd1ef78..5064f0fd5bfd 100644 --- a/src/partest/scala/tools/partest/ConsoleLog.scala +++ b/src/partest/scala/tools/partest/ConsoleLog.scala @@ -65,6 +65,7 @@ class ConsoleLog(colorEnabled: Boolean) { def echoWarning(msg: String) = echo(bold(red(msg))) def printDot(): Unit = printProgress(".") + def printS(): Unit = printProgress(_warning + "s" +_default) def printEx(): Unit = printProgress(_failure + "X" + _default) private def printProgress(icon: String): Unit = synchronized { if (dotCount >= DotWidth) { diff --git a/src/partest/scala/tools/partest/ScaladocModelTest.scala b/src/partest/scala/tools/partest/ScaladocModelTest.scala index 487c962a298a..ec158f9cfd60 100644 --- a/src/partest/scala/tools/partest/ScaladocModelTest.scala +++ b/src/partest/scala/tools/partest/ScaladocModelTest.scala @@ -85,15 +85,15 @@ abstract class ScaladocModelTest extends DirectTest { System.setErr(prevErr) } - private[this] var settings: doc.Settings = null + private[this] var docSettings: doc.Settings = null // create a new scaladoc compiler def newDocFactory: DocFactory = { - settings = new doc.Settings(_ => ()) - settings.scaladocQuietRun = true // yaay, no more "model contains X documentable templates"! + docSettings = new doc.Settings(_ => ()) + docSettings.scaladocQuietRun = true // yaay, no more "model contains X documentable templates"! val args = extraSettings + " " + scaladocSettings - new ScalaDoc.Command((CommandLineParser tokenize (args)), settings) // side-effecting, I think - val docFact = new DocFactory(new ConsoleReporter(settings), settings) + new ScalaDoc.Command((CommandLineParser tokenize (args)), docSettings) // side-effecting, I think + val docFact = new DocFactory(new ConsoleReporter(docSettings), docSettings) docFact } diff --git a/src/partest/scala/tools/partest/TestState.scala b/src/partest/scala/tools/partest/TestState.scala index 8867ffe72c8d..3b6dc49444ac 100644 --- a/src/partest/scala/tools/partest/TestState.scala +++ b/src/partest/scala/tools/partest/TestState.scala @@ -30,7 +30,7 @@ sealed abstract class TestState { def shortStatus = if (isOk) "ok" else "!!" - final def andAlso(next: => TestState): TestState = if (isOk) next else this + final def andAlso(next: => TestState): TestState = if (isOk && !isSkipped) next else this override def toString = status } diff --git a/src/partest/scala/tools/partest/nest/AbstractRunner.scala b/src/partest/scala/tools/partest/nest/AbstractRunner.scala index a38ca75e18ed..7f6dd9a5b794 100644 --- a/src/partest/scala/tools/partest/nest/AbstractRunner.scala +++ b/src/partest/scala/tools/partest/nest/AbstractRunner.scala @@ -99,7 +99,8 @@ class AbstractRunner(val config: RunnerSpec.Config, protected final val testSour diffed ::: logged } if (terse) { - if (state.isOk) { printDot() ; Nil } + if (state.isSkipped) { printS(); Nil } + else if (state.isOk) { printDot() ; Nil } else { printEx() ; statusLine(state, durationMs) :: errInfo } } else { echo(statusLine(state, durationMs)) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala index 8be3bd69d9af..906b021771b1 100644 --- a/src/partest/scala/tools/partest/nest/Runner.scala +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -130,25 +130,15 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { /** Fail the action. */ def nextTestActionFailing(reason: String): TestState = nextTestActionExpectTrue(reason, false) - private def assembleTestCommand(outDir: File, logFile: File): List[String] = { - // check whether there is a ".javaopts" file - val argsFile = testFile changeExtension "javaopts" - val javaopts = readOptionsFile(argsFile) + private def assembleTestCommand(outDir: File, javaopts: List[String]): List[String] = { if (javaopts.nonEmpty) - suiteRunner.verbose(s"Found javaopts file '$argsFile', using options: '${javaopts.mkString(",")}'") - - // Note! As this currently functions, suiteRunner.javaOpts must precede argString - // because when an option is repeated to java only the last one wins. - // That means until now all the .javaopts files were being ignored because - // they all attempt to change options which are also defined in - // partest.java_opts, leading to debug output like: - // - // debug: Found javaopts file 'files/shootout/message.scala-2.javaopts', using options: '-Xss32k' - // debug: java -Xss32k -Xss2m -Xms256M -Xmx1024M -classpath [...] + suiteRunner.verbose(s"Using java options: '${javaopts.mkString(",")}'") + val propertyOpts = propertyOptions(fork = true).map { case (k, v) => s"-D$k=$v" } val classpath = joinPaths(extraClasspath ++ testClassPath) + // `javaopts` last; for repeated arguments, the last one wins javaCmdPath +: ( (suiteRunner.javaOpts.split(' ') ++ extraJavaOptions ++ javaopts).filter(_ != "").toList ++ Seq( "-classpath", @@ -224,8 +214,8 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { } } - private def execTest(outDir: File, logFile: File): TestState = { - val cmd = assembleTestCommand(outDir, logFile) + private def execTest(outDir: File, logFile: File, javaopts: List[String]): TestState = { + val cmd = assembleTestCommand(outDir, javaopts) pushTranscript((cmd mkString s" \\$EOL ") + " > " + logFile.getName) nextTestAction(runCommand(cmd, logFile)) { @@ -514,9 +504,35 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { def description = mkScalacString() lazy val result = { pushTranscript(description) ; attemptCompile(fs) } } + case class SkipRound(fs: List[File], state: TestState) extends CompileRound { + def description: String = state.status + lazy val result = { pushTranscript(description); state } + } + + def compilationRounds(file: File): List[CompileRound] = { + import scala.util.Properties.javaSpecVersion + val Range = """(\d+)(?:(\+)|(?: *\- *(\d+)))?""".r + lazy val currentJavaVersion = javaSpecVersion.stripPrefix("1.").toInt + val allFiles = sources(file) + val skipStates = toolArgsFor(allFiles)("javaVersion", split = false).flatMap({ + case v @ Range(from, plus, to) => + val ok = + if (plus == null) + if (to == null) currentJavaVersion == from.toInt + else from.toInt <= currentJavaVersion && currentJavaVersion <= to.toInt + else + currentJavaVersion >= from.toInt + if (ok) None + else Some(genSkip(s"skipped on Java $javaSpecVersion, only running on $v")) + case v => + Some(genFail(s"invalid javaVersion range in test comment: $v")) + }) + skipStates.headOption match { + case Some(state) => List(SkipRound(List(file), state)) + case _ => groupedFiles(allFiles).flatMap(mixedCompileGroup) + } + } - def compilationRounds(file: File): List[CompileRound] = - groupedFiles(sources(file)).map(mixedCompileGroup).flatten def mixedCompileGroup(allFiles: List[File]): List[CompileRound] = { val (scalaFiles, javaFiles) = allFiles partition (_.isScala) val round1 = if (scalaFiles.isEmpty) None else Some(ScalaAndJava(allFiles)) @@ -533,17 +549,18 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { // pass if it checks and didn't crash the compiler // or, OK, we'll let you crash the compiler with a FatalError if you supply a check file def checked(r: CompileRound) = r.result match { + case s: Skip => s case crash @ Crash(_, t, _) if !checkFile.canRead || !t.isInstanceOf[FatalError] => crash - case dnc @ _ => diffIsOk + case _ => diffIsOk } - compilationRounds(testFile).find(!_.result.isOk).map(checked).getOrElse(genFail("expected compilation failure")) + compilationRounds(testFile).find(r => !r.result.isOk || r.result.isSkipped).map(checked).getOrElse(genFail("expected compilation failure")) } // run compilation until failure, evaluate `andAlso` on success def runTestCommon(andAlso: => TestState = genPass()): TestState = runInContext { // DirectCompiler already says compilation failed - val res = compilationRounds(testFile).find(!_.result.isOk).map(_.result).getOrElse(genPass()) + val res = compilationRounds(testFile).find(r => !r.result.isOk || r.result.isSkipped).map(_.result).getOrElse(genPass()) res andAlso andAlso } @@ -639,10 +656,9 @@ class Runner(val testInfo: TestInfo, val suiteRunner: AbstractRunner) { } private def runRunTest(): TestState = { - val argsFile = testFile changeExtension "javaopts" - val javaopts = readOptionsFile(argsFile) + val javaopts = toolArgs("java") val execInProcess = PartestDefaults.execInProcess && javaopts.isEmpty && !Set("specialized", "instrumented").contains(testFile.getParentFile.getName) - def exec() = if (execInProcess) execTestInProcess(outDir, logFile) else execTest(outDir, logFile) + def exec() = if (execInProcess) execTestInProcess(outDir, logFile) else execTest(outDir, logFile, javaopts) def noexec() = genSkip("no-exec: tests compiled but not run") runTestCommon(if (suiteRunner.config.optNoExec) noexec() else exec().andAlso(diffIsOk)) } diff --git a/src/partest/scala/tools/partest/nest/RunnerSpec.scala b/src/partest/scala/tools/partest/nest/RunnerSpec.scala index a83eaa209999..80c1bae94c02 100644 --- a/src/partest/scala/tools/partest/nest/RunnerSpec.scala +++ b/src/partest/scala/tools/partest/nest/RunnerSpec.scala @@ -25,7 +25,7 @@ trait RunnerSpec extends Spec with Meta.StdOpts with Interpolation { heading("Test categories:") val optPos = "pos" / "run compilation tests (success)" --? val optNeg = "neg" / "run compilation tests (failure)" --? - val optRun = "run" / "run interpreter and backend tests" --? + val optRun = "run" / "run REPL and backend tests" --? val optJvm = "jvm" / "run JVM backend tests" --? val optRes = "res" / "run resident compiler tests" --? val optScalap = "scalap" / "run scalap tests" --? diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index b4ba200511e5..d3e5f070eed9 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -129,8 +129,6 @@ package object partest { def fileSeparator = java.io.File.separator def pathSeparator = java.io.File.pathSeparator - def words(s: String): List[String] = (s.trim split "\\s+").toList - def timed[T](body: => T): (T, Long) = { val t1 = System.currentTimeMillis val result = body @@ -143,18 +141,6 @@ package object partest { def basename(name: String): String = Path(name).stripExtension - /** In order to allow for spaces in flags/options, this - * parses .flags, .javaopts, javacopts etc files as follows: - * If it is exactly one line, it is split (naively) on spaces. - * If it contains more than one line, each line is its own - * token, spaces and all. - */ - def readOptionsFile(file: File): List[String] = - file.fileLines match { - case x :: Nil => words(x) - case xs => xs - } - def findProgram(name: String): Option[File] = { val pathDirs = sys.env("PATH") match { case null => List("/usr/local/bin", "/usr/bin", "/bin") diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala index 2fc29f0bb382..d59241927674 100644 --- a/src/reflect/scala/reflect/api/Types.scala +++ b/src/reflect/scala/reflect/api/Types.scala @@ -137,7 +137,7 @@ trait Types { * Unlike `members` this method doesn't returns inherited members. * * Members in the returned scope might appear in arbitrary order. - * Use `declarations.sorted` to get an ordered list of members. + * Use `decls.sorted` to get an ordered list of members. */ def decls: MemberScope @@ -150,7 +150,7 @@ trait Types { * Unlike `declarations` this method also returns inherited members. * * Members in the returned scope might appear in arbitrary order. - * Use `declarations.sorted` to get an ordered list of members. + * Use `members.sorted` to get an ordered list of members. */ def members: MemberScope diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 4683e8dedfda..5eea4620f9d4 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -282,12 +282,22 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => // !!! when annotation arguments are not literals, but any sort of // expression, there is a fair chance they will turn up here not as // Literal(const) but some arbitrary AST. - def constantAtIndex(index: Int): Option[Constant] = - if (args.nonEmpty) argAtIndex(args, index) collect { - case Literal(x) => x - } else if (assocs.nonEmpty) argAtIndex(assocs, index) collect { + // + // We recurse over Typed / Annotated trees to allow things like: + // `@implicitNotFound("$foo": @nowarn)` + def constantAtIndex(index: Int): Option[Constant] = { + @tailrec + def lit(tree: Tree): Option[Constant] = tree match { + case Literal(c) => Some(c) + case Typed(t, _) => lit(t) + case Annotated(_, t) => lit(t) + case _ => None + } + if (args.nonEmpty) argAtIndex(args, index).flatMap(lit) + else if (assocs.nonEmpty) argAtIndex(assocs, index) collect { case (_, LiteralAnnotArg(const)) => const } else None + } def argAtIndex[T](l: List[T], index: Int): Option[T] = if (index < l.size) Some(l(index)) else None diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index b99f40770791..570a94e960ed 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -16,7 +16,7 @@ package internal // todo implement in terms of BitSet import scala.collection.mutable -import util.{Statistics, StatisticsStatics} +import util.Statistics /** A base type sequence (BaseTypeSeq) is an ordered sequence spanning all the base types * of a type. It characterized by the following two laws: @@ -50,8 +50,8 @@ trait BaseTypeSeqs { */ class BaseTypeSeq protected[reflect] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { self => - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(baseTypeSeqCount) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(baseTypeSeqLenTotal, elems.length) + if (settings.areStatisticsEnabled) statistics.incCounter(baseTypeSeqCount) + if (settings.areStatisticsEnabled) statistics.incCounter(baseTypeSeqLenTotal, elems.length) private[this] val typeSymbols = { val tmp = new Array[Int](elems.length) var i = 0 diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 94f0b464e67f..f6a8615e44d2 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -456,13 +456,13 @@ trait Definitions extends api.StandardDefinitions { else if (isScalaRepeatedParamType(tp)) elementExtract(RepeatedParamClass, tp) orElse tp else tp ) - def repeatedToSingle(tp: Type): Type = elementExtract(RepeatedParamClass, tp) orElse elementExtract(JavaRepeatedParamClass, tp) orElse tp + def repeatedToSingle(tp: Type): Type = elementExtract(RepeatedParamClass, tp) orElse elementExtract(JavaRepeatedParamClass, tp) orElse tp // We don't need to deal with JavaRepeatedParamClass here, as `repeatedToSeq` is only called in the patmat translation for Scala sources. - def repeatedToSeq(tp: Type): Type = elementTransform(RepeatedParamClass, tp)(seqType) orElse tp - def seqToRepeated(tp: Type): Type = elementTransform(SeqClass, tp)(scalaRepeatedType) orElse tp - def isReferenceArray(tp: Type) = elementTest(ArrayClass, tp)(elemtp => elemtp <:< AnyRefTpe || (elemtp eq ObjectTpeJava)) - def isArrayOfSymbol(tp: Type, elem: Symbol) = elementTest(ArrayClass, tp)(_.typeSymbol == elem) - def elementType(container: Symbol, tp: Type): Type = elementExtract(container, tp) + def repeatedToSeq(tp: Type): Type = elementTransform(RepeatedParamClass, tp)(seqType) orElse tp + def seqToRepeated(tp: Type): Type = elementTransform(SeqClass, tp)(scalaRepeatedType) orElse tp + def isReferenceArray(tp: Type) = elementTest(ArrayClass, tp)(elemtp => elemtp <:< AnyRefTpe || (elemtp eq ObjectTpeJava)) + def isArrayOfSymbol(tp: Type, elem: Symbol) = elementTest(ArrayClass, tp)(_.typeSymbol == elem) + def elementType(container: Symbol, tp: Type): Type = elementExtract(container, tp) // Classes treated specially with respect to -Ywarn-unused lazy val SubTypeClass = requiredClass[scala.<:<[_,_]] @@ -474,7 +474,7 @@ trait Definitions extends api.StandardDefinitions { lazy val IteratorClass = requiredClass[scala.collection.Iterator[_]] lazy val IterableClass = requiredClass[scala.collection.Iterable[_]] lazy val ListClass = requiredClass[scala.collection.immutable.List[_]] - def List_cons = getMemberMethod(ListClass, nme.CONS) + def List_cons = getMemberMethod(ListClass, nme.CONS) @migration("SeqClass now refers to scala.collection.immutable.Seq", "2.13.0") lazy val SeqClass = requiredClass[scala.collection.immutable.Seq[_]] lazy val SeqFactoryClass = requiredModule[scala.collection.SeqFactory.type] @@ -640,8 +640,7 @@ trait Definitions extends api.StandardDefinitions { case _ => false }) // The given class has a main method. - def hasJavaMainMethod(sym: Symbol): Boolean = - (sym.tpe member nme.main).alternatives exists isJavaMainMethod + def hasJavaMainMethod(sym: Symbol): Boolean = sym.tpe.member(nme.main).alternatives.exists(isJavaMainMethod) class VarArityClass(name: String, maxArity: Int, countFrom: Int = 0, init: Option[ClassSymbol] = None) extends VarArityClassApi { private[this] val offset = countFrom - init.size @@ -995,7 +994,6 @@ trait Definitions extends api.StandardDefinitions { (sym eq PartialFunctionClass) || (sym eq AbstractPartialFunctionClass) } - private[this] val doSam = settings.isScala212 private[this] val samCache = perRunCaches.newAnyRefMap[Symbol, Symbol]() /** The single abstract method declared by type `tp` (or `NoSymbol` if it cannot be found). * @@ -1008,7 +1006,7 @@ trait Definitions extends api.StandardDefinitions { * It's kind of strange that erasure sees deferredMembers that typer does not (see commented out assert below) */ def samOf(tp: Type): Symbol = - if (doSam && isNonRefinementClassType(unwrapToClass(tp))) { // TODO: is this really faster than computing tpSym below? how about just `tp.typeSymbol.isClass` (and !tpSym.isRefinementClass)? + if (isNonRefinementClassType(unwrapToClass(tp))) { // TODO: is this really faster than computing tpSym below? how about just `tp.typeSymbol.isClass` (and !tpSym.isRefinementClass)? // look at erased type because we (only) care about what ends up in bytecode // (e.g., an alias type is fine as long as is compiles to a single-abstract-method) val tpSym: Symbol = erasure.javaErasure(tp).typeSymbol @@ -1783,6 +1781,19 @@ trait Definitions extends api.StandardDefinitions { lazy val ensureAccessibleMethod = getMemberMethod(ScalaRunTimeModule, nme.ensureAccessible) lazy val arrayClassMethod = getMemberMethod(ScalaRunTimeModule, nme.arrayClass) lazy val wrapVarargsRefArrayMethod = getMemberMethod(ScalaRunTimeModule, nme.wrapRefArray) + lazy val genericWrapVarargsRefArrayMethod = getMemberMethod(ScalaRunTimeModule, nme.genericWrapArray) + lazy val primitiveWrapArrayMethod = Seq[Symbol]( + getMemberMethod(ScalaRunTimeModule, nme.wrapBooleanArray), + getMemberMethod(ScalaRunTimeModule, nme.wrapByteArray), + getMemberMethod(ScalaRunTimeModule, nme.wrapCharArray), + getMemberMethod(ScalaRunTimeModule, nme.wrapIntArray), + getMemberMethod(ScalaRunTimeModule, nme.wrapDoubleArray), + getMemberMethod(ScalaRunTimeModule, nme.wrapFloatArray), + getMemberMethod(ScalaRunTimeModule, nme.wrapLongArray), + getMemberMethod(ScalaRunTimeModule, nme.wrapShortArray), + getMemberMethod(ScalaRunTimeModule, nme.wrapUnitArray) + ) + lazy val RuntimeStatics_ioobe = getMemberMethod(RuntimeStaticsModule, nme.ioobe) diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index d366c7fce8fb..3df0c63373c3 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -80,7 +80,7 @@ package internal // 57: notOVERRIDE // 58: notPRIVATE // 59: -// 60: +// 60: SCALA3X // 61: // 62: // 63: @@ -113,6 +113,7 @@ class ModifierFlags { final val LOCAL = 1L << 19 // symbol is local to current class (i.e. private[this] or protected[this] // pre: PRIVATE or PROTECTED are also set final val JAVA = 1L << 20 // symbol was defined by a Java class + final val SCALA3X = 1L << 60 // class was defined in Scala 3 final val STATIC = 1L << 23 // static field, method or class final val CASEACCESSOR = 1L << 24 // symbol is a case parameter (or its accessor, or a GADT skolem) final val TRAIT = 1L << 25 // symbol is a trait @@ -202,7 +203,7 @@ class Flags extends ModifierFlags { // The flags (1L << 59) to (1L << 63) are currently unused. If added to the InitialFlags mask, // they could be used as normal flags. - final val InitialFlags = 0x0007FFFFFFFFFFFFL // normal flags, enabled from the first phase: 1L to (1L << 50) + final val InitialFlags = 0x1007FFFFFFFFFFFFL // normal flags, enabled from the first phase: 1L to (1L << 50) + (1L << 60) final val LateFlags = 0x00F8000000000000L // flags that override flags in (1L << 4) to (1L << 8): DEFERRED, FINAL, INTERFACE, METHOD, MODULE final val AntiFlags = 0x0700000000000000L // flags that cancel flags in 1L to (1L << 2): PROTECTED, OVERRIDE, PRIVATE final val LateShift = 47 @@ -320,7 +321,7 @@ class Flags extends ModifierFlags { /** These flags are not pickled */ - final val FlagsNotPickled = IS_ERROR | OVERLOADED | LIFTED | TRANS_FLAG | LOCKED | TRIEDCOOKING + final val FlagsNotPickled = IS_ERROR | OVERLOADED | LIFTED | TRANS_FLAG | LOCKED | TRIEDCOOKING | SCALA3X // A precaution against future additions to FlagsNotPickled turning out // to be overloaded flags thus not-pickling more than intended. @@ -477,8 +478,8 @@ class Flags extends ModifierFlags { case `notPROTECTED` => "" // (1L << 56) case 0x200000000000000L => "" // (1L << 57) case `notPRIVATE` => "" // (1L << 58) - case NEEDS_TREES => "" // (1L << 59) - case 0x1000000000000000L => "" // (1L << 60) + case NEEDS_TREES => "" // (1L << 59) + case SCALA3X => "" // (1L << 60) case 0x2000000000000000L => "" // (1L << 61) case 0x4000000000000000L => "" // (1L << 62) case 0x8000000000000000L => "" // (1L << 63) diff --git a/src/reflect/scala/reflect/internal/Kinds.scala b/src/reflect/scala/reflect/internal/Kinds.scala index 698be6563c5c..d53da5a4ca37 100644 --- a/src/reflect/scala/reflect/internal/Kinds.scala +++ b/src/reflect/scala/reflect/internal/Kinds.scala @@ -152,7 +152,7 @@ trait Kinds { def kindCheck(cond: Boolean, f: KindErrors => KindErrors): Unit = if (!cond) kindErrors = f(kindErrors) - if (settings.debug) { + if (settings.isDebug) { log("checkKindBoundsHK expected: "+ param +" with params "+ hkparams +" by definition in "+ paramOwner) log("checkKindBoundsHK supplied: "+ arg +" with params "+ hkargs +" from "+ argOwner) log("checkKindBoundsHK under params: "+ underHKParams +" with args "+ withHKArgs) @@ -215,7 +215,7 @@ trait Kinds { else NoKindErrors } - if (settings.debug && (tparams.nonEmpty || targs.nonEmpty)) log( + if (settings.isDebug && (tparams.nonEmpty || targs.nonEmpty)) log( "checkKindBounds0(" + tparams + ", " + targs + ", " + pre + ", " + owner + ", " + explainErrors + ")" ) diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index e7d434ca3a95..4099423cbed8 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -61,7 +61,7 @@ trait Mirrors extends api.Mirrors { val result = if (name.isTermName) sym.suchThat(_ hasFlag MODULE) else sym if (result != NoSymbol) result else { - if (settings.debug) { log(sym.info); log(sym.info.members) }//debug + if (settings.isDebug) { log(sym.info); log(sym.info.members) }//debug thisMirror.missingHook(owner, name) orElse { MissingRequirementError.notFound((if (name.isTermName) "object " else "class ")+path+" in "+thisMirror) } diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala index 54183d7f3867..bfc995d96cc9 100644 --- a/src/reflect/scala/reflect/internal/Positions.scala +++ b/src/reflect/scala/reflect/internal/Positions.scala @@ -345,7 +345,8 @@ trait Positions extends api.Positions { self: SymbolTable => if (t.pos includes pos) { if (isEligible(t)) last = t super.traverse(t) - } else t match { + } + t match { case mdef: MemberDef => val annTrees = mdef.mods.annotations match { case Nil if mdef.symbol != null => diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index f869bd121981..8d62aea85931 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -207,7 +207,7 @@ trait Printers extends api.Printers { self: SymbolTable => ) def printFlags(flags: Long, privateWithin: String) = { - val mask: Long = if (settings.debug) -1L else PrintableFlags + val mask: Long = if (settings.isDebug) -1L else PrintableFlags val s = flagsToString(flags & mask, privateWithin) if (s != "") print(s + " ") } @@ -320,7 +320,7 @@ trait Printers extends api.Printers { self: SymbolTable => if (qual.nonEmpty || (checkSymbol && tree.symbol != NoSymbol)) print(resultName + ".") print("super") if (mix.nonEmpty) print(s"[$mix]") - else if (settings.debug) tree.tpe match { + else if (settings.isDebug) tree.tpe match { case st: SuperType => print(s"[${st.supertpe}]") case tp: Type => print(s"[$tp]") case _ => @@ -479,7 +479,7 @@ trait Printers extends api.Printers { self: SymbolTable => case th @ This(qual) => printThis(th, symName(tree, qual)) - case Select(qual: New, name) if !settings.debug => + case Select(qual: New, name) if !settings.isDebug => print(qual) case Select(qualifier, name) => @@ -781,26 +781,30 @@ trait Printers extends api.Printers { self: SymbolTable => print("class ", printedName(name)) printTypeParams(tparams) - val build.SyntacticClassDef(_, _, _, ctorMods, vparamss, earlyDefs, parents, selfType, body) = cl: @unchecked - - // constructor's modifier - if (ctorMods.hasFlag(AccessFlags) || ctorMods.hasAccessBoundary) { - print(" ") - printModifiers(ctorMods, primaryCtorParam = false) - } + cl match { + case build.SyntacticClassDef(_, _, _, ctorMods, vparamss, earlyDefs, parents, selfType, body) => + // constructor's modifier + if (ctorMods.hasFlag(AccessFlags) || ctorMods.hasAccessBoundary) { + print(" ") + printModifiers(ctorMods, primaryCtorParam = false) + } - def printConstrParams(ts: List[ValDef]): Unit = { - parenthesize() { - printImplicitInParamsList(ts) - printSeq(ts)(printVParam(_, primaryCtorParam = true))(print(", ")) - } - } - // constructor's params processing (don't print single empty constructor param list) - vparamss match { - case Nil | List(Nil) if !mods.isCase && !ctorMods.hasFlag(AccessFlags) => - case _ => vparamss foreach printConstrParams + def printConstrParams(ts: List[ValDef]): Unit = { + parenthesize() { + printImplicitInParamsList(ts) + printSeq(ts)(printVParam(_, primaryCtorParam = true))(print(", ")) + } + } + // constructor's params processing (don't print single empty constructor param list) + vparamss match { + case Nil | List(Nil) if !mods.isCase && !ctorMods.hasFlag(AccessFlags) => + case _ => vparamss foreach printConstrParams + } + parents + case _ => + // Can get here with erroneous code, like `{@deprecatedName ` + Nil } - parents } // get trees without default classes and traits (when they are last) diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index e428747db7cb..f0bdf01331a7 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -17,7 +17,7 @@ package internal import scala.annotation.tailrec import scala.collection.{AbstractIterable, AbstractIterator} import scala.collection.mutable.Clearable -import scala.reflect.internal.util.{Statistics, StatisticsStatics} +import scala.reflect.internal.util.Statistics trait Scopes extends api.Scopes { self: SymbolTable => @@ -515,22 +515,22 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** Create a new scope nested in another one with which it shares its elements */ final def newNestedScope(outer: Scope): Scope = { - val startTime = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.scopePopulationTime) else null + val startTime = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.scopePopulationTime) else null val nested = newScope // not `new Scope`, we must allow the runtime reflection universe to mixin SynchronizedScopes! nested.elems = outer.elems nested.nestinglevel = outer.nestinglevel + 1 if (outer.hashtable ne null) nested.hashtable = java.util.Arrays.copyOf(outer.hashtable, outer.hashtable.length) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.scopePopulationTime, startTime) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.scopePopulationTime, startTime) nested } /** Create a new scope with given initial elements */ def newScopeWith(elems: Symbol*): Scope = { - val startTime = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.startTimer(statistics.scopePopulationTime) else null + val startTime = if (settings.areStatisticsEnabled) statistics.startTimer(statistics.scopePopulationTime) else null val scope = newScope elems foreach scope.enter - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.stopTimer(statistics.scopePopulationTime, startTime) + if (settings.areStatisticsEnabled) statistics.stopTimer(statistics.scopePopulationTime, startTime) scope } diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 8f820ae11d0c..0c8af3b7601f 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -116,7 +116,7 @@ trait StdAttachments { */ case object KnownDirectSubclassesCalled extends PlainAttachment - class DottyEnumSingleton(val name: String) extends PlainAttachment + case object DottyEnumSingleton extends PlainAttachment class DottyParameterisedTrait(val params: List[Symbol]) diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 44b945dbfa2c..3d944af6c26d 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -322,6 +322,9 @@ trait StdNames { final val scala_ : NameType = nameType("scala") + // Scala 3 special type + val AND: NameType = nme.AND.toTypeName + def dropSingletonName(name: Name): TypeName = (name dropRight SINGLETON_SUFFIX.length).toTypeName def singletonName(name: Name): TypeName = (name append SINGLETON_SUFFIX).toTypeName } @@ -391,7 +394,7 @@ trait StdNames { val OUTER: NameType = nameType("$outer") val OUTER_LOCAL: NameType = OUTER.localName val OUTER_ARG: NameType = nameType("arg" + OUTER) - val OUTER_SYNTH: NameType = nameType("") // emitted by virtual pattern matcher, replaced by outer accessor in explicitouter + val OUTER_SYNTH: NameType = nameType("") // emitted by pattern matcher, replaced by outer accessor in explicitouter val ROOTPKG: NameType = nameType("_root_") val SELECTOR_DUMMY: NameType = nameType("") val SELF: NameType = nameType(s"$$this") @@ -658,6 +661,13 @@ trait StdNames { val long2Long: NameType = nameType("long2Long") val boolean2Boolean: NameType = nameType("boolean2Boolean") + // Scala 3 import syntax + val as: NameType = nameType("as") + + // Scala 3 soft keywords + val infix: NameType = nameType("infix") + val open: NameType = nameType("open") + // Compiler utilized names val AnnotatedType: NameType = nameType("AnnotatedType") @@ -960,6 +970,7 @@ trait StdNames { final val PLUS : NameType = nameType("+") final val STAR : NameType = nameType("*") final val TILDE: NameType = nameType("~") + final val QMARK: NameType = nameType("?") final val isUnary: Set[Name] = Set(MINUS, PLUS, TILDE, BANG) } diff --git a/src/reflect/scala/reflect/internal/SymbolPairs.scala b/src/reflect/scala/reflect/internal/SymbolPairs.scala index 83a3d8abca22..7d2f1f895550 100644 --- a/src/reflect/scala/reflect/internal/SymbolPairs.scala +++ b/src/reflect/scala/reflect/internal/SymbolPairs.scala @@ -43,12 +43,12 @@ abstract class SymbolPairs { def rootType: Type = self def lowType: Type = self memberType low - def lowErased: Type = erasure.specialErasure(base)(low.tpe) + def lowErased: Type = erasure.specialErasure(low)(low.tpe) def lowClassBound: Type = classBoundAsSeen(low.tpe.typeSymbol) def highType: Type = self memberType high def highInfo: Type = self memberInfo high - def highErased: Type = erasure.specialErasure(base)(high.tpe) + def highErased: Type = erasure.specialErasure(high)(high.tpe) def highClassBound: Type = classBoundAsSeen(high.tpe.typeSymbol) def isErroneous = low.tpe.isErroneous || high.tpe.isErroneous diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 9c7abb1e1524..3113062c5b51 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -87,15 +87,16 @@ abstract class SymbolTable extends macros.Universe def shouldLogAtThisPhase = false def isPastTyper = false - final def isDeveloper: Boolean = settings.debug.value || settings.developer.value - def picklerPhase: Phase + @inline final def isDeveloper: Boolean = settings.isDebug || settings.isDeveloper + + def picklerPhase: Phase def erasurePhase: Phase def settings: MutableSettings /** Override with final implementation for inlining. */ - def debuglog(msg: => String): Unit = if (settings.debug) log(msg) + def debuglog(msg: => String): Unit = if (settings.isDebug) log(msg) /** dev-warns if dev-warning is enabled and `cond` is true; no-op otherwise */ @inline final def devWarningIf(cond: => Boolean)(msg: => String): Unit = diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 3c683fb96df1..4f4ad17caf1f 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -20,7 +20,7 @@ package internal import scala.collection.immutable import scala.collection.mutable.ListBuffer -import util.{ Statistics, shortClassOfInstance, StatisticsStatics } +import util.{ ReusableInstance, Statistics, shortClassOfInstance } import Flags._ import scala.annotation.tailrec import scala.reflect.io.{AbstractFile, NoAbstractFile} @@ -124,7 +124,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isJavaEnum: Boolean = hasJavaEnumFlag def isJavaAnnotation: Boolean = hasJavaAnnotationFlag def isStaticAnnotation: Boolean = - hasJavaAnnotationFlag || isNonBottomSubClass(StaticAnnotationClass) + hasJavaAnnotationFlag || isNonBottomSubClass(StaticAnnotationClass) && this != NowarnClass def newNestedSymbol(name: Name, pos: Position, newFlags: Long, isClass: Boolean): Symbol = name match { case n: TermName => newTermSymbol(n, pos, newFlags) @@ -292,7 +292,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def varianceString: String = variance.symbolicString override def flagMask = - if (settings.debug && !isAbstractType) AllFlags + if (settings.isDebug && !isAbstractType) AllFlags else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE else ExplicitFlags @@ -970,6 +970,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isModuleVar = hasFlag(MODULEVAR) + final def isScala3Defined = hasFlag(SCALA3X) + /** * Is this symbol static (i.e. with no outer instance)? * Q: When exactly is a sym marked as STATIC? @@ -2724,7 +2726,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => symbolKind.abbreviation final def kindString: String = - if (settings.debug.value) accurateKindString + if (settings.isDebug) accurateKindString else sanitizedKindString /** If the name of the symbol's owner should be used when you care about @@ -2748,7 +2750,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If settings.Yshowsymkinds, adds abbreviated symbol kind. */ def nameString: String = { - val name_s = if (settings.debug.value) "" + unexpandedName else unexpandedName.dropLocal.decode + val name_s = if (settings.isDebug) "" + unexpandedName else unexpandedName.dropLocal.decode val kind_s = if (settings.Yshowsymkinds.value) "#" + abbreviatedKindString else "" name_s + idString + kind_s @@ -2775,7 +2777,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If hasMeaninglessName is true, uses the owner's name to disambiguate identity. */ override def toString: String = { - val simplifyNames = !settings.debug + val simplifyNames = !settings.isDebug if (isPackageObjectOrClass && simplifyNames) s"package object ${owner.decodedName}" else { val kind = kindString @@ -2811,7 +2813,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isStructuralThisType = owner.isInitialized && owner.isStructuralRefinement && tp == owner.tpe // scala/bug#8158 // colon+space, preceded by an extra space if needed to prevent the colon glomming onto a symbolic name def postnominalColon: String = if (!followsParens && name.isOperatorName) " : " else ": " - def parents = if (settings.debug) parentsString(tp.parents) else briefParentsString(tp.parents) + def parents = if (settings.isDebug) parentsString(tp.parents) else briefParentsString(tp.parents) def typeRest = if (isClass) " extends " + parents else if (isAliasType) " = " + tp.resultType @@ -2871,7 +2873,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** String representation of existentially bound variable */ def existentialToString = - if (isSingletonExistential && !settings.debug.value) + if (isSingletonExistential && !settings.isDebug) "val " + tpnme.dropSingletonName(name) + ": " + dropSingletonType(info.upperBound) else defString } @@ -3278,7 +3280,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => * info for T in Test1 should be >: Nothing <: Test3[_] */ - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(typeSymbolCount) + if (settings.areStatisticsEnabled) statistics.incCounter(typeSymbolCount) } implicit val TypeSymbolTag = ClassTag[TypeSymbol](classOf[TypeSymbol]) @@ -3328,7 +3330,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => owner.newTypeSkolemSymbol(name, origin, pos, newFlags) override def nameString: String = - if (settings.debug.value) (super.nameString + "&" + level) + if ((settings.isDebug)) (super.nameString + "&" + level) else super.nameString } @@ -3498,7 +3500,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else super.toString ) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(classSymbolCount) + if (settings.areStatisticsEnabled) statistics.incCounter(classSymbolCount) } implicit val ClassSymbolTag = ClassTag[ClassSymbol](classOf[ClassSymbol]) @@ -3508,7 +3510,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ class ModuleClassSymbol protected[Symbols] (owner: Symbol, pos: Position, name: TypeName) extends ClassSymbol(owner, pos, name) { - private[this] var module: Symbol = _ + private[this] var moduleSymbol: Symbol = _ private[this] var typeOfThisCache: Type = _ private[this] var typeOfThisPeriod = NoPeriod @@ -3541,8 +3543,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => implicitMembersCacheValue } // The null check seems to be necessary for the reifier. - override def sourceModule = if (module ne null) module else companionModule - override def sourceModule_=(module: Symbol): Unit = { this.module = module } + override def sourceModule = if (moduleSymbol ne null) moduleSymbol else companionModule + override def sourceModule_=(module: Symbol): Unit = { this.moduleSymbol = module } } class PackageObjectClassSymbol protected[Symbols] (owner0: Symbol, pos0: Position) @@ -3597,7 +3599,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // Avoid issuing lots of redundant errors if (!hasFlag(IS_ERROR)) { globalError(pos, missingMessage) - if (settings.debug.value) + if (settings.isDebug) (new Throwable).printStackTrace this setFlag IS_ERROR @@ -3696,7 +3698,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (syms.isEmpty) Nil else { val syms1 = mapList(syms)(symFn) - val map = new SubstSymMap(syms, syms1) + val map = SubstSymMap(syms, syms1) syms1.foreach(_.modifyInfo(map)) syms1 } @@ -3760,7 +3762,19 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Convenience functions which derive symbols by cloning. */ def cloneSymbols(syms: List[Symbol]): List[Symbol] = - deriveSymbols(syms, _.cloneSymbol) + if (syms.isEmpty) Nil + else { + val syms1 = mapList(syms)(_.cloneSymbol) + cloneSymbolsSubstSymMap.using { (msm: SubstSymMap) => + msm.reset(syms, syms1) + syms1.foreach(_.modifyInfo(msm)) + } + syms1 + } + + private[this] val cloneSymbolsSubstSymMap: ReusableInstance[SubstSymMap] = + ReusableInstance[SubstSymMap](SubstSymMap(), enabled = isCompilerUniverse) + def cloneSymbolsAtOwner(syms: List[Symbol], owner: Symbol): List[Symbol] = deriveSymbols(syms, _ cloneSymbol owner) @@ -3814,7 +3828,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** An exception for cyclic references of symbol definitions */ case class CyclicReference(sym: Symbol, info: Type) extends TypeError("illegal cyclic reference involving " + sym) { - if (settings.debug) printStackTrace() + if (settings.isDebug) printStackTrace() } /** A class for type histories */ diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index d1138cc510fc..6ae62eb81581 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -754,9 +754,10 @@ abstract class TreeGen { propagateNoWarnAttachment(from, to).updateAttachment(PatVarDefAttachment) /** Create tree for pattern definition */ - def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = matchVarPattern(pat) match { + def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = mkPatDef(mods, pat, rhs, rhs.pos)(fresh) + def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree, rhsPos: Position)(implicit fresh: FreshNameCreator): List[ValDef] = matchVarPattern(pat) match { case Some((name, tpt)) => - List(atPos(pat.pos union rhs.pos) { + List(atPos(pat.pos union rhsPos) { propagateNoWarnAttachment(pat, ValDef(mods, name.toTermName, tpt, rhs)) }) @@ -784,13 +785,13 @@ abstract class TreeGen { case Typed(expr, tpt) if !expr.isInstanceOf[Ident] => val rhsTypedUnchecked = if (tpt.isEmpty) rhsUnchecked - else Typed(rhsUnchecked, tpt) setPos (rhs.pos union tpt.pos) + else Typed(rhsUnchecked, tpt) setPos (rhsPos union tpt.pos) (expr, rhsTypedUnchecked) case ok => (ok, rhsUnchecked) } val vars = getVariables(pat1) - val matchExpr = atPos((pat1.pos union rhs.pos).makeTransparent) { + val matchExpr = atPos((pat1.pos union rhsPos).makeTransparent) { Match( rhs1, List( @@ -801,7 +802,7 @@ abstract class TreeGen { } vars match { case List((vname, tpt, pos, original)) => - List(atPos(pat.pos union pos union rhs.pos) { + List(atPos(pat.pos union pos union rhsPos) { propagatePatVarDefAttachments(original, ValDef(mods, vname.toTermName, tpt, matchExpr)) }) case _ => diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 47945ed0eed2..821aebd7084b 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1467,7 +1467,7 @@ trait Trees extends api.Trees { private def requireLegal(value: Any, allowed: Any, what: String) = ( if (value != allowed) { log(s"can't set $what for $self to value other than $allowed") - if (settings.debug && settings.developer) + if (settings.isDebug && settings.isDeveloper) (new Throwable).printStackTrace } ) @@ -1745,7 +1745,7 @@ trait Trees extends api.Trees { lazy val EmptyTreeTypeSubstituter = new TreeTypeSubstituter(List(), List()) - class TreeSymSubstTraverser(val from: List[Symbol], val to: List[Symbol]) extends TypeMapTreeSubstituter(new SubstSymMap(from, to)) { + class TreeSymSubstTraverser(val from: List[Symbol], val to: List[Symbol]) extends TypeMapTreeSubstituter(SubstSymMap(from, to)) { override def toString() = "TreeSymSubstTraverser/" + substituterString("Symbol", "Symbol", from, to) } @@ -1759,7 +1759,7 @@ trait Trees extends api.Trees { * a symbol in `from` will have a new type assigned. */ class TreeSymSubstituter(from: List[Symbol], to: List[Symbol]) extends InternalTransformer { - val symSubst = new SubstSymMap(from, to) + val symSubst = SubstSymMap(from, to) private[this] var mutatedSymbols: List[Symbol] = Nil override def transform(tree: Tree): Tree = { @tailrec diff --git a/src/reflect/scala/reflect/internal/TypeDebugging.scala b/src/reflect/scala/reflect/internal/TypeDebugging.scala index 550bd11bb433..2be3f520345a 100644 --- a/src/reflect/scala/reflect/internal/TypeDebugging.scala +++ b/src/reflect/scala/reflect/internal/TypeDebugging.scala @@ -63,24 +63,7 @@ trait TypeDebugging { /** Light color wrappers. */ - object typeDebug { - import scala.io.AnsiColor._ - - private[this] val colorsOk = scala.util.Properties.coloredOutputEnabled - private def inColor(s: String, color: String) = if (colorsOk && s != "") color + s + RESET else s - private def inBold(s: String, color: String) = if (colorsOk && s != "") color + BOLD + s + RESET else s - - def inLightRed(s: String) = inColor(s, RED) - def inLightGreen(s: String) = inColor(s, GREEN) - def inLightMagenta(s: String) = inColor(s, MAGENTA) - def inLightCyan(s: String): String = inColor(s, CYAN) - def inGreen(s: String): String = inBold(s, GREEN) - def inRed(s: String): String = inBold(s, RED) - def inBlue(s: String): String = inBold(s, BLUE) - def inCyan(s: String): String = inBold(s, CYAN) - def inMagenta(s: String) = inBold(s, MAGENTA) - def resetColor(s: String): String = if (colorsOk) s + RESET else s - + object typeDebug extends TypeDebugging.AnsiColor { private def to_s(x: Any): String = x match { // otherwise case classes are caught looking like products case _: Tree | _: Type => "" + x @@ -160,3 +143,33 @@ trait TypeDebugging { def typeParamsString(tp: Type) = typeDebug.str brackets (tp.typeParams map (_.defString)) def debugString(tp: Type) = typeDebug debugString tp } + +object TypeDebugging { + object AnsiColor extends AnsiColor { + implicit class StringColorOps(private val s: String) extends AnyVal { + def red = inLightRed(s) + def green = inLightGreen(s) + def yellow = inLightYellow(s) + def blue = inLightBlue(s) + } + } + + trait AnsiColor extends scala.io.AnsiColor { + private[this] val colorsOk = scala.util.Properties.coloredOutputEnabled + private def inColor(s: String, color: String) = if (colorsOk && s != "") color + s + RESET else s + private def inBold(s: String, color: String) = if (colorsOk && s != "") color + BOLD + s + RESET else s + + def inLightRed(s: String) = inColor(s, RED) + def inLightBlue(s: String) = inColor(s, BLUE) + def inLightGreen(s: String) = inColor(s, GREEN) + def inLightYellow(s: String): String = inColor(s, YELLOW) + def inLightMagenta(s: String) = inColor(s, MAGENTA) + def inLightCyan(s: String): String = inColor(s, CYAN) + def inGreen(s: String): String = inBold(s, GREEN) + def inRed(s: String): String = inBold(s, RED) + def inBlue(s: String): String = inBold(s, BLUE) + def inCyan(s: String): String = inBold(s, CYAN) + def inMagenta(s: String) = inBold(s, MAGENTA) + def resetColor(s: String): String = if (colorsOk) s + RESET else s + } +} diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 29a5177e9675..1cefcf355dfd 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -22,7 +22,7 @@ import mutable.{ListBuffer, LinkedHashSet} import Flags._ import scala.util.control.ControlThrowable import scala.annotation.{tailrec, unused} -import util.{Statistics, StatisticsStatics} +import util.{ReusableInstance, Statistics} import util.ThreeValues._ import Variance._ import Depth._ @@ -692,7 +692,7 @@ trait Types * = Int */ def asSeenFrom(pre: Type, clazz: Symbol): Type = { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, asSeenFromNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, asSeenFromNanos) else null try { val trivial = ( this.isTrivial @@ -708,7 +708,7 @@ trait Types if (m.capturedSkolems.isEmpty) tp1 else deriveType(m.capturedSkolems, _.cloneSymbol setFlag CAPTURED)(tp1) } - } finally if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + } finally if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) } /** The info of `sym`, seen as a member of this type. @@ -754,7 +754,7 @@ trait Types */ def substSym(from: List[Symbol], to: List[Symbol]): Type = if ((from eq to) || from.isEmpty) this - else new SubstSymMap(from, to) apply this + else SubstSymMap(from, to).apply(this) /** Substitute all occurrences of `ThisType(from)` in this type by `to`. * @@ -814,7 +814,7 @@ trait Types /** Is this type a subtype of that type? */ def <:<(that: Type): Boolean = { - if (StatisticsStatics.areSomeColdStatsEnabled) stat_<:<(that) + if (settings.areStatisticsEnabled) stat_<:<(that) else { (this eq that) || (if (explainSwitch) explain("<:", isSubType(_: Type, _: Type), this, that) @@ -846,26 +846,26 @@ trait Types }) def stat_<:<(that: Type): Boolean = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(subtypeCount) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, subtypeNanos) else null + if (settings.areStatisticsEnabled) statistics.incCounter(subtypeCount) + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, subtypeNanos) else null val result = (this eq that) || (if (explainSwitch) explain("<:", isSubType(_: Type, _: Type), this, that) else isSubType(this, that)) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) result } /** Is this type a weak subtype of that type? True also for numeric types, i.e. Int weak_<:< Long. */ def weak_<:<(that: Type): Boolean = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(subtypeCount) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, subtypeNanos) else null + if (settings.areStatisticsEnabled) statistics.incCounter(subtypeCount) + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, subtypeNanos) else null val result = ((this eq that) || (if (explainSwitch) explain("weak_<:", isWeakSubType, this, that) else isWeakSubType(this, that))) - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) result } @@ -1411,7 +1411,7 @@ trait Types override def underlying: Type = sym.typeOfThis override def isHigherKinded = sym.isRefinementClass && underlying.isHigherKinded override def prefixString = - if (settings.debug) sym.nameString + ".this." + if (settings.isDebug) sym.nameString + ".this." else if (sym.isAnonOrRefinementClass) "this." else if (sym.isOmittablePrefix) "" else if (sym.isModuleClass) sym.fullNameString + "." @@ -1689,7 +1689,7 @@ trait Types override def isStructuralRefinement: Boolean = typeSymbol.isAnonOrRefinementClass && (decls exists symbolIsPossibleInRefinement) - protected def shouldForceScope = settings.debug || parents.isEmpty || !decls.isEmpty + protected def shouldForceScope = settings.isDebug || parents.isEmpty || !decls.isEmpty protected def initDecls = fullyInitializeScope(decls) protected def scopeString = if (shouldForceScope) initDecls.mkString("{", "; ", "}") else "" override def safeToString = parentsString(parents) + scopeString @@ -1760,8 +1760,8 @@ trait Types tpe.baseTypeSeqCache = tpWithoutTypeVars.baseTypeSeq lateMap paramToVar } else { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(compoundBaseTypeSeqCount) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) else null + if (settings.areStatisticsEnabled) statistics.incCounter(compoundBaseTypeSeqCount) + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) else null try { tpe.baseTypeSeqCache = undetBaseTypeSeq tpe.baseTypeSeqCache = @@ -1770,7 +1770,7 @@ trait Types else compoundBaseTypeSeq(tpe) } finally { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) } // [Martin] suppressing memoization solves the problem with "same type after erasure" errors // when compiling with @@ -1793,13 +1793,13 @@ trait Types if (period != currentPeriod) { tpe.baseClassesPeriod = currentPeriod if (!isValidForBaseClasses(period)) { - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, baseClassesNanos) else null + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, baseClassesNanos) else null try { tpe.baseClassesCache = null tpe.baseClassesCache = tpe.memo(computeBaseClasses(tpe))(tpe.typeSymbol :: _.baseClasses.tail) } finally { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) } } } @@ -2056,7 +2056,7 @@ trait Types /** A nicely formatted string with newlines and such. */ def formattedToString = parents.mkString("\n with ") + scopeString - override protected def shouldForceScope = settings.debug || decls.size > 1 + override protected def shouldForceScope = settings.isDebug || decls.size > 1 override protected def scopeString = initDecls.mkString(" {\n ", "\n ", "\n}") override def safeToString = if (shouldForceScope) formattedToString else super.safeToString } @@ -2642,7 +2642,7 @@ trait Types } // ensure that symbol is not a local copy with a name coincidence private def needsPreString = ( - settings.debug + settings.isDebug || !shorthands(sym.fullName) || (sym.ownersIterator exists (s => !s.isClass)) ) @@ -2713,12 +2713,12 @@ trait Types case _ => "" } override def safeToString = { - val custom = if (settings.debug) "" else customToString + val custom = if (settings.isDebug) "" else customToString if (custom != "") custom else finishPrefix(preString + sym.nameString + argsString) } override def prefixString = "" + ( - if (settings.debug) + if (settings.isDebug) super.prefixString else if (sym.isOmittablePrefix) "" @@ -2796,13 +2796,13 @@ trait Types if (period != currentPeriod) { tpe.baseTypeSeqPeriod = currentPeriod if (!isValidForBaseClasses(period)) { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(typerefBaseTypeSeqCount) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) else null + if (settings.areStatisticsEnabled) statistics.incCounter(typerefBaseTypeSeqCount) + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) else null try { tpe.baseTypeSeqCache = undetBaseTypeSeq tpe.baseTypeSeqCache = tpe.baseTypeSeqImpl } finally { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) } } } @@ -3152,7 +3152,7 @@ trait Types } override def nameAndArgsString: String = underlying match { - case TypeRef(_, sym, args) if !settings.debug && isRepresentableWithWildcards => + case TypeRef(_, sym, args) if !settings.isDebug && isRepresentableWithWildcards => sym.name.toString + wildcardArgsString(quantified.toSet, args).mkString("[", ",", "]") case TypeRef(_, sym, args) => sym.name.toString + args.mkString("[", ",", "]") + existentialClauses @@ -3192,7 +3192,7 @@ trait Types } override def safeToString: String = underlying match { - case TypeRef(pre, sym, args) if !settings.debug && isRepresentableWithWildcards => + case TypeRef(pre, sym, args) if !settings.isDebug && isRepresentableWithWildcards => val ref = typeRef(pre, sym, Nil).toString val wildcards = wildcardArgsString(quantified.toSet, args) if (wildcards.isEmpty) ref else ref + wildcards.mkString("[", ", ", "]") @@ -3620,7 +3620,7 @@ trait Types // This is a higher-kinded type var with same arity as tp. // If so (see scala/bug#7517), side effect: adds the type constructor itself as a bound. isSubArgs(lhs, rhs, params, AnyDepth) && {addBound(tp.typeConstructor); true} - } else if (settings.isScala213 && numCaptured > 0) { + } else if (numCaptured > 0) { // Simple algorithm as suggested by Paul Chiusano in the comments on scala/bug#2712 // // https://github.com/scala/bug/issues/2712#issuecomment-292374655 @@ -4041,6 +4041,9 @@ trait Types def refinedType(parents: List[Type], owner: Symbol): Type = refinedType(parents, owner, newScope, owner.pos) + private[this] val copyRefinedTypeSSM: ReusableInstance[SubstSymMap] = + ReusableInstance[SubstSymMap](SubstSymMap(), enabled = isCompilerUniverse) + def copyRefinedType(original: RefinedType, parents: List[Type], decls: Scope) = if ((parents eq original.parents) && (decls eq original.decls)) original else { @@ -4055,9 +4058,10 @@ trait Types val syms2 = result.decls.toList val resultThis = result.typeSymbol.thisType val substThisMap = new SubstThisMap(original.typeSymbol, resultThis) - val substMap = new SubstSymMap(syms1, syms2) - for (sym <- syms2) - sym.modifyInfo(info => substMap.apply(substThisMap.apply(info))) + copyRefinedTypeSSM.using { (msm: SubstSymMap) => + msm.reset(syms1, syms2) + syms2.foreach(_.modifyInfo(info => msm.apply(substThisMap.apply(info)))) + } } result } @@ -5192,7 +5196,7 @@ trait Types def this(msg: String) = this(NoPosition, msg) final override def fillInStackTrace() = - if (settings.debug) super.fillInStackTrace() else this + if (settings.isDebug) super.fillInStackTrace() else this } // TODO: RecoverableCyclicReference should be separated from TypeError, @@ -5200,7 +5204,7 @@ trait Types /** An exception for cyclic references from which we can recover */ case class RecoverableCyclicReference(sym: Symbol) extends TypeError("illegal cyclic reference involving " + sym) { - if (settings.debug) printStackTrace() + if (settings.isDebug) printStackTrace() } class NoCommonType(tps: List[Type]) extends ControlThrowable( diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index d8abf5b30c13..09f3e8009b98 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -58,8 +58,6 @@ abstract class UnPickler { class Scan(_bytes: Array[Byte], offset: Int, classRoot: ClassSymbol, moduleRoot: ModuleSymbol, filename: String) extends PickleBuffer(_bytes, offset, -1) { //println("unpickle " + classRoot + " and " + moduleRoot)//debug - protected def debug = settings.debug.value - checkVersion() private[this] val loadingMirror = mirrorThatLoaded(classRoot) diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index ca8c24d6e8d3..57c880f894c7 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -16,6 +16,8 @@ package scala package reflect.internal package settings +import scala.reflect.internal.util.StatisticsStatics + /** A mutable Settings object. */ abstract class MutableSettings extends AbsSettings { @@ -63,13 +65,17 @@ abstract class MutableSettings extends AbsSettings { def YstatisticsEnabled: BooleanSetting def Yrecursion: IntSetting - - def isScala212: Boolean - private[scala] def isScala213: Boolean } object MutableSettings { import scala.language.implicitConversions /** Support the common use case, `if (settings.debug) println("Hello, martin.")` */ @inline implicit def reflectSettingToBoolean(s: MutableSettings#BooleanSetting): Boolean = s.value + + implicit class SettingsOps(private val settings: MutableSettings) extends AnyVal { + @inline final def areStatisticsEnabled = (StatisticsStatics.COLD_STATS_GETTER.invokeExact(): Boolean) && settings.YstatisticsEnabled + @inline final def areHotStatisticsEnabled = (StatisticsStatics.HOT_STATS_GETTER.invokeExact(): Boolean) && settings.YhotStatisticsEnabled + @inline final def isDebug: Boolean = (StatisticsStatics.DEBUG_GETTER.invokeExact(): Boolean) && settings.debug + @inline final def isDeveloper: Boolean = (StatisticsStatics.DEVELOPER_GETTER.invokeExact(): Boolean) && settings.developer + } } diff --git a/src/reflect/scala/reflect/internal/tpe/FindMembers.scala b/src/reflect/scala/reflect/internal/tpe/FindMembers.scala index fa2ba469c276..7cc3f799430a 100644 --- a/src/reflect/scala/reflect/internal/tpe/FindMembers.scala +++ b/src/reflect/scala/reflect/internal/tpe/FindMembers.scala @@ -13,7 +13,7 @@ package scala.reflect.internal package tpe -import util.{ReusableInstance, StatisticsStatics} +import util.ReusableInstance import Flags._ import scala.runtime.Statics.releaseFence @@ -51,10 +51,10 @@ trait FindMembers { // Main entry point def apply(): T = { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(findMemberCount) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, findMemberNanos) else null + if (settings.areStatisticsEnabled) statistics.incCounter(findMemberCount) + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, findMemberNanos) else null try searchConcreteThenDeferred - finally if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + finally if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) } protected def result: T @@ -316,11 +316,11 @@ trait FindMembers { // Assemble the result from the hand-rolled ListBuffer protected def result: Symbol = if (members eq null) { if (member0 == NoSymbol) { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(noMemberCount) + if (settings.areStatisticsEnabled) statistics.incCounter(noMemberCount) NoSymbol } else member0 } else { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(multMemberCount) + if (settings.areStatisticsEnabled) statistics.incCounter(multMemberCount) lastM.next = Nil releaseFence() initBaseClasses.head.newOverloaded(tpe, members) diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala index 2e7b7a058b3e..ffb24459fce0 100644 --- a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala +++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala @@ -17,7 +17,6 @@ package tpe import scala.collection.mutable import scala.annotation.tailrec -import scala.reflect.internal.util.StatisticsStatics import Variance._ private[internal] trait GlbLubs { @@ -278,8 +277,8 @@ private[internal] trait GlbLubs { case Nil => NothingTpe case t :: Nil => t case _ => - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(lubCount) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, lubNanos) else null + if (settings.areStatisticsEnabled) statistics.incCounter(lubCount) + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, lubNanos) else null try { val res = lub(ts, lubDepth(ts)) // If the number of unapplied type parameters in all incoming @@ -297,7 +296,7 @@ private[internal] trait GlbLubs { finally { lubResults.clear() glbResults.clear() - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) } } @@ -396,7 +395,7 @@ private[internal] trait GlbLubs { // parameters are not handled correctly. val ok = ts forall { t => isSubType(t, lubRefined, depth.decr) || { - if (settings.debug || printLubs) { + if (settings.isDebug || printLubs) { Console.println( "Malformed lub: " + lubRefined + "\n" + "Argument " + t + " does not conform. Falling back to " + lubBase @@ -420,7 +419,7 @@ private[internal] trait GlbLubs { indent = indent + " " assert(indent.length <= 100, "LUB is highly indented") } - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(nestedLubCount) + if (settings.areStatisticsEnabled) statistics.incCounter(nestedLubCount) val res = lub0(ts) if (printLubs) { indent = indent stripSuffix " " @@ -445,14 +444,14 @@ private[internal] trait GlbLubs { case List() => AnyTpe case List(t) => t case ts0 => - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(lubCount) - val start = if (StatisticsStatics.areSomeColdStatsEnabled) statistics.pushTimer(typeOpsStack, lubNanos) else null + if (settings.areStatisticsEnabled) statistics.incCounter(lubCount) + val start = if (settings.areStatisticsEnabled) statistics.pushTimer(typeOpsStack, lubNanos) else null try { glbNorm(ts0, lubDepth(ts0)) } finally { lubResults.clear() glbResults.clear() - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.popTimer(typeOpsStack, start) + if (settings.areStatisticsEnabled) statistics.popTimer(typeOpsStack, start) } } @@ -575,7 +574,7 @@ private[internal] trait GlbLubs { } } // if (settings.debug.value) { println(indent + "glb of " + ts + " at depth "+depth); indent = indent + " " } //DEBUG - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(nestedLubCount) + if (settings.areStatisticsEnabled) statistics.incCounter(nestedLubCount) glb0(ts) // if (settings.debug.value) { indent = indent.substring(0, indent.length() - 2); log(indent + "glb of " + ts + " is " + res) }//DEBUG } diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index baaa6d4561c7..92357d0e0e19 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -18,7 +18,6 @@ package tpe import scala.collection.mutable import util.TriState import scala.annotation.tailrec -import scala.reflect.internal.util.StatisticsStatics trait TypeComparers { self: SymbolTable => @@ -66,7 +65,7 @@ trait TypeComparers { private def isSubPre(pre1: Type, pre2: Type, sym: Symbol) = if ((pre1 ne pre2) && (pre1 ne NoPrefix) && (pre2 ne NoPrefix) && pre1 <:< pre2) { - if (settings.debug) println(s"new isSubPre $sym: $pre1 <:< $pre2") + if (settings.isDebug) println(s"new isSubPre $sym: $pre1 <:< $pre2") true } else false @@ -104,7 +103,7 @@ trait TypeComparers { /** Do `tp1` and `tp2` denote equivalent types? */ def isSameType(tp1: Type, tp2: Type): Boolean = try { - if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(sametypeCount) + if (settings.areStatisticsEnabled) statistics.incCounter(sametypeCount) subsametypeRecursions += 1 //OPT cutdown on Function0 allocation //was: @@ -179,7 +178,7 @@ trait TypeComparers { sameLength(tparams1, tparams2) && { // corresponds does not check length of two sequences before checking the predicate, // but SubstMap assumes it has been checked (scala/bug#2956) - val substMap = new SubstSymMap(tparams2, tparams1) + val substMap = SubstSymMap(tparams2, tparams1) ( (tparams1 corresponds tparams2)((p1, p2) => methodHigherOrderTypeParamsSameVariance(p1, p2) && p1.info =:= substMap(p2.info)) && (res1 =:= substMap(res2)) @@ -358,8 +357,8 @@ trait TypeComparers { //@M for an example of why we need to generate fresh symbols otherwise, see neg/tcpoly_ticket2101.scala val substitutes = if (isMethod) tparams1 else cloneSymbols(tparams1) - val sub1: Type => Type = if (isMethod) (tp => tp) else new SubstSymMap(tparams1, substitutes) - val sub2: Type => Type = new SubstSymMap(tparams2, substitutes) + val sub1: Type => Type = if (isMethod) (tp => tp) else SubstSymMap(tparams1, substitutes) + val sub2: Type => Type = SubstSymMap(tparams2, substitutes) def cmp(p1: Symbol, p2: Symbol) = sub2(p2.info) <:< sub1(p1.info) (tparams1 corresponds tparams2)(cmp) && (sub1(res1) <:< sub2(res2)) @@ -404,7 +403,7 @@ trait TypeComparers { } def isSub(tp1: Type, tp2: Type) = - settings.isScala213 && isSubHKTypeVar(tp1, tp2) || + isSubHKTypeVar(tp1, tp2) || isSub2(tp1.normalize, tp2.normalize) // @M! normalize reduces higher-kinded typeref to PolyType def isSub2(ntp1: Type, ntp2: Type) = (ntp1, ntp2) match { diff --git a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala index 7af1bb9376a3..9376640a5d17 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala @@ -62,7 +62,7 @@ private[internal] trait TypeConstraints { } def clear(): Unit = { - if (settings.debug) + if (settings.isDebug) self.log("Clearing " + log.size + " entries from the undoLog.") log = Nil } diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index 5604e7d88e86..96684ffe9f3e 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -663,21 +663,46 @@ private[internal] trait TypeMaps { override def toString = s"AsSeenFromMap($seenFromPrefix, $seenFromClass)" } - /** A base class to compute all substitutions */ - abstract class SubstMap[T](from: List[Symbol], to: List[T]) extends TypeMap { - // OPT this check was 2-3% of some profiles, demoted to -Xdev - if (isDeveloper) assert(sameLength(from, to), "Unsound substitution from "+ from +" to "+ to) + /** A base class to compute all substitutions. */ + abstract class SubstMap[T >: Null](from0: List[Symbol], to0: List[T]) extends TypeMap { + private[this] var from: List[Symbol] = from0 + private[this] var to: List[T] = to0 private[this] var fromHasTermSymbol = false private[this] var fromMin = Int.MaxValue private[this] var fromMax = Int.MinValue private[this] var fromSize = 0 - from.foreach { - sym => - fromMin = math.min(fromMin, sym.id) - fromMax = math.max(fromMax, sym.id) - fromSize += 1 - if (sym.isTerm) fromHasTermSymbol = true + + // So SubstTypeMap can expose them publicly + // while SubstMap can continue to access them as private fields + protected[this] final def accessFrom: List[Symbol] = from + protected[this] final def accessTo: List[T] = to + + reset(from0, to0) + def reset(from0: List[Symbol], to0: List[T]): this.type = { + // OPT this check was 2-3% of some profiles, demoted to -Xdev + if (isDeveloper) assert(sameLength(from, to), "Unsound substitution from "+ from +" to "+ to) + + from = from0 + to = to0 + + fromHasTermSymbol = false + fromMin = Int.MaxValue + fromMax = Int.MinValue + fromSize = 0 + + def scanFrom(ss: List[Symbol]): Unit = + ss match { + case sym :: rest => + fromMin = math.min(fromMin, sym.id) + fromMax = math.max(fromMax, sym.id) + fromSize += 1 + if (sym.isTerm) fromHasTermSymbol = true + scanFrom(rest) + case _ => () + } + scanFrom(from) + this } /** Are `sym` and `sym1` the same? Can be tuned by subclasses. */ @@ -760,9 +785,12 @@ private[internal] trait TypeMaps { } /** A map to implement the `substSym` method. */ - class SubstSymMap(from: List[Symbol], to: List[Symbol]) extends SubstMap(from, to) { + class SubstSymMap(from0: List[Symbol], to0: List[Symbol]) extends SubstMap[Symbol](from0, to0) { def this(pairs: (Symbol, Symbol)*) = this(pairs.toList.map(_._1), pairs.toList.map(_._2)) + private[this] final def from: List[Symbol] = accessFrom + private[this] final def to: List[Symbol] = accessTo + protected def toType(fromTpe: Type, sym: Symbol) = fromTpe match { case TypeRef(pre, _, args) => copyTypeRef(fromTpe, pre, sym, args) case SingleType(pre, _) => singleType(pre, sym) @@ -821,9 +849,18 @@ private[internal] trait TypeMaps { mapTreeSymbols.transform(tree) } + object SubstSymMap { + def apply(): SubstSymMap = new SubstSymMap() + def apply(from: List[Symbol], to: List[Symbol]): SubstSymMap = new SubstSymMap(from, to) + def apply(fromto: (Symbol, Symbol)): SubstSymMap = new SubstSymMap(fromto) + } + /** A map to implement the `subst` method. */ - class SubstTypeMap(val from: List[Symbol], val to: List[Type]) extends SubstMap(from, to) { - protected def toType(fromtp: Type, tp: Type) = tp + class SubstTypeMap(from0: List[Symbol], to0: List[Type]) extends SubstMap[Type](from0, to0) { + final def from: List[Symbol] = accessFrom + final def to: List[Type] = accessTo + + override protected def toType(fromtp: Type, tp: Type) = tp override def mapOver(tree: Tree, giveup: () => Nothing): Tree = { object trans extends TypeMapTransformer { diff --git a/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala index e9691b9b404f..8a8540df3cea 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala @@ -39,7 +39,7 @@ private[internal] trait TypeToStrings { // else if (toStringRecursions >= maxToStringRecursions) { devWarning("Exceeded recursion depth attempting to print " + util.shortClassOfInstance(tpe)) - if (settings.debug) + if (settings.isDebug) (new Throwable).printStackTrace "..." diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 981a0e3ce140..c42455575db8 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -100,10 +100,11 @@ trait Erasure { assert(!phase.erasedTypes, "Types are erased") val clazz = tref.sym if (valueClassIsParametric(clazz)) { - val underlying = tref.memberType(clazz.derivedValueClassUnbox).resultType - boxingErasure(underlying) + val erasureMap = if (clazz.isScala3Defined) boxing3Erasure else boxingErasure + erasureMap(tref.memberType(clazz.derivedValueClassUnbox).resultType) } else { - scalaErasure(underlyingOfValueClass(clazz)) + val erasureMap = if (clazz.isScala3Defined) scala3Erasure else scalaErasure + erasureMap(underlyingOfValueClass(clazz)) } } @@ -118,6 +119,7 @@ trait Erasure { abstract class ErasureMap extends TypeMap { def mergeParents(parents: List[Type]): Type + def eraseArray(arrayRef: Type, pre: Type, args: List[Type]): Type def eraseNormalClassRef(tref: TypeRef): Type = { val TypeRef(pre, clazz, args) = tref @@ -140,21 +142,15 @@ trait Erasure { apply(st.supertype) case tref @ TypeRef(pre, sym, args) => def isDottyEnumSingleton(sym: Symbol): Boolean = - sym.isModuleClass && sym.sourceModule.hasAttachment[DottyEnumSingleton] - if (sym eq ArrayClass) - if (unboundedGenericArrayLevel(tp) == 1) ObjectTpe - else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectTpe) - else typeRef(apply(pre), sym, args map applyInArray) + sym.isScala3Defined && sym.isModuleClass && sym.sourceModule.hasAttachment[DottyEnumSingleton.type] + if (sym eq ArrayClass) eraseArray(tp, pre, args) else if ((sym eq AnyClass) || (sym eq AnyValClass) || (sym eq SingletonClass)) ObjectTpe else if (sym eq UnitClass) BoxedUnitTpe else if (sym.isRefinementClass) apply(mergeParents(tp.parents)) else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref) - else if (isDottyEnumSingleton(sym)) apply(intersectionType(tp.parents)) // TODO [tasty]: dotty enum singletons are not modules. + else if (isDottyEnumSingleton(sym)) apply(mergeParents(tp.parents)) // TODO [tasty]: dotty enum singletons are not modules. else if (sym.isClass) eraseNormalClassRef(tref) - else sym.attachments.get[DottyOpaqueTypeAlias] match { - case Some(alias: DottyOpaqueTypeAlias) => apply(alias.tpe) // TODO [tasty]: refactor if we build-in opaque types - case _ => apply(sym.info.asSeenFrom(pre, sym.owner)) // alias type or abstract type - } + else apply(transparentDealias(sym, pre, sym.owner)) // alias type or abstract type (including opaque type) case PolyType(tparams, restpe) => apply(restpe) case ExistentialType(tparams, restpe) => @@ -246,10 +242,16 @@ trait Erasure { * parents |Ps|, but with duplicate references of Object removed. * - for all other types, the type itself (with any sub-components erased) */ - def erasure(sym: Symbol): ErasureMap = - if (sym == NoSymbol || !sym.enclClass.isJavaDefined) scalaErasure - else if (verifyJavaErasure && sym.isMethod) verifiedJavaErasure - else javaErasure + def erasure(sym: Symbol): ErasureMap = { + if (sym == NoSymbol) return scalaErasure + val enclosing = sym.enclClass + if (enclosing.isJavaDefined) { + if (verifyJavaErasure && sym.isMethod) verifiedJavaErasure + else javaErasure + } + else if (enclosing.isScala3Defined) scala3Erasure + else scalaErasure + } /** This is used as the Scala erasure during the erasure phase itself * It differs from normal erasure in that value classes are erased to ErasedValueTypes which @@ -260,8 +262,9 @@ trait Erasure { erasure(sym)(tp) else if (sym.isClassConstructor) specialConstructorErasure(sym.owner, tp) - else - specialScalaErasure(tp) + else { + specialScalaErasureFor(sym)(tp) + } def specialConstructorErasure(clazz: Symbol, tpe: Type): Type = { tpe match { @@ -271,14 +274,14 @@ trait Erasure { specialConstructorErasure(clazz, restpe) case mt @ MethodType(params, restpe) => MethodType( - cloneSymbolsAndModify(params, specialScalaErasure), + cloneSymbolsAndModify(params, specialScalaErasureFor(clazz)), specialConstructorErasure(clazz, restpe)) case TypeRef(pre, `clazz`, args) => typeRef(pre, clazz, List()) case tp => if (!(clazz == ArrayClass || tp.isError)) assert(clazz == ArrayClass || tp.isError, s"!!! unexpected constructor erasure $tp for $clazz") - specialScalaErasure(tp) + specialScalaErasureFor(clazz)(tp) } } @@ -294,7 +297,8 @@ trait Erasure { * For this reason and others (such as distinguishing constructors from other methods) * erasure is now (Symbol, Type) => Type rather than Type => Type. */ - class ScalaErasureMap extends ErasureMap { + abstract class ScalaErasureMap extends ErasureMap with Scala2JavaArrayErasure { + /** In scala, calculate a useful parent. * An intersection such as `Object with Trait` erases to Trait. */ @@ -302,7 +306,213 @@ trait Erasure { intersectionDominator(parents) } - class JavaErasureMap extends ErasureMap { + trait Scala2JavaArrayErasure { self: ErasureMap => + + def eraseArray(arrayRef: Type, pre: Type, args: List[Type]): Type = + if (unboundedGenericArrayLevel(arrayRef) == 1) ObjectTpe + else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectTpe) + else typeRef(self(pre), ArrayClass, args map applyInArray) + + } + + class Scala3ErasureMap extends ErasureMap { self => + + def mergeParents(parents: List[Type]): Type = { + erasedGlb(parents.map(self(_))) + } + + def mergeParentsInArray(parents: List[Type]): Type = { + erasedGlb(parents.map(super.applyInArray(_))) + } + + override def applyInArray(tp: Type): Type = { + tp match { + case RefinedType(parents, _) => + super.applyInArray(mergeParentsInArray(parents)) + case _ => + super.applyInArray(tp) + } + } + + def eraseArray(arrayRef: Type, pre: Type, args: List[Type]): Type = { + if (isGenericArrayElement(args.head)) ObjectTpe + else typeRef(self(pre), ArrayClass, args map applyInArray) + } + + /** Scala 3 implementation of erasure for intersection types. + * @param components the erased component types of the intersection. + */ + private def erasedGlb(components: List[Type]): Type = { + + /** A comparison function that induces a total order on erased types, + * where `A <= B` implies that the erasure of `A & B` should be A. + * + * This order respects the following properties: + * - ErasedValueTypes <= non-ErasedValueTypes + * - arrays <= non-arrays + * - primitives <= non-primitives + * - real classes <= traits + * - subtypes <= supertypes + * + * Since this isn't enough to order to unrelated classes, we use + * lexicographic ordering of the class symbol full name as a tie-breaker. + * This ensure that `A <= B && B <= A` iff `A =:= B`. + */ + def compareErasedGlb(tp1: Type, tp2: Type): Int = { + // this check is purely an optimization. + if (tp1 eq tp2) return 0 + + val isEVT1 = tp1.isInstanceOf[ErasedValueType] + val isEVT2 = tp2.isInstanceOf[ErasedValueType] + if (isEVT1 && isEVT2) { + return compareErasedGlb( + tp1.asInstanceOf[ErasedValueType].valueClazz.tpe_*, + tp2.asInstanceOf[ErasedValueType].valueClazz.tpe_*) + } + else if (isEVT1) + return -1 + else if (isEVT2) + return 1 + + val sym1 = tp1.baseClasses.head + val sym2 = tp2.baseClasses.head + + def compareClasses: Int = { + if (sym1.isSubClass(sym2)) + -1 + else if (sym2.isSubClass(sym1)) + 1 + else + sym1.fullName.compareTo(sym2.fullName) + } + + val isArray1 = tp1.typeArgs.nonEmpty && sym1.isSubClass(definitions.ArrayClass) + val isArray2 = tp2.typeArgs.nonEmpty && sym2.isSubClass(definitions.ArrayClass) + if (isArray1 && isArray2) + return compareErasedGlb(tp1.typeArgs.head, tp2.typeArgs.head) + else if (isArray1) + return -1 + else if (isArray2) + return 1 + + val isPrimitive1 = sym1.isPrimitiveValueClass + val isPrimitive2 = sym2.isPrimitiveValueClass + if (isPrimitive1 && isPrimitive2) + return compareClasses + else if (isPrimitive1) + return -1 + else if (isPrimitive2) + return 1 + + val isRealClass1 = sym1.isClass && !sym1.isTrait + val isRealClass2 = sym2.isClass && !sym2.isTrait + if (isRealClass1 && isRealClass2) + return compareClasses + else if (isRealClass1) + return -1 + else if (isRealClass2) + return 1 + + compareClasses + } + + components.min((t, u) => compareErasedGlb(t, u)) + } + + /** Dotty implementation of Array Erasure: + * + * Is `Array[tp]` a generic Array that needs to be erased to `Object`? + * This is true if among the subtypes of `Array[tp]` there is either: + * - both a reference array type and a primitive array type + * (e.g. `Array[_ <: Int | String]`, `Array[_ <: Any]`) + * - or two different primitive array types (e.g. `Array[_ <: Int | Double]`) + * In both cases the erased lub of those array types on the JVM is `Object`. + */ + private def isGenericArrayElement(tp: Type): Boolean = { + + object DottyTypeProxy { + + def unapply(tp: Type): Option[Type] = { + val superTpe = translucentSuperType(tp) + if (superTpe ne NoType) Some(superTpe) else None + } + + def translucentSuperType(tp: Type): Type = tp match { + case tp: TypeRef => transparentDealias(tp.sym, tp.pre, tp.sym.owner) + case tp: SingleType => tp.underlying + case tp: ThisType => tp.sym.typeOfThis + case tp: ConstantType => tp.value.tpe + case tp: RefinedType if tp.decls.nonEmpty => intersectionType(tp.parents) + case tp: PolyType => tp.resultType + case tp: ExistentialType => tp.underlying + case tp: TypeBounds => tp.hi + case tp: AnnotatedType => tp.underlying + case tp: SuperType => tp.thistpe.baseType(tp.supertpe.typeSymbol) + case tp => NoType + } + + } + + object DottyAndType { + def unapply(tp: RefinedType): Boolean = tp.decls.isEmpty + } + + /** A symbol that represents the sort of JVM array that values of type `t` can be stored in: + * - If we can always store such values in a reference array, return Object + * - If we can always store them in a specific primitive array, return the + * corresponding primitive class + * - Otherwise, return `NoSymbol`. + */ + def arrayUpperBound(tp: Type): Symbol = tp.dealias match { + case tp: TypeRef if tp.sym.isClass => + val cls = tp.sym + // Only a few classes have both primitives and references as subclasses. + if ((cls eq AnyClass) || (cls eq AnyValClass) || (cls eq SingletonClass)) + NoSymbol + // We only need to check for primitives because derived value classes in arrays are always boxed. + else if (cls.isPrimitiveValueClass) + cls + else + ObjectClass + case DottyTypeProxy(unwrapped) => + arrayUpperBound(unwrapped) + case tp @ DottyAndType() => + // Find first `p` in `parents` where `arrayUpperBound(p) ne NoSymbol` + @tailrec def loop(tps: List[Type]): Symbol = tps match { + case tp :: tps1 => + val ub = arrayUpperBound(tp) + if (ub ne NoSymbol) ub + else loop(tps1) + case nil => NoSymbol + } + loop(tp.parents) + case _ => + NoSymbol + } + + /** Can one of the JVM Array type store all possible values of type `t`? */ + def fitsInJVMArray(tp: Type): Boolean = arrayUpperBound(tp) ne NoSymbol + + def isOpaque(sym: Symbol) = sym.isScala3Defined && !sym.isClass && sym.hasAttachment[DottyOpaqueTypeAlias] + + tp.dealias match { + case tp: TypeRef if !isOpaque(tp.sym) => + !tp.sym.isClass && + !tp.sym.isJavaDefined && // In Java code, Array[T] can never erase to Object + !fitsInJVMArray(tp) + case DottyTypeProxy(unwrapped) => + isGenericArrayElement(unwrapped) + case tp @ DottyAndType() => + tp.parents.forall(isGenericArrayElement) + case tp => + false + } + + } + + } + + class JavaErasureMap extends ErasureMap with Scala2JavaArrayErasure { /** In java, always take the first parent. * An intersection such as `Object with Trait` erases to Object. */ @@ -314,14 +524,27 @@ trait Erasure { } object scalaErasure extends ScalaErasureMap + object scala3Erasure extends Scala3ErasureMap + + trait SpecialScalaErasure extends ErasureMap { + override def eraseDerivedValueClassRef(tref: TypeRef): Type = + ErasedValueType(tref.sym, erasedValueClassArg(tref)) + } /** This is used as the Scala erasure during the erasure phase itself * It differs from normal erasure in that value classes are erased to ErasedValueTypes which * are then later unwrapped to the underlying parameter type in phase posterasure. */ - object specialScalaErasure extends ScalaErasureMap { - override def eraseDerivedValueClassRef(tref: TypeRef): Type = - ErasedValueType(tref.sym, erasedValueClassArg(tref)) + object specialScalaErasure extends ScalaErasureMap with SpecialScalaErasure + + /** This is used as the Scala erasure for Scala 3 methods during the erasure phase itself. + * @see specialScalaErasure + */ + object specialScala3Erasure extends Scala3ErasureMap with SpecialScalaErasure + + def specialScalaErasureFor(sym: Symbol): ErasureMap = { + if (sym.isScala3Defined) specialScala3Erasure + else specialScalaErasure } object javaErasure extends JavaErasureMap @@ -336,7 +559,8 @@ trait Erasure { } } - object boxingErasure extends ScalaErasureMap { + trait BoxingErasure extends ErasureMap { + private[this] var boxPrimitives = true override def applyInArray(tp: Type): Type = { @@ -349,10 +573,15 @@ trait Erasure { override def eraseNormalClassRef(tref: TypeRef) = if (boxPrimitives && isPrimitiveValueClass(tref.sym)) boxedClass(tref.sym).tpe else super.eraseNormalClassRef(tref) + override def eraseDerivedValueClassRef(tref: TypeRef) = super.eraseNormalClassRef(tref) + } + object boxingErasure extends ScalaErasureMap with BoxingErasure + object boxing3Erasure extends Scala3ErasureMap with BoxingErasure + /** The intersection dominator (SLS 3.7) of a list of types is computed as follows. * * - If the list contains one or more occurrences of scala.Array with @@ -388,6 +617,21 @@ trait Erasure { } } + /** For a type alias, get its info as seen from + * the current prefix and owner. + * Sees through opaque type aliases. + */ + def transparentDealias(sym: Symbol, pre: Type, owner: Symbol) = { + @inline def visible(tp: Type) = tp.asSeenFrom(pre, owner) + + if (sym.isScala3Defined && !sym.isClass) + sym.attachments.get[DottyOpaqueTypeAlias] + .map(alias => visible(alias.tpe)) + .getOrElse(visible(sym.info)) + else + visible(sym.info) + } + /** The symbol's erased info. This is the type's erasure, except for the following primitive symbols: * * - $asInstanceOf --> [T]T @@ -414,7 +658,7 @@ trait Erasure { if (sym.isClassConstructor) // TODO: switch on name for all branches -- this one is sym.name == nme.CONSTRUCTOR tp match { case MethodType(params, TypeRef(pre, sym1, args)) => - MethodType(cloneSymbolsAndModify(params, specialErasure(sym)), + MethodType(cloneSymbolsAndModify(params, tp => specialErasure(sym)(tp)), typeRef(specialErasure(sym)(pre), sym1, args)) case x => throw new MatchError(x) } diff --git a/src/reflect/scala/reflect/internal/transform/Transforms.scala b/src/reflect/scala/reflect/internal/transform/Transforms.scala index 8a4bc08c0a45..eecc286f2044 100644 --- a/src/reflect/scala/reflect/internal/transform/Transforms.scala +++ b/src/reflect/scala/reflect/internal/transform/Transforms.scala @@ -49,7 +49,10 @@ trait Transforms { self: SymbolTable => erasure.transformInfo(sym, uncurry.transformInfo(sym, sym.info))) - def transformedType(tpe: Type) = - postErasure.elimErasedValueType(erasure.scalaErasure(uncurry.uncurry(tpe))) + def transformedType(tpe: Type) = { + val symbol = tpe.widen.typeSymbol + val erasureMap = if (symbol.isScala3Defined) erasure.scala3Erasure else erasure.scalaErasure + postErasure.elimErasedValueType(erasureMap(uncurry.uncurry(tpe))) + } } diff --git a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala index 04591dc0fa6c..2c50d5cf9443 100644 --- a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala +++ b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala @@ -109,8 +109,9 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) throw new UnsupportedOperationException() } + // TODO: `getPackage` is deprecated in JDK 9+ - what should be overridden instead? override def getPackage(name: String): Package = findAbstractDir(name) match { - case null => super.getPackage(name) + case null => super.getPackage(name): @nowarn("cat=deprecation") case file => packages.getOrElseUpdate(name, { val ctor = classOf[Package].getDeclaredConstructor(classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[String], classOf[URL], classOf[ClassLoader]) ctor.setAccessible(true) diff --git a/src/reflect/scala/reflect/internal/util/AlmostFinalValue.java b/src/reflect/scala/reflect/internal/util/AlmostFinalValue.java index 415f91f9a8ff..f9bb24f00a85 100644 --- a/src/reflect/scala/reflect/internal/util/AlmostFinalValue.java +++ b/src/reflect/scala/reflect/internal/util/AlmostFinalValue.java @@ -14,93 +14,35 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.invoke.MutableCallSite; -import java.lang.invoke.SwitchPoint; /** * Represents a value that is wrapped with JVM machinery to allow the JVM - * to speculate on its content and effectively optimize it as if it was final. - * - * This file has been drawn from JSR292 cookbook created by Rémi Forax. - * https://code.google.com/archive/p/jsr292-cookbook/. The explanation of the strategy - * can be found in https://community.oracle.com/blogs/forax/2011/12/17/jsr-292-goodness-almost-static-final-field. - * - * Before copying this file to the repository, I tried to adapt the most important - * parts of this implementation and special case it for `Statistics`, but that - * caused an important performance penalty (~10%). This performance penalty is - * due to the fact that using `static`s for the method handles and all the other + * to speculate on its content and effectively optimize it as if it was a constant. + * + * Originally from the JSR-292 cookbook created by Rémi Forax: + * https://code.google.com/archive/p/jsr292-cookbook/. + * + * Implemented in Java because using `static`s for the method handles and all the other * fields is extremely important for the JVM to correctly optimize the code, and * we cannot do that if we make `Statistics` an object extending `MutableCallSite` - * in Scala. We instead rely on the Java implementation that uses a boxed representation. + * in Scala. + * + * Subsequently specialised for booleans, to avoid needless Boolean boxing. + * + * Finally reworked to default to false and only allow for the value to be toggled on, + * using Rémi Forax's newer "MostlyConstant" as inspiration, in https://github.com/forax/exotic. */ -public class AlmostFinalValue { - private final AlmostFinalCallSite callsite = - new AlmostFinalCallSite(this); - - protected boolean initialValue() { - return false; - } - - public MethodHandle createGetter() { - return callsite.dynamicInvoker(); - } - - public void setValue(boolean value) { - callsite.setValue(value); - } - - private static class AlmostFinalCallSite extends MutableCallSite { - private Boolean value; - private SwitchPoint switchPoint; - private final AlmostFinalValue volatileFinalValue; - private final MethodHandle fallback; - private final Object lock; - - private static final Boolean NONE = null; - private static final MethodHandle FALLBACK; - static { - try { - FALLBACK = MethodHandles.lookup().findVirtual(AlmostFinalCallSite.class, "fallback", - MethodType.methodType(Boolean.TYPE)); - } catch (NoSuchMethodException|IllegalAccessException e) { - throw new AssertionError(e.getMessage(), e); - } - } - - AlmostFinalCallSite(AlmostFinalValue volatileFinalValue) { - super(MethodType.methodType(Boolean.TYPE)); - Object lock = new Object(); - MethodHandle fallback = FALLBACK.bindTo(this); - synchronized(lock) { - value = null; - switchPoint = new SwitchPoint(); - setTarget(fallback); - } - this.volatileFinalValue = volatileFinalValue; - this.lock = lock; - this.fallback = fallback; - } +final class AlmostFinalValue { + private static final MethodHandle K_FALSE = MethodHandles.constant(boolean.class, false); + private static final MethodHandle K_TRUE = MethodHandles.constant(boolean.class, true); + + private final MutableCallSite callsite = new MutableCallSite(K_FALSE); + final MethodHandle invoker = callsite.dynamicInvoker(); - boolean fallback() { - synchronized(lock) { - Boolean value = this.value; - if (value == NONE) { - value = volatileFinalValue.initialValue(); - } - MethodHandle target = switchPoint.guardWithTest(MethodHandles.constant(Boolean.TYPE, value), fallback); - setTarget(target); - return value; - } - } - - void setValue(boolean value) { - synchronized(lock) { - SwitchPoint switchPoint = this.switchPoint; - this.value = value; - this.switchPoint = new SwitchPoint(); - SwitchPoint.invalidateAll(new SwitchPoint[] {switchPoint}); - } - } + void toggleOnAndDeoptimize() { + if (callsite.getTarget() == K_TRUE) return; + callsite.setTarget(K_TRUE); + MutableCallSite.syncAll(new MutableCallSite[] { callsite }); } -} \ No newline at end of file +} diff --git a/src/reflect/scala/reflect/internal/util/ReusableInstance.scala b/src/reflect/scala/reflect/internal/util/ReusableInstance.scala index 5dea888f6d2e..8853e7d72242 100644 --- a/src/reflect/scala/reflect/internal/util/ReusableInstance.scala +++ b/src/reflect/scala/reflect/internal/util/ReusableInstance.scala @@ -19,17 +19,18 @@ import scala.util.chaining._ * The wrapper is recursion-reentrant: several instances are kept, so * at each depth of reentrance we are reusing the instance for that. * - * An instance is created upon creating this object, and more instances - * are allocated dynamically, on demand, when reentrance occurs. + * An instance is created eagerly, then more instances + * are allocated as needed on re-entry. Once allocated, + * cached instances are not reclaimed for the life of this ReusableInstance. * * Not thread safe. */ -final class ReusableInstance[T <: AnyRef] private (make: => T, enabled: Boolean) { - private[this] val cache = if (enabled) new ArrayBuffer[T](ReusableInstance.InitialSize).tap(_.addOne(make)) else null +final class ReusableInstance[T <: AnyRef] private (make: => T, initialSize: Int) { + private[this] val cache = if (initialSize > 0) new ArrayBuffer[T](initialSize).tap(_.addOne(make)) else null private[this] var taken = 0 @inline def using[R](action: T => R): R = - if (!enabled) + if (cache == null) action(make) else { if (taken == cache.size) @@ -42,6 +43,12 @@ final class ReusableInstance[T <: AnyRef] private (make: => T, enabled: Boolean) object ReusableInstance { private final val InitialSize = 4 - def apply[T <: AnyRef](make: => T): ReusableInstance[T] = new ReusableInstance[T](make, enabled = true) - def apply[T <: AnyRef](make: => T, enabled: Boolean): ReusableInstance[T] = new ReusableInstance[T](make, enabled = enabled) + def apply[T <: AnyRef](make: => T, initialSize: Int): ReusableInstance[T] = new ReusableInstance[T](make, initialSize) + + def apply[T <: AnyRef](make: => T): ReusableInstance[T] = + apply(make, InitialSize) + def apply[T <: AnyRef](make: => T, enabled: Boolean): ReusableInstance[T] = + if (enabled) apply(make) else apply(make, -1) + def apply[T <: AnyRef](make: => T, initialSize: Int, enabled: Boolean): ReusableInstance[T] = + if (enabled) apply(make, initialSize) else apply(make, -1) } diff --git a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala index abb75b66baf9..82f376e5c379 100644 --- a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala +++ b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala @@ -29,16 +29,12 @@ trait HasClassPath { def classPathURLs: Seq[URL] } -/** A wrapper around java.lang.ClassLoader to lower the annoyance - * of java reflection. - */ -trait ScalaClassLoader extends JClassLoader { +final class RichClassLoader(private val self: JClassLoader) extends AnyVal { /** Executing an action with this classloader as context classloader */ def asContext[T](action: => T): T = { - import ScalaClassLoader.setContext val saved = Thread.currentThread.getContextClassLoader - try { setContext(this) ; action } - finally setContext(saved) + try { ScalaClassLoader.setContext(self) ; action } + finally ScalaClassLoader.setContext(saved) } /** Load and link a class with this classloader */ @@ -48,7 +44,7 @@ trait ScalaClassLoader extends JClassLoader { private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] = catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt - Class.forName(path, initialize, this).asInstanceOf[Class[T]] + Class.forName(path, initialize, self).asInstanceOf[Class[T]] /** Create an instance of a class with this classloader */ def create(path: String): AnyRef = @@ -59,7 +55,7 @@ trait ScalaClassLoader extends JClassLoader { def fail(msg: String) = error(msg, new IllegalArgumentException(msg)) def error(msg: String, e: Throwable) = { errorFn(msg) ; throw e } try { - val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ this) + val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ self) if (classTag[T].runtimeClass isAssignableFrom clazz) { val ctor = { val maybes = clazz.getConstructors filter (c => c.getParameterCount == args.size && @@ -70,7 +66,7 @@ trait ScalaClassLoader extends JClassLoader { (ctor.newInstance(args: _*)).asInstanceOf[T] } else { errorFn(s"""Loader for ${classTag[T]}: [${show(classTag[T].runtimeClass.getClassLoader)}] - |Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin) + |Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin) fail(s"Not a ${classTag[T]}: ${path}") } } catch { @@ -88,7 +84,7 @@ trait ScalaClassLoader extends JClassLoader { } /** An InputStream representing the given class name, or null if not found. */ - def classAsStream(className: String) = getResourceAsStream { + def classAsStream(className: String) = self.getResourceAsStream { if (className endsWith ".class") className else s"${className.replace('.', '/')}.class" // classNameToPath } @@ -107,6 +103,41 @@ trait ScalaClassLoader extends JClassLoader { } } +object RichClassLoader { + implicit def wrapClassLoader(loader: ClassLoader): RichClassLoader = new RichClassLoader(loader) +} + +/** A wrapper around java.lang.ClassLoader to lower the annoyance + * of java reflection. + */ +trait ScalaClassLoader extends JClassLoader { + private def wrap = new RichClassLoader(this) + /** Executing an action with this classloader as context classloader */ + def asContext[T](action: => T): T = wrap.asContext(action) + + /** Load and link a class with this classloader */ + def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = wrap.tryToLoadClass[T](path) + /** Load, link and initialize a class with this classloader */ + def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = wrap.tryToInitializeClass(path) + + /** Create an instance of a class with this classloader */ + def create(path: String): AnyRef = wrap.create(path) + + /** Create an instance with ctor args, or invoke errorFn before throwing. */ + def create[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T = + wrap.create[T](path, errorFn)(args: _*) + + /** The actual bytes for a class file, or an empty array if it can't be found. */ + def classBytes(className: String): Array[Byte] = wrap.classBytes(className) + + /** An InputStream representing the given class name, or null if not found. */ + def classAsStream(className: String) = wrap.classAsStream(className) + + /** Run the main method of a class to be loaded by this classloader */ + def run(objectName: String, arguments: Seq[String]): Unit = wrap.run(objectName, arguments) +} + + /** Methods for obtaining various classloaders. * appLoader: the application classloader. (Also called the java system classloader.) * extLoader: the extension classloader. @@ -149,6 +180,10 @@ object ScalaClassLoader { new URLClassLoader(urls, if (parent == null) bootClassLoader else parent) } + def fromURLsParallelCapable(urls: Seq[URL], parent: ClassLoader = null): JURLClassLoader = { + new JURLClassLoader(urls.toArray, if (parent == null) bootClassLoader else parent) + } + /** True if supplied class exists in supplied path */ def classExists(urls: Seq[URL], name: String): Boolean = (fromURLs(urls) tryToLoadClass name).isDefined diff --git a/src/reflect/scala/reflect/internal/util/Statistics.scala b/src/reflect/scala/reflect/internal/util/Statistics.scala index b9ef1220a003..ce12b1c7a159 100644 --- a/src/reflect/scala/reflect/internal/util/Statistics.scala +++ b/src/reflect/scala/reflect/internal/util/Statistics.scala @@ -22,57 +22,49 @@ import scala.annotation.nowarn import scala.runtime.LongRef abstract class Statistics(val symbolTable: SymbolTable, settings: MutableSettings) { - - initFromSettings(settings) - - def initFromSettings(currentSettings: MutableSettings): Unit = { - enabled = currentSettings.YstatisticsEnabled - hotEnabled = currentSettings.YhotStatisticsEnabled - } - type TimerSnapshot = (Long, Long) /** If enabled, increment counter by one */ @inline final def incCounter(c: Counter): Unit = { - if (areStatisticsLocallyEnabled && c != null) c.value += 1 + if (enabled && c != null) c.value += 1 } /** If enabled, increment counter by given delta */ @inline final def incCounter(c: Counter, delta: Int): Unit = { - if (areStatisticsLocallyEnabled && c != null) c.value += delta + if (enabled && c != null) c.value += delta } /** If enabled, increment counter in map `ctrs` at index `key` by one */ @inline final def incCounter[K](ctrs: QuantMap[K, Counter], key: K) = - if (areStatisticsLocallyEnabled && ctrs != null) ctrs(key).value += 1 + if (enabled && ctrs != null) ctrs(key).value += 1 /** If enabled, start subcounter. While active it will track all increments of * its base counter. */ @inline final def startCounter(sc: SubCounter): (Int, Int) = - if (areStatisticsLocallyEnabled && sc != null) sc.start() else null + if (enabled && sc != null) sc.start() else null /** If enabled, stop subcounter from tracking its base counter. */ @inline final def stopCounter(sc: SubCounter, start: (Int, Int)): Unit = { - if (areStatisticsLocallyEnabled && sc != null) sc.stop(start) + if (enabled && sc != null) sc.stop(start) } /** If enabled, start timer */ @inline final def startTimer(tm: Timer): TimerSnapshot = - if (areStatisticsLocallyEnabled && tm != null) tm.start() else null + if (enabled && tm != null) tm.start() else null /** If enabled, stop timer */ @inline final def stopTimer(tm: Timer, start: TimerSnapshot): Unit = { - if (areStatisticsLocallyEnabled && tm != null) tm.stop(start) + if (enabled && tm != null) tm.stop(start) } /** If enabled, push and start a new timer in timer stack */ @inline final def pushTimer(timers: TimerStack, timer: => StackableTimer): TimerSnapshot = - if (areStatisticsLocallyEnabled && timers != null) timers.push(timer) else null + if (enabled && timers != null) timers.push(timer) else null /** If enabled, stop and pop timer from timer stack */ @inline final def popTimer(timers: TimerStack, prev: TimerSnapshot): Unit = { - if (areStatisticsLocallyEnabled && timers != null) timers.pop(prev) + if (enabled && timers != null) timers.pop(prev) } /** Create a new counter that shows as `prefix` and is active in given phases */ @@ -294,29 +286,8 @@ quant) } private[this] val qs = new mutable.HashMap[String, Quantity] - private[scala] var areColdStatsLocallyEnabled: Boolean = false - private[scala] var areHotStatsLocallyEnabled: Boolean = false - - /** Represents whether normal statistics can or cannot be enabled. */ - @inline final def enabled: Boolean = areColdStatsLocallyEnabled - def enabled_=(cond: Boolean) = { - if (cond && !enabled) { - StatisticsStatics.enableColdStats() - areColdStatsLocallyEnabled = true - } - } - - /** Represents whether hot statistics can or cannot be enabled. */ - @inline final def hotEnabled: Boolean = enabled && areHotStatsLocallyEnabled - def hotEnabled_=(cond: Boolean) = { - if (cond && enabled && !areHotStatsLocallyEnabled) { - StatisticsStatics.enableHotStats() - areHotStatsLocallyEnabled = true - } - } - /** Tells whether statistics should be definitely reported to the user for this `Global` instance. */ - @inline final def areStatisticsLocallyEnabled: Boolean = areColdStatsLocallyEnabled + @inline final def enabled: Boolean = settings.areStatisticsEnabled import scala.reflect.internal.Reporter /** Reports the overhead of measuring statistics via the nanoseconds variation. */ diff --git a/src/reflect/scala/reflect/internal/util/StatisticsStatics.java b/src/reflect/scala/reflect/internal/util/StatisticsStatics.java index dc9021471d87..76c1644e18bf 100644 --- a/src/reflect/scala/reflect/internal/util/StatisticsStatics.java +++ b/src/reflect/scala/reflect/internal/util/StatisticsStatics.java @@ -12,7 +12,6 @@ package scala.reflect.internal.util; -import scala.reflect.internal.util.AlmostFinalValue; import java.lang.invoke.MethodHandle; /** @@ -22,46 +21,18 @@ * which helps performance (see docs to find out why). */ public final class StatisticsStatics { - private static final AlmostFinalValue COLD_STATS = new AlmostFinalValue() { - @Override - protected boolean initialValue() { - return false; - } - }; - - private static final AlmostFinalValue HOT_STATS = new AlmostFinalValue() { - @Override - protected boolean initialValue() { - return false; - } - }; - - private static final MethodHandle COLD_STATS_GETTER = COLD_STATS.createGetter(); - private static final MethodHandle HOT_STATS_GETTER = HOT_STATS.createGetter(); - - public static boolean areSomeColdStatsEnabled() throws Throwable { - return (boolean) COLD_STATS_GETTER.invokeExact(); - } - - public static boolean areSomeHotStatsEnabled() throws Throwable { - return (boolean) HOT_STATS_GETTER.invokeExact(); - } - - public static void enableColdStats() throws Throwable { - if (!areSomeColdStatsEnabled()) - COLD_STATS.setValue(true); - } - - public static void disableColdStats() { - COLD_STATS.setValue(false); - } - - public static void enableHotStats() throws Throwable { - if (!areSomeHotStatsEnabled()) - HOT_STATS.setValue(true); - } - - public static void disableHotStats() { - HOT_STATS.setValue(false); - } + private static final AlmostFinalValue COLD_STATS = new AlmostFinalValue(); + private static final AlmostFinalValue HOT_STATS = new AlmostFinalValue(); + private static final AlmostFinalValue DEBUG = new AlmostFinalValue(); + private static final AlmostFinalValue DEVELOPER = new AlmostFinalValue(); + + public static final MethodHandle COLD_STATS_GETTER = COLD_STATS.invoker; + public static final MethodHandle HOT_STATS_GETTER = HOT_STATS.invoker; + public static final MethodHandle DEBUG_GETTER = DEBUG.invoker; + public static final MethodHandle DEVELOPER_GETTER = DEVELOPER.invoker; + + public static void enableColdStatsAndDeoptimize() { COLD_STATS.toggleOnAndDeoptimize(); } + public static void enableHotStatsAndDeoptimize() { HOT_STATS.toggleOnAndDeoptimize(); } + public static void enableDebugAndDeoptimize() { DEBUG.toggleOnAndDeoptimize(); } + public static void enableDeveloperAndDeoptimize() { DEVELOPER.toggleOnAndDeoptimize(); } } diff --git a/src/reflect/scala/reflect/io/AbstractFile.scala b/src/reflect/scala/reflect/io/AbstractFile.scala index d97e6d23e5ec..72736bfb2f26 100644 --- a/src/reflect/scala/reflect/io/AbstractFile.scala +++ b/src/reflect/scala/reflect/io/AbstractFile.scala @@ -121,7 +121,7 @@ abstract class AbstractFile extends AbstractIterable[AbstractFile] { /** Does this abstract file denote an existing file? */ def exists: Boolean = { - //if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(IOStats.fileExistsCount) + //if (settings.areStatisticsEnabled) statistics.incCounter(IOStats.fileExistsCount) (file eq null) || file.exists } diff --git a/src/reflect/scala/reflect/io/Path.scala b/src/reflect/scala/reflect/io/Path.scala index 26bef55f5796..361805ba8955 100644 --- a/src/reflect/scala/reflect/io/Path.scala +++ b/src/reflect/scala/reflect/io/Path.scala @@ -64,12 +64,12 @@ object Path { def apply(path: String): Path = apply(new JFile(path)) def apply(jfile: JFile): Path = try { def isFile = { - //if (StatisticsStatics.areSomeColdStatsEnabled) Statistics.incCounter(IOStats.fileIsFileCount) + //if (settings.areStatisticsEnabled) Statistics.incCounter(IOStats.fileIsFileCount) jfile.isFile } def isDirectory = { - //if (StatisticsStatics.areSomeColdStatsEnabled) Statistics.incCounter(IOStats.fileIsDirectoryCount) + //if (settings.areStatisticsEnabled) Statistics.incCounter(IOStats.fileIsDirectoryCount) jfile.isDirectory } @@ -206,16 +206,16 @@ class Path private[io] (val jfile: JFile) { def canRead = jfile.canRead() def canWrite = jfile.canWrite() def exists = { - //if (StatisticsStatics.areSomeColdStatsEnabled) Statistics.incCounter(IOStats.fileExistsCount) + //if (settings.areStatisticsEnabled) Statistics.incCounter(IOStats.fileExistsCount) try jfile.exists() catch { case ex: SecurityException => false } } def isFile = { - //if (StatisticsStatics.areSomeColdStatsEnabled) Statistics.incCounter(IOStats.fileIsFileCount) + //if (settings.areStatisticsEnabled) Statistics.incCounter(IOStats.fileIsFileCount) try jfile.isFile() catch { case ex: SecurityException => false } } def isDirectory = { - //if (StatisticsStatics.areSomeColdStatsEnabled) Statistics.incCounter(IOStats.fileIsDirectoryCount) + //if (settings.areStatisticsEnabled) Statistics.incCounter(IOStats.fileIsDirectoryCount) try jfile.isDirectory() catch { case ex: SecurityException => jfile.getPath == "." } } def isAbsolute = jfile.isAbsolute() diff --git a/src/reflect/scala/reflect/io/ZipArchive.scala b/src/reflect/scala/reflect/io/ZipArchive.scala index 157e2e8e2c62..24452194f190 100644 --- a/src/reflect/scala/reflect/io/ZipArchive.scala +++ b/src/reflect/scala/reflect/io/ZipArchive.scala @@ -139,7 +139,7 @@ abstract class ZipArchive(override val file: JFile, release: Option[String]) ext ensureDir(name) } - @volatile private[this] var lastDirName: String = "" + @volatile private[this] var lastDirName: String = RootEntry private def dirNameUsingLast(name: String): String = { val last = lastDirName if (name.length > last.length + 1 && name.startsWith(last) && name.charAt(last.length) == '/' && name.indexOf('/', last.length + 1) == -1) { diff --git a/src/reflect/scala/reflect/macros/Typers.scala b/src/reflect/scala/reflect/macros/Typers.scala index 9a0904a1a47e..e702f21ebbb1 100644 --- a/src/reflect/scala/reflect/macros/Typers.scala +++ b/src/reflect/scala/reflect/macros/Typers.scala @@ -91,7 +91,7 @@ trait Typers { * * If `silent` is false, `TypecheckException` will be thrown in case of an inference error. * If `silent` is true, the typecheck is silent and will return `EmptyTree` if an error occurs. - * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. + * Such errors don't vanish and can be inspected by turning on -Vimplicits. * Unlike in `typecheck`, `silent` is true by default. * * @throws scala.reflect.macros.TypecheckException @@ -103,7 +103,7 @@ trait Typers { * * If `silent` is false, `TypecheckException` will be thrown in case of an inference error. * If `silent` is true, the typecheck is silent and will return `EmptyTree` if an error occurs. - * Such errors don't vanish and can be inspected by turning on -Xlog-implicits. + * Such errors don't vanish and can be inspected by turning on -Vimplicits. * Unlike in `typecheck`, `silent` is true by default. * * @throws scala.reflect.macros.TypecheckException diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 48e1f0a2c3e5..4e227174901b 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -638,7 +638,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive def markAbsent(tpe: Type) = setAllInfos(clazz, module, tpe) def handleError(ex: Exception) = { markAbsent(ErrorType) - if (settings.debug) ex.printStackTrace() + if (settings.isDebug) ex.printStackTrace() val msg = ex.getMessage() MissingRequirementError.signal( (if (msg eq null) "reflection error while loading " + clazz.name @@ -1153,8 +1153,10 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive } case japplied: ParameterizedType => // https://stackoverflow.com/questions/5767122/parameterizedtype-getrawtype-returns-j-l-r-type-not-class - val sym = classToScala(japplied.getRawType.asInstanceOf[jClass[_]]) - val pre = if (japplied.getOwnerType ne null) typeToScala(japplied.getOwnerType) else sym.owner.thisType + val jcls = japplied.getRawType.asInstanceOf[jClass[_]] + val sym = classToScala(jcls) + val isStatic = java.lang.reflect.Modifier.isStatic(jcls.getModifiers) + val pre = if (!isStatic && (japplied.getOwnerType ne null)) typeToScala(japplied.getOwnerType) else sym.owner.thisType val args0 = japplied.getActualTypeArguments val (args, bounds) = targsToScala(pre.typeSymbol, args0.toList) newExistentialType(bounds, typeRef(pre, sym, args)) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index be33ed5a6651..6d0ac295eed7 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -67,6 +67,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.UseInvokeSpecial this.TypeParamVarargsAttachment this.KnownDirectSubclassesCalled + this.DottyEnumSingleton this.ConstructorNeedsFence this.MultiargInfixAttachment this.NullaryOverrideAdapted @@ -204,6 +205,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.abstractTypesToBounds this.dropIllegalStarTypes this.wildcardExtrapolation + this.SubstSymMap this.IsDependentCollector this.ApproximateDependentMap this.identityTypeMap @@ -520,9 +522,12 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => uncurry.DesugaredParameterType erasure.GenericArray erasure.scalaErasure + erasure.scala3Erasure erasure.specialScalaErasure + erasure.specialScala3Erasure erasure.javaErasure erasure.verifiedJavaErasure erasure.boxingErasure + erasure.boxing3Erasure } } diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 4b8b771f52c5..56786a5581d9 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -15,6 +15,7 @@ package reflect package runtime import scala.reflect.internal.settings.MutableSettings +import scala.reflect.internal.util.StatisticsStatics /** The Settings class for runtime reflection. * This should be refined, so that settings are settable via command @@ -50,17 +51,17 @@ private[reflect] class Settings extends MutableSettings { val Yshowsymowners = new BooleanSetting(false) val Yshowsymkinds = new BooleanSetting(false) val breakCycles = new BooleanSetting(false) - val debug = new BooleanSetting(false) - val developer = new BooleanSetting(false) + val debug = new BooleanSetting(false) { override def postSetHook() = if (v) StatisticsStatics.enableDebugAndDeoptimize() } + val developer = new BooleanSetting(false) { override def postSetHook() = if (v) StatisticsStatics.enableDeveloperAndDeoptimize() } val explaintypes = new BooleanSetting(false) val printtypes = new BooleanSetting(false) val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) - val YhotStatisticsEnabled = new BooleanSetting(false) - val YstatisticsEnabled = new BooleanSetting(false) + val YhotStatisticsEnabled = new BooleanSetting(false) { override def postSetHook() = if (v && YstatisticsEnabled) StatisticsStatics.enableHotStatsAndDeoptimize() } + val YstatisticsEnabled = new BooleanSetting(false) { override def postSetHook() = if (v) StatisticsStatics.enableColdStatsAndDeoptimize() } - val Yrecursion = new IntSetting(0) - def isScala212 = true - private[scala] def isScala213 = true + val Yrecursion = new IntSetting(0) + def isScala212 = true + def isScala213 = true } diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala index 3bb674953521..ccb94eb2dec0 100644 --- a/src/reflect/scala/reflect/runtime/SymbolTable.scala +++ b/src/reflect/scala/reflect/runtime/SymbolTable.scala @@ -25,7 +25,7 @@ private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors w if (settings.verbose) println("[reflect-compiler] "+msg) def debugInfo(msg: => String) = - if (settings.debug) info(msg) + if (settings.isDebug) info(msg) /** Declares that this is a runtime reflection universe. * diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala index ce881c849895..c79c2eee2693 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala @@ -13,14 +13,21 @@ package scala.tools.nsc.interpreter package jline -import java.util.{List => JList} - -import org.jline.reader.{Candidate, Completer, CompletingParsedLine, EOFError, EndOfFileException, History, LineReader, ParsedLine, Parser, SyntaxError, UserInterruptException} -import org.jline.reader.impl.{DefaultParser, LineReaderImpl} +import org.jline.builtins.InputRC +import org.jline.keymap.KeyMap +import org.jline.reader.Parser.ParseContext +import org.jline.reader._ +import org.jline.reader.impl.{CompletionMatcherImpl, DefaultParser, LineReaderImpl} import org.jline.terminal.Terminal -import shell.{Accumulator, ShellConfig} -import Parser.ParseContext +import java.io.{ByteArrayInputStream, File} +import java.net.{MalformedURLException, URL} +import java.util.{List => JList} +import scala.io.Source +import scala.reflect.internal.Chars +import scala.tools.nsc.interpreter.shell.{Accumulator, ShellConfig} +import scala.util.Using +import scala.util.control.NonFatal /** A Reader that delegates to JLine3. */ @@ -69,6 +76,31 @@ object Reader { System.setProperty(LineReader.PROP_SUPPORT_PARSEDLINE, java.lang.Boolean.TRUE.toString()) + def inputrcFileUrl(): Option[URL] = { + sys.props + .get("jline.inputrc") + .flatMap { path => + try Some(new URL(path)) + catch { + case _: MalformedURLException => + Some(new File(path).toURI.toURL) + } + }.orElse { + sys.props.get("user.home").map { home => + val f = new File(home).toPath.resolve(".inputrc").toFile + (if (f.isFile) f else new File("/etc/inputrc")).toURI.toURL + } + } + } + + def urlByteArray(url: URL): Array[Byte] = { + Using.resource(Source.fromURL(url).bufferedReader()) { + bufferedReader => + LazyList.continually(bufferedReader.read).takeWhile(_ != -1).map(_.toByte).toArray + } + } + + lazy val inputrcFileContents: Option[Array[Byte]] = inputrcFileUrl().map(in => urlByteArray(in)) val jlineTerminal = TerminalBuilder.builder().jna(true).build() val completer = new Completion(completion) val parser = new ReplParser(repl) @@ -92,14 +124,86 @@ object Reader { .variable(SECONDARY_PROMPT_PATTERN, config.encolor(config.continueText)) // Continue prompt .variable(WORDCHARS, LineReaderImpl.DEFAULT_WORDCHARS.filterNot("*?.[]~=/&;!#%^(){}<>".toSet)) .option(Option.DISABLE_EVENT_EXPANSION, true) // Otherwise `scala> println(raw"\n".toList)` gives `List(n)` !! + .option(Option.COMPLETE_MATCHER_CAMELCASE, true) + } + object customCompletionMatcher extends CompletionMatcherImpl { + override def compile(options: java.util.Map[LineReader.Option, java.lang.Boolean], prefix: Boolean, line: CompletingParsedLine, caseInsensitive: Boolean, errors: Int, originalGroupName: String): Unit = { + val errorsReduced = line.wordCursor() match { + case 0 | 1 | 2 | 3 => 0 // disable JLine's levenshtein-distance based typo matcher for short strings + case 4 | 5 => math.max(errors, 1) + case _ => errors + } + super.compile(options, prefix, line, caseInsensitive, errorsReduced, originalGroupName) + + // TODO JLINE All of this can/must be removed after the next JLine upgrade + matchers.remove(matchers.size() - 2) // remove ty + val wd = line.word(); + val wdi = if (caseInsensitive) wd.toLowerCase() else wd + val typoMatcherWord = if (prefix) wdi.substring(0, line.wordCursor()) else wdi + val fixedTypoMatcher = typoMatcher( + typoMatcherWord, + errorsReduced, + !caseInsensitive, // Fixed in JLine https://github.com/jline/jline3/pull/647, remove the negation when upgrading! + originalGroupName + ) + matchers.add(matchers.size - 2, fixedTypoMatcher) + } + + override def matches(candidates: JList[Candidate]): JList[Candidate] = { + val matching = super.matches(candidates) + matching + } } + builder.completionMatcher(customCompletionMatcher) + val reader = builder.build() + try inputrcFileContents.foreach(f => InputRC.configure(reader, new ByteArrayInputStream(f))) catch { + case NonFatal(_) => + } //ignore + + val keyMap = reader.getKeyMaps.get("main") + + object ScalaShowType { + val Name = "scala-show-type" + private var lastInvokeLocation: Option[(String, Int)] = None + def apply(): Boolean = { + val nextInvokeLocation = Some((reader.getBuffer.toString, reader.getBuffer.cursor())) + val cursor = reader.getBuffer.cursor() + val text = reader.getBuffer.toString + val result = completer.complete(text, cursor, filter = true) + if (lastInvokeLocation == nextInvokeLocation) { + showTree(result) + lastInvokeLocation = None + } else { + showType(result) + lastInvokeLocation = nextInvokeLocation + } + true + } + def showType(result: shell.CompletionResult): Unit = { + reader.getTerminal.writer.println() + reader.getTerminal.writer.println(result.typeAtCursor) + reader.callWidget(LineReader.REDRAW_LINE) + reader.callWidget(LineReader.REDISPLAY) + reader.getTerminal.flush() + } + def showTree(result: shell.CompletionResult): Unit = { + reader.getTerminal.writer.println() + reader.getTerminal.writer.println(Naming.unmangle(result.typedTree)) + reader.callWidget(LineReader.REDRAW_LINE) + reader.callWidget(LineReader.REDISPLAY) + reader.getTerminal.flush() + } + } + reader.getWidgets().put(ScalaShowType.Name, () => ScalaShowType()) + locally { import LineReader._ // VIINS, VICMD, EMACS val keymap = if (config.viMode) VIINS else EMACS reader.getKeyMaps.put(MAIN, reader.getKeyMaps.get(keymap)); + keyMap.bind(new Reference(ScalaShowType.Name), KeyMap.ctrl('T')) } def secure(p: java.nio.file.Path): Unit = { try scala.reflect.internal.util.OwnerOnlyChmod.chmodFileOrCreateEmpty(p) @@ -109,7 +213,8 @@ object Reader { } } def backupHistory(): Unit = { - import java.nio.file.{Files, Paths, StandardCopyOption}, StandardCopyOption.REPLACE_EXISTING + import java.nio.file.{Files, Paths, StandardCopyOption} + import StandardCopyOption.REPLACE_EXISTING val hf = Paths.get(config.historyFile) val bk = Paths.get(config.historyFile + ".bk") Files.move(/*source =*/ hf, /*target =*/ bk, REPLACE_EXISTING) @@ -167,6 +272,12 @@ object Reader { val (wordCursor, wordIndex) = current match { case Some(t) if t.isIdentifier => (cursor - t.start, tokens.indexOf(t)) + case Some(t) => + val isIdentifierStartKeyword = (t.start until t.end).forall(i => Chars.isIdentifierPart(line.charAt(i))) + if (isIdentifierStartKeyword) + (cursor - t.start, tokens.indexOf(t)) + else + (0, -1) case _ => (0, -1) } @@ -223,47 +334,53 @@ object Reader { * It delegates both interfaces to an underlying `Completion`. */ class Completion(delegate: shell.Completion) extends shell.Completion with Completer { + var lastPrefix: String = "" require(delegate != null) // REPL Completion - def complete(buffer: String, cursor: Int): shell.CompletionResult = delegate.complete(buffer, cursor) + def complete(buffer: String, cursor: Int, filter: Boolean): shell.CompletionResult = delegate.complete(buffer, cursor, filter) // JLine Completer def complete(lineReader: LineReader, parsedLine: ParsedLine, newCandidates: JList[Candidate]): Unit = { - def candidateForResult(cc: CompletionCandidate): Candidate = { - val value = cc.defString - val displayed = cc.defString + (cc.arity match { + def candidateForResult(cc: CompletionCandidate, deprecated: Boolean, universal: Boolean): Candidate = { + val value = cc.name + val displayed = cc.name + (cc.arity match { case CompletionCandidate.Nullary => "" case CompletionCandidate.Nilary => "()" case _ => "(" }) val group = null // results may be grouped val descr = // displayed alongside - if (cc.isDeprecated) "deprecated" - else if (cc.isUniversal) "universal" + if (deprecated) "deprecated" + else if (universal) "universal" else null val suffix = null // such as slash after directory name val key = null // same key implies mergeable result val complete = false // more to complete? new Candidate(value, displayed, group, descr, suffix, key, complete) } - val result = complete(parsedLine.line, parsedLine.cursor) - result.candidates.map(_.defString) match { - // the presence of the empty string here is a signal that the symbol - // is already complete and so instead of completing, we want to show - // the user the method signature. there are various JLine 3 features - // one might use to do this instead; sticking to basics for now - case "" :: defStrings if defStrings.nonEmpty => - // specifics here are cargo-culted from Ammonite - lineReader.getTerminal.writer.println() - for (cc <- result.candidates.tail) - lineReader.getTerminal.writer.println(cc.defString) - lineReader.callWidget(LineReader.REDRAW_LINE) - lineReader.callWidget(LineReader.REDISPLAY) - lineReader.getTerminal.flush() - // normal completion - case _ => - for (cc <- result.candidates) - newCandidates.add(candidateForResult(cc)) + val result = complete(parsedLine.line, parsedLine.cursor, filter = false) + for (group <- result.candidates.groupBy(_.name)) { + // scala/bug#12238 + // Currently, only when all methods are Deprecated should they be displayed `Deprecated` to users. Only handle result of PresentationCompilation#toCandidates. + // We don't handle result of PresentationCompilation#defStringCandidates, because we need to show the deprecated here. + val allDeprecated = group._2.forall(_.isDeprecated) + val allUniversal = group._2.forall(_.isUniversal) + group._2.foreach(cc => newCandidates.add(candidateForResult(cc, allDeprecated, allUniversal))) + } + + val parsedLineWord = parsedLine.word() + result.candidates.filter(_.name == parsedLineWord) match { + case Nil => + case exacts => + val declStrings = exacts.map(_.declString()).filterNot(_ == "") + if (declStrings.nonEmpty) { + lineReader.getTerminal.writer.println() + for (declString <- declStrings) + lineReader.getTerminal.writer.println(declString) + lineReader.callWidget(LineReader.REDRAW_LINE) + lineReader.callWidget(LineReader.REDISPLAY) + lineReader.getTerminal.flush() + } } } } diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/Completion.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/Completion.scala index 6e5585d86596..389dd194e824 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/Completion.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/Completion.scala @@ -14,22 +14,23 @@ package scala.tools.nsc.interpreter package shell trait Completion { - def complete(buffer: String, cursor: Int): CompletionResult + final def complete(buffer: String, cursor: Int): CompletionResult = complete(buffer, cursor, filter = true) + def complete(buffer: String, cursor: Int, filter: Boolean): CompletionResult } object NoCompletion extends Completion { - def complete(buffer: String, cursor: Int) = NoCompletions + def complete(buffer: String, cursor: Int, filter: Boolean) = NoCompletions } -case class CompletionResult(cursor: Int, candidates: List[CompletionCandidate]) { +case class CompletionResult(line: String, cursor: Int, candidates: List[CompletionCandidate], typeAtCursor: String = "", typedTree: String = "") { final def orElse(other: => CompletionResult): CompletionResult = if (candidates.nonEmpty) this else other } object CompletionResult { val empty: CompletionResult = NoCompletions } -object NoCompletions extends CompletionResult(-1, Nil) +object NoCompletions extends CompletionResult("", -1, Nil, "", "") case class MultiCompletion(underlying: Completion*) extends Completion { - override def complete(buffer: String, cursor: Int) = - underlying.foldLeft(CompletionResult.empty)((r,c) => r.orElse(c.complete(buffer, cursor))) + override def complete(buffer: String, cursor: Int, filter: Boolean) = + underlying.foldLeft(CompletionResult.empty)((r,c) => r.orElse(c.complete(buffer, cursor, filter))) } diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/ILoop.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/ILoop.scala index b925c595a819..8f51bc84e691 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/ILoop.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/ILoop.scala @@ -200,10 +200,10 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, cmd("load", "", "interpret lines in a file", loadCommand, fileCompletion), cmd("paste", "[-raw] [path]", "enter paste mode or paste a file", pasteCommand, fileCompletion), nullary("power", "enable power user mode", () => powerCmd()), - nullary("quit", "exit the interpreter", () => Result(keepRunning = false, None)), - cmd("replay", "[options]", "reset the repl and replay all previous commands", replayCommand, settingsCompletion), + nullary("quit", "exit the REPL", () => Result(keepRunning = false, None)), + cmd("replay", "[options]", "reset the REPL and replay all previous commands", replayCommand, settingsCompletion), cmd("require", "", "add a jar to the classpath", require), - cmd("reset", "[options]", "reset the repl to its initial state, forgetting all session entries", resetCommand, settingsCompletion), + cmd("reset", "[options]", "reset the REPL to its initial state, forgetting all session entries", resetCommand, settingsCompletion), cmd("save", "", "save replayable session to a file", saveCommand, fileCompletion), shCommand, cmd("settings", "", "update compiler options, if possible; see reset", changeSettings, settingsCompletion), @@ -223,15 +223,15 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, val emptyWord = """(\s+)$""".r.unanchored val directorily = """(\S*/)$""".r.unanchored val trailingWord = """(\S+)$""".r.unanchored - def listed(i: Int, dir: Option[Path]) = + def listed(buffer: String, i: Int, dir: Option[Path]) = dir.filter(_.isDirectory) - .map(d => CompletionResult(i, d.toDirectory.list.map(x => CompletionCandidate(x.name)).toList)) + .map(d => CompletionResult(buffer, i, d.toDirectory.list.map(x => CompletionCandidate(x.name)).toList)) .getOrElse(NoCompletions) def listedIn(dir: Directory, name: String) = dir.list.filter(_.name.startsWith(name)).map(_.name).toList - def complete(buffer: String, cursor: Int): CompletionResult = + def complete(buffer: String, cursor: Int, filter: Boolean): CompletionResult = buffer.substring(0, cursor) match { - case emptyWord(s) => listed(cursor, Directory.Current) - case directorily(s) => listed(cursor, Option(Path(s))) + case emptyWord(s) => listed(buffer, cursor, Directory.Current) + case directorily(s) => listed(buffer, cursor, Option(Path(s))) case trailingWord(s) => val f = File(s) val (i, maybes) = @@ -239,7 +239,7 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, else if (f.isDirectory) (cursor - s.length, List(s"${f.toAbsolute.path}/")) else if (f.parent.exists) (cursor - f.name.length, listedIn(f.parent.toDirectory, f.name)) else (-1, Nil) - if (maybes.isEmpty) NoCompletions else CompletionResult(i, maybes.map(CompletionCandidate(_))) + if (maybes.isEmpty) NoCompletions else CompletionResult(buffer, i, maybes.map(CompletionCandidate(_))) case _ => NoCompletions } } @@ -247,13 +247,13 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, // complete settings name val settingsCompletion: Completion = new Completion { val trailingWord = """(\S+)$""".r.unanchored - def complete(buffer: String, cursor: Int): CompletionResult = { + def complete(buffer: String, cursor: Int, filter: Boolean): CompletionResult = { buffer.substring(0, cursor) match { case trailingWord(s) => - val maybes = intp.visibleSettings.filter(_.name.startsWith(s)).map(_.name) + val maybes = intp.visibleSettings.filter(x => if (filter) x.name.startsWith(s) else true).map(_.name) .filterNot(cond(_) { case "-"|"-X"|"-Y" => true }).sorted if (maybes.isEmpty) NoCompletions - else CompletionResult(cursor - s.length, maybes.map(CompletionCandidate(_))) + else CompletionResult(buffer, cursor - s.length, maybes.map(CompletionCandidate(_)), "", "") case _ => NoCompletions } } @@ -512,7 +512,7 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, */ def resetCommand(line: String): Unit = { def run(destructive: Boolean): Unit = { - echo("Resetting interpreter state.") + echo("Resetting REPL state.") if (replayCommandStack.nonEmpty) { echo("Forgetting this session history:\n") replayCommands foreach echo @@ -541,8 +541,8 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, MultiCompletion(shellCompletion, rc) } val shellCompletion = new Completion { - override def complete(buffer: String, cursor: Int) = - if (buffer.startsWith(":")) colonCompletion(buffer, cursor).complete(buffer, cursor) + override def complete(buffer: String, cursor: Int, filter: Boolean) = + if (buffer.startsWith(":")) colonCompletion(buffer, cursor).complete(buffer, cursor, filter) else NoCompletions } @@ -554,13 +554,13 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, // condition here is a bit weird because of the weird hack we have where // the first candidate having an empty defString means it's not really // completion, but showing the method signature instead - if (candidates.headOption.exists(_.defString.nonEmpty)) { + if (candidates.headOption.exists(_.name.nonEmpty)) { val prefix = if (completions == NoCompletions) "" else what.substring(0, completions.cursor) // hvesalai (emacs sbt-mode maintainer) says it's important to echo only once and not per-line echo( - candidates.map(c => s"[completions] $prefix${c.defString}") + candidates.map(c => s"[completions] $prefix${c.name}") .mkString("\n") ) } diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/LoopCommands.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/LoopCommands.scala index b2540e4817bd..441829d86e15 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/LoopCommands.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/LoopCommands.scala @@ -14,7 +14,6 @@ package scala.tools.nsc.interpreter package shell import java.io.{PrintWriter => JPrintWriter} - import scala.language.implicitConversions import scala.collection.mutable.ListBuffer import scala.tools.nsc.interpreter.ReplStrings.words @@ -60,6 +59,7 @@ trait LoopCommands { // subclasses may provide completions def completion: Completion = NoCompletion + override def toString(): String = name } object LoopCommand { def nullary(name: String, help: String, f: () => Result): LoopCommand = @@ -135,15 +135,15 @@ trait LoopCommands { case cmd :: Nil if !cursorAtName => cmd.completion case cmd :: Nil if cmd.name == name => NoCompletion case cmd :: Nil => - val completion = if (cmd.isInstanceOf[NullaryCmd] || cursor < line.length) cmd.name else cmd.name + " " + val completion = ":" + cmd.name new Completion { - def complete(buffer: String, cursor: Int) = - CompletionResult(cursor = 1, List(CompletionCandidate(completion))) + def complete(buffer: String, cursor: Int, filter: Boolean) = + CompletionResult(buffer, cursor = 1, List(CompletionCandidate(completion)), "", "") } case cmd :: rest => new Completion { - def complete(buffer: String, cursor: Int) = - CompletionResult(cursor = 1, cmds.map(cmd => CompletionCandidate(cmd.name))) + def complete(buffer: String, cursor: Int, filter: Boolean) = + CompletionResult(buffer, cursor = 1, cmds.map(cmd => CompletionCandidate(":" + cmd.name)), "", "") } } case _ => NoCompletion diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/ReplCompletion.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/ReplCompletion.scala index 8d0959e833c1..8d0fb56fba02 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/ReplCompletion.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/ReplCompletion.scala @@ -14,14 +14,12 @@ package scala.tools.nsc.interpreter package shell import scala.util.control.NonFatal -import scala.tools.nsc.interpreter.Repl -import scala.tools.nsc.interpreter.Naming /** Completion for the REPL. */ class ReplCompletion(intp: Repl, val accumulator: Accumulator = new Accumulator) extends Completion { - def complete(buffer: String, cursor: Int): CompletionResult = { + def complete(buffer: String, cursor: Int, filter: Boolean): CompletionResult = { // special case for: // // scala> 1 @@ -32,13 +30,13 @@ class ReplCompletion(intp: Repl, val accumulator: Accumulator = new Accumulator) val bufferWithMultiLine = accumulator.toString + bufferWithVar val cursor1 = cursor + (bufferWithMultiLine.length - buffer.length) - codeCompletion(bufferWithMultiLine, cursor1) + codeCompletion(bufferWithMultiLine, cursor1, filter) } // A convenience for testing def complete(before: String, after: String = ""): CompletionResult = complete(before + after, before.length) - private def codeCompletion(buf: String, cursor: Int): CompletionResult = { + private def codeCompletion(buf: String, cursor: Int, filter: Boolean): CompletionResult = { require(cursor >= 0 && cursor <= buf.length) // secret handshakes @@ -50,23 +48,25 @@ class ReplCompletion(intp: Repl, val accumulator: Accumulator = new Accumulator) case Left(_) => NoCompletions case Right(result) => try { buf match { - case slashPrint() if cursor == buf.length => - CompletionResult(cursor, CompletionCandidate.fromStrings("" :: Naming.unmangle(result.print) :: Nil)) - case slashPrintRaw() if cursor == buf.length => - CompletionResult(cursor, CompletionCandidate.fromStrings("" :: result.print :: Nil)) + case slashPrint() if cursor == buf.length => + CompletionResult(buf, cursor, CompletionCandidate.fromStrings("" :: Naming.unmangle(result.print) :: Nil), "", "") + case slashPrintRaw() if cursor == buf.length => + CompletionResult(buf, cursor, CompletionCandidate.fromStrings("" :: result.print :: Nil), "", "") case slashTypeAt(start, end) if cursor == buf.length => - CompletionResult(cursor, CompletionCandidate.fromStrings("" :: result.typeAt(start.toInt, end.toInt) :: Nil)) - case _ => + CompletionResult(buf, cursor, CompletionCandidate.fromStrings("" :: result.typeAt(start.toInt, end.toInt) :: Nil), "", "") + case _ => // under JLine 3, we no longer use the tabCount concept, so tabCount is always 1 // which always gives us all completions - val (c, r) = result.completionCandidates(tabCount = 1) - CompletionResult(c, r) + val (c, r) = result.completionCandidates(filter, tabCount = 1) + val typeAtCursor = result.typeAt(cursor, cursor) + CompletionResult(buf, c, r, typeAtCursor, result.print) } } finally result.cleanup() } } catch { case NonFatal(e) => - // e.printStackTrace() +// if (intp.settings.debug) + e.printStackTrace() NoCompletions } } diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index a38b1075490b..27a6af6b5a00 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -735,7 +735,10 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade compile(new CompilationUnit(new BatchSourceFile(label, packaged(code)))) def compile(unit: CompilationUnit): Boolean = { + val oldRunReporting = currentRun.reporting val run = new Run() + // The unit is already parsed and won't be parsed again. This makes sure suspended warnings are not discarded. + run.reporting.initFrom(oldRunReporting) assert(run.typerPhase != NoPhase, "REPL requires a typer phase.") run.compileUnits(unit :: Nil) @@ -784,9 +787,12 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade // The source file contents only has the code originally input by the user, // with unit's body holding the synthetic trees. // When emitting errors, be careful not to refer to the synthetic code - private val unit = new CompilationUnit(new BatchSourceFile(if (synthetic) "" else label, line)) + // pad with a trailing " " so that the synthetic position for enclosing trees does not exactly coincide with the + // position of the user-written code, these seems to confuse the presentation compiler. + private val paddedLine = line + " " + private val unit = new CompilationUnit(new BatchSourceFile(if (synthetic) "" else label, paddedLine)) // a dummy position used for synthetic trees (needed for pres compiler to locate the trees for user input) - private val wholeUnit = Position.range(unit.source, 0, 0, line.length) + private val wholeUnit = Position.range(unit.source, 0, 0, paddedLine.length) private def storeInVal(tree: Tree): Tree = { val resName = newTermName(if (synthetic) freshInternalVarName() else freshUserVarName()) @@ -794,12 +800,16 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade } // Wrap last tree in a valdef to give user a nice handle for it (`resN`) - val trees: List[Tree] = - origTrees.init :+ (origTrees.last match { - case tree@(_: Assign) => tree - case tree@(_: RefTree | _: TermTree) => storeInVal(tree) - case tree => tree - }) + val trees: List[Tree] = origTrees.init :+ { + val tree = origTrees.last + @tailrec def loop(scrut: Tree): Tree = scrut match { + case _: Assign => tree + case _: RefTree | _: TermTree => storeInVal(tree) + case Annotated(_, arg) => loop(arg) + case _ => tree + } + loop(tree) + } /** handlers for each tree in this request */ val handlers: List[MemberHandler] = trees map (memberHandlers chooseHandler _) @@ -882,13 +892,13 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade else ModuleDef(NoMods, readName, wrapperTempl)) if (isClassBased) - stats += q"""object $readName { val INSTANCE = new ${tq"""${readName.toTypeName}"""} }""" + stats += atPos(wholeUnit.focus)(q"""object $readName { val INSTANCE = new ${tq"""${readName.toTypeName}"""} }""") val unspliced = PackageDef(atPos(wholeUnit.focus)(Ident(lineRep.packageName)), stats.toList) unit.body = spliceUserCode.transform(unspliced) unit.encounteredXml(firstXmlPos) -// settings.Xprintpos.value = true + // settings.Xprintpos.value = true showCode(asCompactString(unit.body)) unit diff --git a/src/repl/scala/tools/nsc/interpreter/Interface.scala b/src/repl/scala/tools/nsc/interpreter/Interface.scala index 73f27ed749e9..790750daf367 100644 --- a/src/repl/scala/tools/nsc/interpreter/Interface.scala +++ b/src/repl/scala/tools/nsc/interpreter/Interface.scala @@ -323,21 +323,24 @@ trait PresentationCompilationResult { def candidates(tabCount: Int): (Int, List[String]) = completionCandidates(tabCount) match { case (cursor, cands) => - (cursor, cands.map(_.defString)) + (cursor, cands.map(_.name)) } - def completionCandidates(tabCount: Int = -1): (Int, List[CompletionCandidate]) + final def completionCandidates(tabCount: Int = -1): (Int, List[CompletionCandidate]) = completionCandidates(filter = true, tabCount) + def completionCandidates(filter: Boolean, tabCount: Int): (Int, List[CompletionCandidate]) } case class CompletionCandidate( - defString: String, + name: String, arity: CompletionCandidate.Arity = CompletionCandidate.Nullary, isDeprecated: Boolean = false, - isUniversal: Boolean = false) + isUniversal: Boolean = false, + declString: () => String = () => "") object CompletionCandidate { sealed trait Arity case object Nullary extends Arity case object Nilary extends Arity + case object Infix extends Arity case object Other extends Arity // purely for convenience def fromStrings(defStrings: List[String]): List[CompletionCandidate] = diff --git a/src/repl/scala/tools/nsc/interpreter/Naming.scala b/src/repl/scala/tools/nsc/interpreter/Naming.scala index 344e1f84ee4b..5b6dab253480 100644 --- a/src/repl/scala/tools/nsc/interpreter/Naming.scala +++ b/src/repl/scala/tools/nsc/interpreter/Naming.scala @@ -30,7 +30,7 @@ object Naming { // group 1 is the CSI command letter, where 'm' is color rendition // group 2 is a sequence of chars to be rendered as `?`: anything non-printable and not some space char // additional groups are introduced by linePattern but not used - private lazy val cleaner = raw"$csi|([^\p{Print}\p{Space}]+)|$linePattern".r + private lazy val cleaner = raw"$csi|([\p{Cntrl}&&[^\p{Space}]]+)|$linePattern".r /** Final pass to clean up REPL output. * diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala index 91df89362548..4adaaae8c4f2 100644 --- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala @@ -12,7 +12,9 @@ package scala.tools.nsc.interpreter -import scala.reflect.internal.util.{Position, RangePosition, StringOps} +import scala.collection.mutable +import scala.reflect.internal.util.{Position, RangePosition} +import scala.tools.nsc.ast.parser.Tokens import scala.tools.nsc.backend.JavaPlatform import scala.tools.nsc.util.ClassPath import scala.tools.nsc.{Settings, interactive} @@ -22,7 +24,7 @@ import scala.tools.nsc.interpreter.Results.{Error, Result} trait PresentationCompilation { self: IMain => - private final val Cursor = IMain.DummyCursorFragment + " " + private final val Cursor = IMain.DummyCursorFragment /** Typecheck a line of REPL input, suitably wrapped with "interpreter wrapper" objects/classes, with the * presentation compiler. The result of this method gives access to the typechecked tree and to autocompletion @@ -34,7 +36,24 @@ trait PresentationCompilation { self: IMain => if (global == null) Left(Error) else { val pc = newPresentationCompiler() - val line1 = buf.patch(cursor, Cursor, 0) + def cursorIsInKeyword(): Boolean = { + val scanner = pc.newUnitParser(buf).newScanner() + scanner.init() + while (scanner.token != Tokens.EOF) { + val token = scanner.token + val o = scanner.offset + scanner.nextToken() + if ((o to scanner.lastOffset).contains(cursor)) { + return (!Tokens.isIdentifier(token) && pc.syntaxAnalyzer.token2name.contains(token)) + } + } + false + } + // Support completion of "def format = 42; for" by replacing the keyword with foo_CURSOR_ before + // typechecking. Only do this when needed to be able ot correctly return the type of `foo.bar` + // where `bar` is the complete name of a member. + val line1 = if (!cursorIsInKeyword()) buf else buf.patch(cursor, Cursor, 0) + val trees = pc.newUnitParser(line1).parseStats() val importer = global.mkImporter(pc) //println(s"pc: [[$line1]], <<${trees.size}>>") @@ -89,8 +108,6 @@ trait PresentationCompilation { self: IMain => interactiveGlobal } - private var lastCommonPrefixCompletion: Option[String] = None - abstract class PresentationCompileResult(val compiler: interactive.Global, val inputRange: Position, val cursor: Int, val buf: String) extends PresentationCompilationResult { val unit: compiler.RichCompilationUnit // depmet broken for constructors, can't be ctor arg @@ -120,15 +137,23 @@ trait PresentationCompilation { self: IMain => } } - def typeString(tree: compiler.Tree): String = - compiler.exitingTyper(tree.tpe.toString) + def typeString(tree: compiler.Tree): String = { + tree.tpe match { + case null | compiler.NoType | compiler.ErrorType => "" + case tp => compiler.exitingTyper(tp.toString) + } + } def treeString(tree: compiler.Tree): String = compiler.showCode(tree) override def print = { val tree = treeAt(inputRange) - treeString(tree) + " // : " + tree.tpe.safeToString + val tpString = typeString(tree) match { + case "" => "" + case s => " // : " + s + } + treeString(tree) + tpString } @@ -138,7 +163,7 @@ trait PresentationCompilation { self: IMain => val NoCandidates = (-1, Nil) type Candidates = (Int, List[CompletionCandidate]) - override def completionCandidates(tabCount: Int): Candidates = { + override def completionCandidates(filter: Boolean, tabCount: Int): Candidates = { import compiler._ import CompletionResult.NoResults @@ -161,76 +186,56 @@ trait PresentationCompilation { self: IMain => if (m.sym.paramss.isEmpty) CompletionCandidate.Nullary else if (m.sym.paramss.size == 1 && m.sym.paramss.head.isEmpty) CompletionCandidate.Nilary else CompletionCandidate.Other - def defStringCandidates(matching: List[Member], name: Name, isNew: Boolean): Candidates = { + def defStringCandidates(matching: List[Member], name: Name, isNew: Boolean) = { + val seen = new mutable.HashSet[Symbol]() val ccs = for { member <- matching - if member.symNameDropLocal == name + if seen.add(member.sym) sym <- if (member.sym.isClass && isNew) member.sym.info.decl(nme.CONSTRUCTOR).alternatives else member.sym.alternatives sugared = sym.sugaredSymbolOrSelf } yield { - val tp = member.prefix memberType sym - val desc = Seq(if (isMemberDeprecated(member)) "(deprecated)" else "", if (isMemberUniversal(member)) "(universal)" else "") - val methodOtherDesc = if (!desc.exists(_ != "")) "" else " " + desc.filter(_ != "").mkString(" ") CompletionCandidate( - defString = sugared.defStringSeenAs(tp) + methodOtherDesc, + name = member.symNameDropLocal.decoded, arity = memberArity(member), isDeprecated = isMemberDeprecated(member), - isUniversal = isMemberUniversal(member)) + isUniversal = isMemberUniversal(member), + declString = () => { + if (sym.isPackageObjectOrClass) "" + else { + val tp = member.prefix memberType sym + val desc = Seq(if (isMemberDeprecated(member)) "(deprecated)" else "", if (isMemberUniversal(member)) "(universal)" else "") + val methodOtherDesc = if (!desc.exists(_ != "")) "" else " " + desc.filter(_ != "").mkString(" ") + sugared.defStringSeenAs(tp) + methodOtherDesc + } + }) } - (cursor, CompletionCandidate("") :: ccs.distinct) + ccs } - def toCandidates(members: List[Member]): List[CompletionCandidate] = - members - .map(m => CompletionCandidate(m.symNameDropLocal.decoded, memberArity(m), isMemberDeprecated(m), isMemberUniversal(m))) - .sortBy(_.defString) val found = this.completionsAt(cursor) match { case NoResults => NoCandidates case r => def shouldHide(m: Member): Boolean = - tabCount == 0 && (isMemberDeprecated(m) || isMemberUniversal(m)) - val matching = r.matchingResults().filterNot(shouldHide) - val tabAfterCommonPrefixCompletion = lastCommonPrefixCompletion.contains(buf.substring(inputRange.start, cursor)) && matching.exists(_.symNameDropLocal == r.name) - val doubleTab = tabCount > 0 && matching.forall(_.symNameDropLocal == r.name) - if (tabAfterCommonPrefixCompletion || doubleTab) { - val pos1 = positionOf(cursor) - import compiler._ - val locator = new Locator(pos1) - val tree = locator locateIn unit.body - var isNew = false - new TreeStackTraverser { - override def traverse(t: Tree): Unit = { - if (t eq tree) { - isNew = path.dropWhile { case _: Select | _: Annotated => true; case _ => false}.headOption match { - case Some(_: New) => true - case _ => false - } - } else super.traverse(t) - } - }.traverse(unit.body) - defStringCandidates(matching, r.name, isNew) - } else if (matching.isEmpty) { - // Lenient matching based on camel case and on eliding JavaBean "get" / "is" boilerplate - val camelMatches: List[Member] = r.matchingResults(CompletionResult.camelMatch(_)).filterNot(shouldHide) - val memberCompletions: List[CompletionCandidate] = toCandidates(camelMatches) - def allowCompletion = ( - (memberCompletions.size == 1) - || CompletionResult.camelMatch(r.name)(r.name.newName(StringOps.longestCommonPrefix(memberCompletions.map(_.defString)))) - ) - if (memberCompletions.isEmpty) NoCandidates - else if (allowCompletion) (cursor - r.positionDelta, memberCompletions) - else (cursor, CompletionCandidate("") :: memberCompletions) - } else if (matching.nonEmpty && matching.forall(_.symNameDropLocal == r.name)) - NoCandidates // don't offer completion if the only option has been fully typed already - else { - // regular completion - (cursor - r.positionDelta, toCandidates(matching)) - } + filter && tabCount == 0 && (isMemberDeprecated(m) || isMemberUniversal(m)) + val matching = r.matchingResults(nameMatcher = if (filter) {entered => candidate => candidate.startsWith(entered)} else _ => _ => true).filterNot(shouldHide) + val pos1 = positionOf(cursor) + import compiler._ + val locator = new Locator(pos1) + val tree = locator locateIn unit.body + var isNew = false + new TreeStackTraverser { + override def traverse(t: Tree): Unit = { + if (t eq tree) { + isNew = path.dropWhile { case _: Select | _: Annotated => true; case _ => false}.headOption match { + case Some(_: New) => true + case _ => false + } + } else super.traverse(t) + } + }.traverse(unit.body) + val candidates = defStringCandidates(matching, r.name, isNew) + val pos = cursor - r.positionDelta + (pos, candidates.sortBy(_.name)) } - lastCommonPrefixCompletion = - if (found != NoCandidates && buf.length >= found._1) - Some(buf.substring(inputRange.start, found._1) + StringOps.longestCommonPrefix(found._2.map(_.defString))) - else - None found } diff --git a/src/scaladoc/scala/tools/nsc/ScalaDoc.scala b/src/scaladoc/scala/tools/nsc/ScalaDoc.scala index 644d0b839ed2..3ddbe03c9b35 100644 --- a/src/scaladoc/scala/tools/nsc/ScalaDoc.scala +++ b/src/scaladoc/scala/tools/nsc/ScalaDoc.scala @@ -48,7 +48,7 @@ class ScalaDoc { try { new DocFactory(reporter, docSettings) document command.files } catch { case ex @ FatalError(msg) => - if (docSettings.debug.value) ex.printStackTrace() + if (docSettings.isDebug) ex.printStackTrace() reporter.error(null, "fatal error: " + msg) } finally reporter.finish() diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala index ab5ebf0f17aa..e361e7299010 100644 --- a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala +++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala @@ -96,7 +96,7 @@ trait ScaladocAnalyzer extends Analyzer { typedStats(trees, NoSymbol) useCase.defined = context.scope.toList filterNot (useCase.aliases contains _) - if (settings.debug) + if (settings.isDebug) useCase.defined foreach (sym => println("defined use cases: %s:%s".format(sym, sym.tpe))) useCase.defined diff --git a/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala b/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala index 00a888b3f65f..bdec5a30f6b6 100644 --- a/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala +++ b/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala @@ -12,7 +12,9 @@ package scala.tools.nsc package doc + import scala.language.implicitConversions + import scala.reflect.internal.util.NoPosition import scala.tools.nsc.Reporting.WarningCategory @@ -63,7 +65,7 @@ trait Uncompilable { def symbols = pairs map (_._1) def templates = symbols.filter(x => x.isClass || x.isTrait || x == AnyRefClass/* which is now a type alias */).toSet def comments = { - if (settings.debug || settings.verbose) + if (settings.isDebug || settings.verbose) inform("Found %d uncompilable files: %s".format(files.size, files mkString ", ")) if (pairs.isEmpty) diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala index 3c82654fb515..32a0cbca5840 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala @@ -341,7 +341,7 @@ trait EntityPage extends HtmlPage { val postamble = List(Div(id = "tooltip"), if (Set("epfl", "EPFL").contains(tpl.universe.settings.docfooter.value)) - Div(id = "footer", elems = Txt("Scala programming documentation. Copyright (c) 2002-2020 ") :: A(href = "https://www.epfl.ch", target = "_top", elems = Txt("EPFL")) :: Txt(" and ") :: A(href = "https://www.lightbend.com", target = "_top", elems = Txt("Lightbend")) :: Txt(".")) + Div(id = "footer", elems = Txt("Scala programming documentation. Copyright (c) 2002-2021 ") :: A(href = "https://www.epfl.ch", target = "_top", elems = Txt("EPFL")) :: Txt(" and ") :: A(href = "https://www.lightbend.com", target = "_top", elems = Txt("Lightbend")) :: Txt(".")) else Div(id = "footer", elems = Txt(tpl.universe.settings.docfooter.value))) diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js index d6935dd01ee5..e8b44e9b6744 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js @@ -61,7 +61,7 @@ $(document).ready(function() { return $(elem).attr("data-hidden") == 'true'; }; - $("#linearization li:gt(0)").filter(function(){ + $("#linearization li").slice(1).filter(function(){ return isHiddenClass($(this).attr("name")); }).removeClass("in").addClass("out"); @@ -440,7 +440,7 @@ function filter() { var orderingAlphabetic = $("#order > ol > li.alpha").hasClass("in"); var orderingInheritance = $("#order > ol > li.inherit").hasClass("in"); var orderingGroups = $("#order > ol > li.group").hasClass("in"); - var hiddenSuperclassElementsLinearization = orderingInheritance ? $("#linearization > li:gt(0)") : $("#linearization > li.out"); + var hiddenSuperclassElementsLinearization = orderingInheritance ? $("#linearization > li").slice(1) : $("#linearization > li.out"); var hiddenSuperclassesLinearization = hiddenSuperclassElementsLinearization.map(function() { return $(this).attr("name"); }).get(); diff --git a/src/scalap/decoder.properties b/src/scalap/decoder.properties index 3607f029f024..9ac03dd79c51 100644 --- a/src/scalap/decoder.properties +++ b/src/scalap/decoder.properties @@ -1,2 +1,2 @@ version.number=2.0.1 -copyright.string=(c) 2002-2020 LAMP/EPFL +copyright.string=(c) 2002-2021 LAMP/EPFL diff --git a/src/tastytest/dotty/tools/vulpix/ParallelTesting.scala b/src/tastytest/dotty/tools/vulpix/ParallelTesting.scala new file mode 100644 index 000000000000..fc1245e47de7 --- /dev/null +++ b/src/tastytest/dotty/tools/vulpix/ParallelTesting.scala @@ -0,0 +1,11 @@ +package dotty.tools.vulpix + +/** As of Scala 3.0.0-RC2, dotty compiler will enable the + * usage of experimental features if the compiler is invoked + * within a method on the class `dotty.tools.vulpix.ParallelTesting` + * + * We use this to test experimental features on non-nightly releases. + */ +class ParallelTesting { + def unlockExperimentalFeatures[T](op: => T): T = op +} diff --git a/src/tastytest/scala/tools/tastytest/ClasspathOps.scala b/src/tastytest/scala/tools/tastytest/ClasspathOps.scala new file mode 100644 index 000000000000..257eacf1d781 --- /dev/null +++ b/src/tastytest/scala/tools/tastytest/ClasspathOps.scala @@ -0,0 +1,10 @@ +package scala.tools.tastytest + +import java.net.URL +import java.nio.file.Paths + +object ClasspathOps { + implicit class ClassPathSyntax(private val ls: List[String]) extends AnyVal { + def asURLs: List[URL] = ls.map(Paths.get(_).toUri().toURL()) + } +} diff --git a/src/tastytest/scala/tools/tastytest/Classpaths.scala b/src/tastytest/scala/tools/tastytest/Classpaths.scala new file mode 100644 index 000000000000..5458966fe74d --- /dev/null +++ b/src/tastytest/scala/tools/tastytest/Classpaths.scala @@ -0,0 +1,17 @@ +package scala.tools.tastytest + +import scala.util.Properties +import java.io.File.pathSeparatorChar + +object Classpaths { + + private def classpathProp(name: String) = + Properties.propOrNone(name).map(_.split(pathSeparatorChar).filter(_.nonEmpty).toList).getOrElse(Nil) + + def dottyCompiler: List[String] = classpathProp("tastytest.classpaths.dottyCompiler") + + def scalaReflect: List[String] = classpathProp("tastytest.classpaths.scalaReflect") + + def dottyLibrary: List[String] = classpathProp("tastytest.classpaths.dottyLibrary") + +} diff --git a/src/tastytest/scala/tools/tastytest/Dotc.scala b/src/tastytest/scala/tools/tastytest/Dotc.scala index 0c0a7ebf3c8b..e36399c5e08a 100644 --- a/src/tastytest/scala/tools/tastytest/Dotc.scala +++ b/src/tastytest/scala/tools/tastytest/Dotc.scala @@ -1,40 +1,73 @@ package scala.tools.tastytest -import scala.util.{ Try, Success } +import scala.util.{Try, Success, Failure} +import scala.util.control.NonFatal -import java.lang.reflect.Modifier +import scala.reflect.internal.util.ScalaClassLoader +import scala.reflect.runtime.ReflectionUtils +import java.lang.reflect.{Modifier, Method} + +import ClasspathOps._ object Dotc extends Script.Command { - private[this] lazy val dotcProcess = processMethod("dotty.tools.dotc.Main") + final case class ClassLoader private (val parent: ScalaClassLoader) + + def initClassloader(): Try[Dotc.ClassLoader] = + Try(Dotc.ClassLoader(ScalaClassLoader.fromURLs(Classpaths.dottyCompiler.asURLs))) - def processMethod(mainClassName: String): Array[String] => Try[Boolean] = { - // TODO call it directly when we are bootstrapped - val mainClass = Class.forName(mainClassName) - val reporterClass = Class.forName("dotty.tools.dotc.reporting.Reporter") + def loadClass(name: String)(implicit cl: Dotc.ClassLoader) = + Class.forName(name, true, cl.parent) + + def invokeStatic(method: Method, args: Seq[Any])(implicit cl: Dotc.ClassLoader) = { + assert(Modifier.isStatic(method.getModifiers), s"$method is not static!") + invoke(method, null, args) + } + + def invoke(method: Method, obj: AnyRef, args: Seq[Any])(implicit cl: Dotc.ClassLoader) = { + try cl.parent.asContext[AnyRef] { + method.invoke(obj, args.toArray:_*) + } + catch { + case NonFatal(ex) => throw ReflectionUtils.unwrapThrowable(ex) + } + } + + private def dotcProcess(args: Seq[String])(implicit cl: Dotc.ClassLoader) = processMethod("dotty.tools.dotc.Main")(args) + + def processMethod(mainClassName: String)(args: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Boolean] = { + val mainClass = loadClass(mainClassName) + val reporterClass = loadClass("dotty.tools.dotc.reporting.Reporter") val Main_process = mainClass.getMethod("process", classOf[Array[String]]) - assert(Modifier.isStatic(Main_process.getModifiers), s"$mainClassName.process is not static!") val Reporter_hasErrors = reporterClass.getMethod("hasErrors") - args => Try { - val reporter = Main_process.invoke(null, args) - val hasErrors = Reporter_hasErrors.invoke(reporter).asInstanceOf[Boolean] + Try { + val reporter = unlockExperimentalFeatures(invokeStatic(Main_process, Seq(args.toArray))) + val hasErrors = invoke(Reporter_hasErrors, reporter, Seq.empty).asInstanceOf[Boolean] !hasErrors } } - def dotc(out: String, classpath: String, additionalSettings: Seq[String], sources: String*): Try[Boolean] = { + def dotcVersion(implicit cl: Dotc.ClassLoader): String = { + val compilerPropertiesClass = loadClass("dotty.tools.dotc.config.Properties") + val Properties_simpleVersionString = compilerPropertiesClass.getMethod("simpleVersionString") + invokeStatic(Properties_simpleVersionString, Seq.empty).asInstanceOf[String] + } + + def dotc(out: String, classpath: String, additionalSettings: Seq[String], sources: String*)(implicit cl: Dotc.ClassLoader): Try[Boolean] = { if (sources.isEmpty) { Success(true) } else { - val args = Array( + val libraryDeps = Classpaths.dottyLibrary ++ Classpaths.scalaReflect + val args = Seq( "-d", out, - "-classpath", classpath, + "-classpath", libraryDeps.mkString(classpath + Files.classpathSep, Files.classpathSep, ""), "-deprecation", - "-Yerased-terms", "-Xfatal-warnings", - "-usejavacp" ) ++ additionalSettings ++ sources + if (TastyTest.verbose) { + println(yellow(s"Invoking dotc (version $dotcVersion) with args: $args")) + } dotcProcess(args) } } @@ -43,12 +76,18 @@ object Dotc extends Script.Command { val describe: String = s"$commandName " def process(args: String*): Int = { - if (args.length != 2) { - println(red(s"please provide two arguments in sub-command: $describe")) + if (args.length < 2) { + println(red(s"please provide at least two arguments in sub-command: $describe")) return 1 } - val Seq(out, src) = args: @unchecked - val success = dotc(out, out, Nil, src).get + val Seq(out, src, additional @ _*) = args: @unchecked + implicit val scala3classloader: Dotc.ClassLoader = initClassloader() match { + case Success(cl) => cl + case Failure(err) => + println(red(s"could not initialise Scala 3 classpath: $err")) + return 1 + } + val success = dotc(out, out, additional, src).get if (success) 0 else 1 } diff --git a/src/tastytest/scala/tools/tastytest/DotcDecompiler.scala b/src/tastytest/scala/tools/tastytest/DotcDecompiler.scala index ff53ccd782b4..c10582a42bd5 100644 --- a/src/tastytest/scala/tools/tastytest/DotcDecompiler.scala +++ b/src/tastytest/scala/tools/tastytest/DotcDecompiler.scala @@ -1,13 +1,14 @@ package scala.tools.tastytest -import scala.util.Try +import scala.util.{Try, Success, Failure} object DotcDecompiler extends Script.Command { - private[this] lazy val dotcProcess = Dotc.processMethod("dotty.tools.dotc.decompiler.Main") + private def dotcProcess(args: Seq[String])(implicit cl: Dotc.ClassLoader) = + Dotc.processMethod("dotty.tools.dotc.decompiler.Main")(args) - def decompile(source: String, additionalSettings: Seq[String]): Try[Boolean] = - dotcProcess(("-usejavacp" +: additionalSettings :+ source).toArray) + def decompile(source: String, additionalSettings: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Boolean] = + dotcProcess(("-usejavacp" +: additionalSettings :+ source)) val commandName: String = "dotcd" val describe: String = s"$commandName " @@ -18,6 +19,12 @@ object DotcDecompiler extends Script.Command { return 1 } val Seq(tasty, additionalSettings @ _*) = args: @unchecked + implicit val scala3classloader: Dotc.ClassLoader = Dotc.initClassloader() match { + case Success(cl) => cl + case Failure(err) => + println(red(s"could not initialise Scala 3 classpath: $err")) + return 1 + } val success = decompile(tasty, additionalSettings).get if (success) 0 else 1 } diff --git a/src/tastytest/scala/tools/tastytest/TastyTest.scala b/src/tastytest/scala/tools/tastytest/TastyTest.scala index be64ff8ca2f3..d3e9122adbdf 100644 --- a/src/tastytest/scala/tools/tastytest/TastyTest.scala +++ b/src/tastytest/scala/tools/tastytest/TastyTest.scala @@ -14,12 +14,12 @@ import Files._ object TastyTest { - private val verbose = false + private[tastytest] val verbose = false private def log(s: => String): Unit = if (verbose) println(s) - def runSuite(src: String, srcRoot: String, pkgName: String, outDir: Option[String], additionalSettings: Seq[String], additionalDottySettings: Seq[String]): Try[Unit] = for { + def runSuite(src: String, srcRoot: String, pkgName: String, outDir: Option[String], additionalSettings: Seq[String], additionalDottySettings: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Unit] = for { (pre, src2, src3) <- getRunSources(srcRoot/src) out <- outDir.fold(tempDir(pkgName))(dir) _ <- scalacPos(out, sourceRoot=srcRoot/src/"pre", additionalSettings, pre:_*) @@ -29,7 +29,7 @@ object TastyTest { _ <- runMainOn(out, testNames:_*) } yield () - def posSuite(src: String, srcRoot: String, pkgName: String, outDir: Option[String], additionalSettings: Seq[String], additionalDottySettings: Seq[String]): Try[Unit] = for { + def posSuite(src: String, srcRoot: String, pkgName: String, outDir: Option[String], additionalSettings: Seq[String], additionalDottySettings: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Unit] = for { (pre, src2, src3) <- getRunSources(srcRoot/src, preFilters = Set(Scala, Java)) _ = log(s"Sources to compile under test: ${src2.map(cyan).mkString(", ")}") out <- outDir.fold(tempDir(pkgName))(dir) @@ -39,14 +39,14 @@ object TastyTest { _ <- scalacPos(out, sourceRoot=srcRoot/src/"src-2", additionalSettings, src2:_*) } yield () - def negSuite(src: String, srcRoot: String, pkgName: String, outDir: Option[String], additionalSettings: Seq[String], additionalDottySettings: Seq[String]): Try[Unit] = for { + def negSuite(src: String, srcRoot: String, pkgName: String, outDir: Option[String], additionalSettings: Seq[String], additionalDottySettings: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Unit] = for { (src2, src3) <- get2And3Sources(srcRoot/src, src2Filters = Set(Scala, Check, SkipCheck)) out <- outDir.fold(tempDir(pkgName))(dir) _ <- dotcPos(out, sourceRoot=srcRoot/src/"src-3", additionalDottySettings, src3:_*) _ <- scalacNeg(out, additionalSettings, src2:_*) } yield () - def negChangePreSuite(src: String, srcRoot: String, pkgName: String, outDirs: Option[(String, String)], additionalSettings: Seq[String], additionalDottySettings: Seq[String]): Try[Unit] = for { + def negChangePreSuite(src: String, srcRoot: String, pkgName: String, outDirs: Option[(String, String)], additionalSettings: Seq[String], additionalDottySettings: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Unit] = for { (preA, preB, src2, src3) <- getMovePreChangeSources(srcRoot/src, src2Filters = Set(Scala, Check, SkipCheck)) (out1, out2) <- outDirs.fold(tempDir(pkgName) *> tempDir(pkgName))(p => dir(p._1) *> dir(p._2)) _ <- scalacPos(out1, sourceRoot=srcRoot/src/"pre-A", additionalSettings, preA:_*) @@ -55,7 +55,7 @@ object TastyTest { _ <- scalacNeg(out2, additionalSettings, src2:_*) } yield () - def negSuiteIsolated(src: String, srcRoot: String, pkgName: String, outDirs: Option[(String, String)], additionalSettings: Seq[String], additionalDottySettings: Seq[String]): Try[Unit] = for { + def negSuiteIsolated(src: String, srcRoot: String, pkgName: String, outDirs: Option[(String, String)], additionalSettings: Seq[String], additionalDottySettings: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Unit] = for { (src2, src3A, src3B) <- getNegIsolatedSources(srcRoot/src, src2Filters = Set(Scala, Check, SkipCheck)) (out1, out2) <- outDirs.fold(tempDir(pkgName) *> tempDir(pkgName))(p => dir(p._1) *> dir(p._2)) _ <- dotcPos(out1, sourceRoot=srcRoot/src/"src-3-A", additionalDottySettings, src3A:_*) @@ -154,11 +154,12 @@ object TastyTest { } } - def dotcPos(out: String, sourceRoot: String, additionalSettings: Seq[String], sources: String*): Try[Unit] = dotcPos(out, out, sourceRoot, additionalSettings, sources:_*) + def dotcPos(out: String, sourceRoot: String, additionalSettings: Seq[String], sources: String*)(implicit cl: Dotc.ClassLoader): Try[Unit] = dotcPos(out, out, sourceRoot, additionalSettings, sources:_*) - def dotcPos(out: String, classpath: String, sourceRoot: String, additionalSettings: Seq[String], sources: String*): Try[Unit] = { + def dotcPos(out: String, classpath: String, sourceRoot: String, additionalSettings: Seq[String], sources: String*)(implicit cl: Dotc.ClassLoader): Try[Unit] = { log(s"compiling sources in ${yellow(sourceRoot)} with dotc.") - successWhen(Dotc.dotc(out, classpath, additionalSettings, sources:_*))("dotc failed to compile sources.") + val process = Dotc.dotc(out, classpath, additionalSettings, sources:_*) + successWhen(process)("dotc failed to compile sources.") } private def getSourceAsName(path: String): String = @@ -273,7 +274,7 @@ object TastyTest { } case Failure(err) => errors += test - printerrln(s"ERROR: $test failed: ${err.getClass.getSimpleName} ${err.getMessage}") + printerrln(s"ERROR: $test failed: ${err.getClass.getSimpleName} ${err.getMessage} in ${err.getStackTrace().mkString("\n ", "\n ", "")}") } } } diff --git a/src/tastytest/scala/tools/tastytest/package.scala b/src/tastytest/scala/tools/tastytest/package.scala index 95167f2e030e..1d5d745cd066 100644 --- a/src/tastytest/scala/tools/tastytest/package.scala +++ b/src/tastytest/scala/tools/tastytest/package.scala @@ -1,11 +1,16 @@ package scala.tools +import dotty.tools.vulpix.ParallelTesting + package object tastytest { import scala.util.Try import Files.{pathSep, classpathSep} + def unlockExperimentalFeatures[T](op: => T): T = + new ParallelTesting().unlockExperimentalFeatures(op) + def printerrln(str: String): Unit = System.err.println(red(str)) def printwarnln(str: String): Unit = System.err.println(yellow(str)) def printsuccessln(str: String): Unit = System.err.println(green(str)) diff --git a/src/testkit/scala/tools/testkit/AssertUtil.scala b/src/testkit/scala/tools/testkit/AssertUtil.scala index 824adefe107b..4b7083d83e2c 100644 --- a/src/testkit/scala/tools/testkit/AssertUtil.scala +++ b/src/testkit/scala/tools/testkit/AssertUtil.scala @@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicReference import java.lang.ref._ import java.lang.reflect.{Array => _, _} import java.util.IdentityHashMap +import scala.annotation.nowarn /** This module contains additional higher-level assert statements * that are ultimately based on junit.Assert primitives. @@ -166,7 +167,7 @@ object AssertUtil { def assertZeroNetThreads(body: => Unit): Unit = { val group = new ThreadGroup("junit") try assertZeroNetThreads(group)(body) - finally group.destroy() + finally group.destroy(): @nowarn("cat=deprecation") // deprecated since JDK 16, will be removed } def assertZeroNetThreads[A](group: ThreadGroup)(body: => A): Try[A] = { val testDone = new CountDownLatch(1) @@ -294,7 +295,7 @@ class NoTrace[A](body: => A) extends Runnable { case Success(a) => result = Some(a) case Failure(e) => synchronized { uncaught += ((Thread.currentThread, e)) } } - finally group.destroy() + finally group.destroy(): @nowarn("cat=deprecation") // deprecated since JDK 16, will be removed } private[testkit] lazy val errors: List[(Thread, Throwable)] = synchronized(uncaught.toList) diff --git a/test/async/neg/ill-nested-await.check b/test/async/neg/ill-nested-await.check index 73d6d86e5b1e..e04df598a775 100644 --- a/test/async/neg/ill-nested-await.check +++ b/test/async/neg/ill-nested-await.check @@ -34,7 +34,13 @@ ill-nested-await.scala:71: error: await must not be used under a lazy val initia ill-nested-await.scala:77: error: await must not be used under a nested method. async { fooAsByNameLambda(await(f(""))) } ^ +ill-nested-await.scala:82: error: await must not be used under a synchronized call. + async { lock.synchronized { await(f(1)) + await(f(2)) } } + ^ +ill-nested-await.scala:82: error: await must not be used under a synchronized call. + async { lock.synchronized { await(f(1)) + await(f(2)) } } + ^ ill-nested-await.scala:11: error: `await` must be enclosed in an `async` block await[Any](f(null)) ^ -13 errors +15 errors diff --git a/test/async/neg/ill-nested-await.scala b/test/async/neg/ill-nested-await.scala index e46090d0d0e2..dc2a0f93a4f8 100644 --- a/test/async/neg/ill-nested-await.scala +++ b/test/async/neg/ill-nested-await.scala @@ -76,4 +76,9 @@ class NakedAwait { def fooAsByNameLambda = foo("") _ // : (_ => String) = String async { fooAsByNameLambda(await(f(""))) } } + + def underSynchronized(): Unit = { + val lock = new Object + async { lock.synchronized { await(f(1)) + await(f(2)) } } + } } diff --git a/test/benchmarks/project/build.properties b/test/benchmarks/project/build.properties deleted file mode 100644 index 0b2e09c5ac99..000000000000 --- a/test/benchmarks/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.4.7 diff --git a/test/benchmarks/project/plugins.sbt b/test/benchmarks/project/plugins.sbt deleted file mode 100644 index b57429f738ec..000000000000 --- a/test/benchmarks/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.27") diff --git a/test/benchmarks/src/main/java/scala/reflect/internal/util/AlmostFinalValueBenchmarkStatics.java b/test/benchmarks/src/main/java/scala/reflect/internal/util/AlmostFinalValueBenchmarkStatics.java new file mode 100644 index 000000000000..966adedb44e1 --- /dev/null +++ b/test/benchmarks/src/main/java/scala/reflect/internal/util/AlmostFinalValueBenchmarkStatics.java @@ -0,0 +1,12 @@ +package scala.reflect.internal.util; + +import java.lang.invoke.MethodHandle; + +final class AlmostFinalValueBenchmarkStatics { + static final boolean STATIC_FINAL_FALSE = false; + + private static final AlmostFinalValue ALMOST_FINAL_FALSE = new AlmostFinalValue(); + private static final MethodHandle ALMOST_FINAL_FALSE_GETTER = ALMOST_FINAL_FALSE.invoker; + + static boolean isTrue() throws Throwable { return (boolean) ALMOST_FINAL_FALSE_GETTER.invokeExact(); } +} diff --git a/test/benchmarks/src/main/scala/scala/BitManipulationBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/BitManipulationBenchmark.scala similarity index 100% rename from test/benchmarks/src/main/scala/scala/BitManipulationBenchmark.scala rename to test/benchmarks/src/main/scala/scala/collection/BitManipulationBenchmark.scala diff --git a/test/benchmarks/src/main/scala/scala/collection/immutable/ListBenchmark.scala b/test/benchmarks/src/main/scala/scala/collection/immutable/ListBenchmark.scala index 8a44778fae12..c00b2d6be80d 100644 --- a/test/benchmarks/src/main/scala/scala/collection/immutable/ListBenchmark.scala +++ b/test/benchmarks/src/main/scala/scala/collection/immutable/ListBenchmark.scala @@ -24,6 +24,8 @@ class ListBenchmark { var mid: Content = _ var last: Content = _ var replacement: Content = _ + var firstHalf: List[Content] = _ + var lastHalf: List[Content] = _ @Setup(Level.Trial) def initKeys(): Unit = { @@ -31,6 +33,8 @@ class ListBenchmark { mid = Content(size / 2) last = Content(Math.max(0,size -1)) replacement = Content(size * 2 + 1) + firstHalf = values.take(size / 2) + lastHalf = values.drop(size / 2) } @Benchmark def filter_includeAll: Any = { @@ -86,4 +90,28 @@ class ListBenchmark { @Benchmark def partition_exc_last: Any = { values.partition(v => v.value != last.value) } + + @Benchmark def diff_single_mid: Any = { + values.diff(List(mid)) + } + + @Benchmark def diff_single_last: Any = { + values.diff(List(last)) + } + + @Benchmark def diff_notIncluded: Any = { + values.diff(List(Content(-1))) + } + + @Benchmark def diff_identical: Any = { + values.diff(values) + } + + @Benchmark def diff_first_half: Any = { + values.diff(firstHalf) + } + + @Benchmark def diff_last_half: Any = { + values.diff(lastHalf) + } } diff --git a/test/benchmarks/src/main/scala/scala/math/BigIntEulerProblem15Benchmark.scala b/test/benchmarks/src/main/scala/scala/math/BigIntEulerProblem15Benchmark.scala new file mode 100644 index 000000000000..690c078ec2f7 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/math/BigIntEulerProblem15Benchmark.scala @@ -0,0 +1,29 @@ +package scala.math + +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class BigIntEulerProblem15Benchmark { + + @Param(Array("5", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55", + "60", "65", "70", "75", "80", "85", "90", "95", "100")) + var size: Int = _ + + @Benchmark + def eulerProblem15(bh: Blackhole): Unit = { + def f(row: Array[BigInt], c: Int): BigInt = + if (c == 0) row.last else f(row.scan(BigInt(0))(_ + _), c - 1) + def computeAnswer(n: Int): BigInt = f(Array.fill(n + 1)(BigInt(1)), n) + bh.consume(computeAnswer(size)) + } + +} diff --git a/test/benchmarks/src/main/scala/scala/math/BigIntFactorialBenchmark.scala b/test/benchmarks/src/main/scala/scala/math/BigIntFactorialBenchmark.scala new file mode 100644 index 000000000000..0aaa18c029e1 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/math/BigIntFactorialBenchmark.scala @@ -0,0 +1,30 @@ +package scala.math + +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +import scala.annotation.tailrec + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class BigIntFactorialBenchmark { + + @Param(Array("5", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55", + "60", "65", "70", "75", "80", "85", "90", "95", "100")) + var size: Int = _ + + @Benchmark + def factorial(bh: Blackhole): Unit = { + @tailrec def fact(i: Int, n: Int, prev: BigInt): BigInt = + if (i > n) prev else fact(i + 1, n, prev * i) + bh.consume(fact(1, size, BigInt(1))) + } + +} diff --git a/test/benchmarks/src/main/scala/scala/math/BigIntRSABenchmark.scala b/test/benchmarks/src/main/scala/scala/math/BigIntRSABenchmark.scala new file mode 100644 index 000000000000..4c93f324e0bd --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/math/BigIntRSABenchmark.scala @@ -0,0 +1,32 @@ +package scala.math + +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra._ + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class BigIntRSABenchmark { + + @Benchmark + def encodeDecode(bh: Blackhole): Unit = { + // private key + val d = BigInt("5617843187844953170308463622230283376298685") + // public key + val n = BigInt("9516311845790656153499716760847001433441357") + val e = 65537 + + // concatenation of "Scala is great" + val plaintext = BigInt("83099097108097032105115032103114101097116") + val ciphertext = plaintext.modPow(e, n) + val recoveredtext = ciphertext.modPow(d, n) + bh.consume(plaintext == recoveredtext) + } + +} diff --git a/test/benchmarks/src/main/scala/reflect/internal/LubBenchmark.scala b/test/benchmarks/src/main/scala/scala/reflect/internal/LubBenchmark.scala similarity index 100% rename from test/benchmarks/src/main/scala/reflect/internal/LubBenchmark.scala rename to test/benchmarks/src/main/scala/scala/reflect/internal/LubBenchmark.scala diff --git a/test/benchmarks/src/main/scala/reflect/internal/SymbolBenchmark.scala b/test/benchmarks/src/main/scala/scala/reflect/internal/SymbolBenchmark.scala similarity index 100% rename from test/benchmarks/src/main/scala/reflect/internal/SymbolBenchmark.scala rename to test/benchmarks/src/main/scala/scala/reflect/internal/SymbolBenchmark.scala diff --git a/test/benchmarks/src/main/scala/scala/reflect/internal/util/AlmostFinalValueBenchmark.scala b/test/benchmarks/src/main/scala/scala/reflect/internal/util/AlmostFinalValueBenchmark.scala new file mode 100644 index 000000000000..70d69178cb19 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/reflect/internal/util/AlmostFinalValueBenchmark.scala @@ -0,0 +1,56 @@ +package scala.reflect.internal.util + +import java.util.concurrent.TimeUnit + +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +class AlmostFinalValueBenchSettings extends scala.reflect.runtime.Settings { + val flag = new BooleanSetting(false) + + @inline final def isTrue2: Boolean = AlmostFinalValueBenchmarkStatics.isTrue && flag +} + +object AlmostFinalValueBenchSettings { + implicit class SettingsOps(private val settings: AlmostFinalValueBenchSettings) extends AnyVal { + @inline final def isTrue3: Boolean = AlmostFinalValueBenchmarkStatics.isTrue && settings.flag + } + + @inline def isTrue4(settings: AlmostFinalValueBenchSettings): Boolean = + AlmostFinalValueBenchmarkStatics.isTrue && settings.flag +} + +@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +@BenchmarkMode(Array(Mode.AverageTime)) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class AlmostFinalValueBenchmark { + import AlmostFinalValueBenchmarkStatics.STATIC_FINAL_FALSE + val settings = new AlmostFinalValueBenchSettings(); import settings._ + + private def pretendToWorkHard() = Blackhole.consumeCPU(3) + + @Benchmark def bench0_unit = () + @Benchmark def bench0_usingStaticFinalFalse = if (STATIC_FINAL_FALSE && flag) pretendToWorkHard() + @Benchmark def bench0_workingHard = pretendToWorkHard() + + @Benchmark def bench1_usingAlmostFinalFalse = if (AlmostFinalValueBenchmarkStatics.isTrue && flag) pretendToWorkHard() + @Benchmark def bench2_usingInlineMethod = if (settings.isTrue2) pretendToWorkHard() + @Benchmark def bench3_usingExtMethod = if (settings.isTrue3) pretendToWorkHard() + @Benchmark def bench4_usingObjectMethod = if (AlmostFinalValueBenchSettings.isTrue4(settings)) pretendToWorkHard() + +/* + This benchmark is measuring two things: + 1. verifying that using AlmostFinalValue in an if block makes the block a no-op + 2. verifying and comparing which ergonomic wrapper around AlmostFinalValue maintains that + + The first point is satisfied. + + For the second: + 1. inline instance methods add a null-check overhead, slowing it down + 2. extension methods perform as quickly, are very ergonomic and so are the best choice + 3. object methods also perform as quickly, but can be less ergonomic if it requires an import +*/ +} diff --git a/test/benchmarks/src/main/scala/scala/tools/nsc/transform/patmat/ClassMatchBenchmark.scala b/test/benchmarks/src/main/scala/scala/tools/nsc/transform/patmat/ClassMatchBenchmark.scala new file mode 100644 index 000000000000..fd1f2c681239 --- /dev/null +++ b/test/benchmarks/src/main/scala/scala/tools/nsc/transform/patmat/ClassMatchBenchmark.scala @@ -0,0 +1,1127 @@ +package scala.tools.nsc.transform.patmat + +import java.util.concurrent.TimeUnit +import org.openjdk.jmh.annotations.CompilerControl.Mode.DONT_INLINE +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.infra.Blackhole + +import scala.annotation.switch +import scala.util.Random + +@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Fork(3) +@BenchmarkMode(Array(Mode.AverageTime)) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class ClassMatchBenchmark { + private final val count = 10000 + @Param(Array("4", "8", "16", "32", "64", "128", "256")) private var numCases = 0 + + private var names: Array[Name] = null + private var classValue: ClassValue[Int] = null + + @Setup def setup(): Unit = { + val r = new Random(12345) + val names = Array[Name]( + Name0(), Name1(), Name2(), Name3(), Name4(), Name5(), Name6(), Name7(), Name8(), Name9(), + Name10(), Name11(), Name12(), Name13(), Name14(), Name15(), Name16(), Name17(), Name18(), Name19(), + Name20(), Name21(), Name22(), Name23(), Name24(), Name25(), Name26(), Name27(), Name28(), Name29(), + Name30(), Name31(), Name32(), Name33(), Name34(), Name35(), Name36(), Name37(), Name38(), Name39(), + Name40(), Name41(), Name42(), Name43(), Name44(), Name45(), Name46(), Name47(), Name48(), Name49(), + Name50(), Name51(), Name52(), Name53(), Name54(), Name55(), Name56(), Name57(), Name58(), Name59(), + Name60(), Name61(), Name62(), Name63(), Name64(), Name65(), Name66(), Name67(), Name68(), Name69(), + Name70(), Name71(), Name72(), Name73(), Name74(), Name75(), Name76(), Name77(), Name78(), Name79(), + Name80(), Name81(), Name82(), Name83(), Name84(), Name85(), Name86(), Name87(), Name88(), Name89(), + Name90(), Name91(), Name92(), Name93(), Name94(), Name95(), Name96(), Name97(), Name98(), Name99(), + Name100(), Name101(), Name102(), Name103(), Name104(), Name105(), Name106(), Name107(), Name108(), Name109(), + Name110(), Name111(), Name112(), Name113(), Name114(), Name115(), Name116(), Name117(), Name118(), Name119(), + Name120(), Name121(), Name122(), Name123(), Name124(), Name125(), Name126(), Name127(), Name128(), Name129(), + Name130(), Name131(), Name132(), Name133(), Name134(), Name135(), Name136(), Name137(), Name138(), Name139(), + Name140(), Name141(), Name142(), Name143(), Name144(), Name145(), Name146(), Name147(), Name148(), Name149(), + Name150(), Name151(), Name152(), Name153(), Name154(), Name155(), Name156(), Name157(), Name158(), Name159(), + Name160(), Name161(), Name162(), Name163(), Name164(), Name165(), Name166(), Name167(), Name168(), Name169(), + Name170(), Name171(), Name172(), Name173(), Name174(), Name175(), Name176(), Name177(), Name178(), Name179(), + Name180(), Name181(), Name182(), Name183(), Name184(), Name185(), Name186(), Name187(), Name188(), Name189(), + Name190(), Name191(), Name192(), Name193(), Name194(), Name195(), Name196(), Name197(), Name198(), Name199(), + Name200(), Name201(), Name202(), Name203(), Name204(), Name205(), Name206(), Name207(), Name208(), Name209(), + Name210(), Name211(), Name212(), Name213(), Name214(), Name215(), Name216(), Name217(), Name218(), Name219(), + Name220(), Name221(), Name222(), Name223(), Name224(), Name225(), Name226(), Name227(), Name228(), Name229(), + Name230(), Name231(), Name232(), Name233(), Name234(), Name235(), Name236(), Name237(), Name238(), Name239(), + Name240(), Name241(), Name242(), Name243(), Name244(), Name245(), Name246(), Name247(), Name248(), Name249(), + Name250(), Name251(), Name252(), Name253(), Name254(), Name255(), + ) + this.names = Array.fill(count)(names(r.nextInt(numCases))) + this.classValue = new NameClassValue + } + + @Benchmark @OperationsPerInvocation(count) def patmatShow(bh: Blackhole): Unit = { + val names = this.names + var i = 0 + while (i < names.length) { + val x = names(i) match { + case Name0() => "0" case Name1() => "1" case Name2() => "2" case Name3() => "3" case Name4() => "4" + case Name5() => "5" case Name6() => "6" case Name7() => "7" case Name8() => "8" case Name9() => "9" + case Name10() => "10" case Name11() => "11" case Name12() => "12" case Name13() => "13" case Name14() => "14" + case Name15() => "15" case Name16() => "16" case Name17() => "17" case Name18() => "18" case Name19() => "19" + case Name20() => "20" case Name21() => "21" case Name22() => "22" case Name23() => "23" case Name24() => "24" + case Name25() => "25" case Name26() => "26" case Name27() => "27" case Name28() => "28" case Name29() => "29" + case Name30() => "30" case Name31() => "31" case Name32() => "32" case Name33() => "33" case Name34() => "34" + case Name35() => "35" case Name36() => "36" case Name37() => "37" case Name38() => "38" case Name39() => "39" + case Name40() => "40" case Name41() => "41" case Name42() => "42" case Name43() => "43" case Name44() => "44" + case Name45() => "45" case Name46() => "46" case Name47() => "47" case Name48() => "48" case Name49() => "49" + case Name50() => "50" case Name51() => "51" case Name52() => "52" case Name53() => "53" case Name54() => "54" + case Name55() => "55" case Name56() => "56" case Name57() => "57" case Name58() => "58" case Name59() => "59" + case Name60() => "60" case Name61() => "61" case Name62() => "62" case Name63() => "63" case Name64() => "64" + case Name65() => "65" case Name66() => "66" case Name67() => "67" case Name68() => "68" case Name69() => "69" + case Name70() => "70" case Name71() => "71" case Name72() => "72" case Name73() => "73" case Name74() => "74" + case Name75() => "75" case Name76() => "76" case Name77() => "77" case Name78() => "78" case Name79() => "79" + case Name80() => "80" case Name81() => "81" case Name82() => "82" case Name83() => "83" case Name84() => "84" + case Name85() => "85" case Name86() => "86" case Name87() => "87" case Name88() => "88" case Name89() => "89" + case Name90() => "90" case Name91() => "91" case Name92() => "92" case Name93() => "93" case Name94() => "94" + case Name95() => "95" case Name96() => "96" case Name97() => "97" case Name98() => "98" case Name99() => "99" + case Name100() => "100" case Name101() => "101" case Name102() => "102" case Name103() => "103" case Name104() => "104" + case Name105() => "105" case Name106() => "106" case Name107() => "107" case Name108() => "108" case Name109() => "109" + case Name110() => "110" case Name111() => "111" case Name112() => "112" case Name113() => "113" case Name114() => "114" + case Name115() => "115" case Name116() => "116" case Name117() => "117" case Name118() => "118" case Name119() => "119" + case Name120() => "120" case Name121() => "121" case Name122() => "122" case Name123() => "123" case Name124() => "124" + case Name125() => "125" case Name126() => "126" case Name127() => "127" case Name128() => "128" case Name129() => "129" + case Name130() => "130" case Name131() => "131" case Name132() => "132" case Name133() => "133" case Name134() => "134" + case Name135() => "135" case Name136() => "136" case Name137() => "137" case Name138() => "138" case Name139() => "139" + case Name140() => "140" case Name141() => "141" case Name142() => "142" case Name143() => "143" case Name144() => "144" + case Name145() => "145" case Name146() => "146" case Name147() => "147" case Name148() => "148" case Name149() => "149" + case Name150() => "150" case Name151() => "151" case Name152() => "152" case Name153() => "153" case Name154() => "154" + case Name155() => "155" case Name156() => "156" case Name157() => "157" case Name158() => "158" case Name159() => "159" + case Name160() => "160" case Name161() => "161" case Name162() => "162" case Name163() => "163" case Name164() => "164" + case Name165() => "165" case Name166() => "166" case Name167() => "167" case Name168() => "168" case Name169() => "169" + case Name170() => "170" case Name171() => "171" case Name172() => "172" case Name173() => "173" case Name174() => "174" + case Name175() => "175" case Name176() => "176" case Name177() => "177" case Name178() => "178" case Name179() => "179" + case Name180() => "180" case Name181() => "181" case Name182() => "182" case Name183() => "183" case Name184() => "184" + case Name185() => "185" case Name186() => "186" case Name187() => "187" case Name188() => "188" case Name189() => "189" + case Name190() => "190" case Name191() => "191" case Name192() => "192" case Name193() => "193" case Name194() => "194" + case Name195() => "195" case Name196() => "196" case Name197() => "197" case Name198() => "198" case Name199() => "199" + case Name200() => "200" case Name201() => "201" case Name202() => "202" case Name203() => "203" case Name204() => "204" + case Name205() => "205" case Name206() => "206" case Name207() => "207" case Name208() => "208" case Name209() => "209" + case Name210() => "210" case Name211() => "211" case Name212() => "212" case Name213() => "213" case Name214() => "214" + case Name215() => "215" case Name216() => "216" case Name217() => "217" case Name218() => "218" case Name219() => "219" + case Name220() => "220" case Name221() => "221" case Name222() => "222" case Name223() => "223" case Name224() => "224" + case Name225() => "225" case Name226() => "226" case Name227() => "227" case Name228() => "228" case Name229() => "229" + case Name230() => "230" case Name231() => "231" case Name232() => "232" case Name233() => "233" case Name234() => "234" + case Name235() => "235" case Name236() => "236" case Name237() => "237" case Name238() => "238" case Name239() => "239" + case Name240() => "240" case Name241() => "241" case Name242() => "242" case Name243() => "243" case Name244() => "244" + case Name245() => "245" case Name246() => "246" case Name247() => "247" case Name248() => "248" case Name249() => "249" + case Name250() => "250" case Name251() => "251" case Name252() => "252" case Name253() => "253" case Name254() => "254" + case Name255() => "255" + } + bh.consume(x) + i += 1 + } + } + + @Benchmark @OperationsPerInvocation(count) def virtualShow(bh: Blackhole): Unit = { + val names = this.names + var i = 0 + while (i < names.length) { + bh.consume(names(i).virtualShow) + i += 1 + } + } + + @Benchmark @OperationsPerInvocation(count) def intSwitchShow(bh: Blackhole): Unit = { + val names = this.names + var i = 0 + while (i < names.length) { + val x = (names(i)._id: @switch) match { + case 0 => "0" case 1 => "1" case 2 => "2" case 3 => "3" case 4 => "4" + case 5 => "5" case 6 => "6" case 7 => "7" case 8 => "8" case 9 => "9" + case 10 => "10" case 11 => "11" case 12 => "12" case 13 => "13" case 14 => "14" + case 15 => "15" case 16 => "16" case 17 => "17" case 18 => "18" case 19 => "19" + case 20 => "20" case 21 => "21" case 22 => "22" case 23 => "23" case 24 => "24" + case 25 => "25" case 26 => "26" case 27 => "27" case 28 => "28" case 29 => "29" + case 30 => "30" case 31 => "31" case 32 => "32" case 33 => "33" case 34 => "34" + case 35 => "35" case 36 => "36" case 37 => "37" case 38 => "38" case 39 => "39" + case 40 => "40" case 41 => "41" case 42 => "42" case 43 => "43" case 44 => "44" + case 45 => "45" case 46 => "46" case 47 => "47" case 48 => "48" case 49 => "49" + case 50 => "50" case 51 => "51" case 52 => "52" case 53 => "53" case 54 => "54" + case 55 => "55" case 56 => "56" case 57 => "57" case 58 => "58" case 59 => "59" + case 60 => "60" case 61 => "61" case 62 => "62" case 63 => "63" case 64 => "64" + case 65 => "65" case 66 => "66" case 67 => "67" case 68 => "68" case 69 => "69" + case 70 => "70" case 71 => "71" case 72 => "72" case 73 => "73" case 74 => "74" + case 75 => "75" case 76 => "76" case 77 => "77" case 78 => "78" case 79 => "79" + case 80 => "80" case 81 => "81" case 82 => "82" case 83 => "83" case 84 => "84" + case 85 => "85" case 86 => "86" case 87 => "87" case 88 => "88" case 89 => "89" + case 90 => "90" case 91 => "91" case 92 => "92" case 93 => "93" case 94 => "94" + case 95 => "95" case 96 => "96" case 97 => "97" case 98 => "98" case 99 => "99" + case 100 => "100" case 101 => "101" case 102 => "102" case 103 => "103" case 104 => "104" + case 105 => "105" case 106 => "106" case 107 => "107" case 108 => "108" case 109 => "109" + case 110 => "110" case 111 => "111" case 112 => "112" case 113 => "113" case 114 => "114" + case 115 => "115" case 116 => "116" case 117 => "117" case 118 => "118" case 119 => "119" + case 120 => "120" case 121 => "121" case 122 => "122" case 123 => "123" case 124 => "124" + case 125 => "125" case 126 => "126" case 127 => "127" case 128 => "128" case 129 => "129" + case 130 => "130" case 131 => "131" case 132 => "132" case 133 => "133" case 134 => "134" + case 135 => "135" case 136 => "136" case 137 => "137" case 138 => "138" case 139 => "139" + case 140 => "140" case 141 => "141" case 142 => "142" case 143 => "143" case 144 => "144" + case 145 => "145" case 146 => "146" case 147 => "147" case 148 => "148" case 149 => "149" + case 150 => "150" case 151 => "151" case 152 => "152" case 153 => "153" case 154 => "154" + case 155 => "155" case 156 => "156" case 157 => "157" case 158 => "158" case 159 => "159" + case 160 => "160" case 161 => "161" case 162 => "162" case 163 => "163" case 164 => "164" + case 165 => "165" case 166 => "166" case 167 => "167" case 168 => "168" case 169 => "169" + case 170 => "170" case 171 => "171" case 172 => "172" case 173 => "173" case 174 => "174" + case 175 => "175" case 176 => "176" case 177 => "177" case 178 => "178" case 179 => "179" + case 180 => "180" case 181 => "181" case 182 => "182" case 183 => "183" case 184 => "184" + case 185 => "185" case 186 => "186" case 187 => "187" case 188 => "188" case 189 => "189" + case 190 => "190" case 191 => "191" case 192 => "192" case 193 => "193" case 194 => "194" + case 195 => "195" case 196 => "196" case 197 => "197" case 198 => "198" case 199 => "199" + case 200 => "200" case 201 => "201" case 202 => "202" case 203 => "203" case 204 => "204" + case 205 => "205" case 206 => "206" case 207 => "207" case 208 => "208" case 209 => "209" + case 210 => "210" case 211 => "211" case 212 => "212" case 213 => "213" case 214 => "214" + case 215 => "215" case 216 => "216" case 217 => "217" case 218 => "218" case 219 => "219" + case 220 => "220" case 221 => "221" case 222 => "222" case 223 => "223" case 224 => "224" + case 225 => "225" case 226 => "226" case 227 => "227" case 228 => "228" case 229 => "229" + case 230 => "230" case 231 => "231" case 232 => "232" case 233 => "233" case 234 => "234" + case 235 => "235" case 236 => "236" case 237 => "237" case 238 => "238" case 239 => "239" + case 240 => "240" case 241 => "241" case 242 => "242" case 243 => "243" case 244 => "244" + case 245 => "245" case 246 => "246" case 247 => "247" case 248 => "248" case 249 => "249" + case 250 => "250" case 251 => "251" case 252 => "252" case 253 => "253" case 254 => "254" + case 255 => "255" + } + bh.consume(x) + i += 1 + } + } + + @Benchmark @OperationsPerInvocation(count) def justClassValueLookup(bh: Blackhole): Unit = { + val names = this.names + val classValue = this.classValue + var i = 0 + while (i < names.length) { + bh.consume(classValue.get(names(i).getClass)) + i += 1 + } + } + + @Benchmark @OperationsPerInvocation(count) def classValueShow(bh: Blackhole): Unit = { + val names = this.names + val classValue = this.classValue + var i = 0 + while (i < names.length) { + val x = (classValue.get(names(i).getClass): @switch) match { + case 0 => "0" case 1 => "1" case 2 => "2" case 3 => "3" case 4 => "4" + case 5 => "5" case 6 => "6" case 7 => "7" case 8 => "8" case 9 => "9" + case 10 => "10" case 11 => "11" case 12 => "12" case 13 => "13" case 14 => "14" + case 15 => "15" case 16 => "16" case 17 => "17" case 18 => "18" case 19 => "19" + case 20 => "20" case 21 => "21" case 22 => "22" case 23 => "23" case 24 => "24" + case 25 => "25" case 26 => "26" case 27 => "27" case 28 => "28" case 29 => "29" + case 30 => "30" case 31 => "31" case 32 => "32" case 33 => "33" case 34 => "34" + case 35 => "35" case 36 => "36" case 37 => "37" case 38 => "38" case 39 => "39" + case 40 => "40" case 41 => "41" case 42 => "42" case 43 => "43" case 44 => "44" + case 45 => "45" case 46 => "46" case 47 => "47" case 48 => "48" case 49 => "49" + case 50 => "50" case 51 => "51" case 52 => "52" case 53 => "53" case 54 => "54" + case 55 => "55" case 56 => "56" case 57 => "57" case 58 => "58" case 59 => "59" + case 60 => "60" case 61 => "61" case 62 => "62" case 63 => "63" case 64 => "64" + case 65 => "65" case 66 => "66" case 67 => "67" case 68 => "68" case 69 => "69" + case 70 => "70" case 71 => "71" case 72 => "72" case 73 => "73" case 74 => "74" + case 75 => "75" case 76 => "76" case 77 => "77" case 78 => "78" case 79 => "79" + case 80 => "80" case 81 => "81" case 82 => "82" case 83 => "83" case 84 => "84" + case 85 => "85" case 86 => "86" case 87 => "87" case 88 => "88" case 89 => "89" + case 90 => "90" case 91 => "91" case 92 => "92" case 93 => "93" case 94 => "94" + case 95 => "95" case 96 => "96" case 97 => "97" case 98 => "98" case 99 => "99" + case 100 => "100" case 101 => "101" case 102 => "102" case 103 => "103" case 104 => "104" + case 105 => "105" case 106 => "106" case 107 => "107" case 108 => "108" case 109 => "109" + case 110 => "110" case 111 => "111" case 112 => "112" case 113 => "113" case 114 => "114" + case 115 => "115" case 116 => "116" case 117 => "117" case 118 => "118" case 119 => "119" + case 120 => "120" case 121 => "121" case 122 => "122" case 123 => "123" case 124 => "124" + case 125 => "125" case 126 => "126" case 127 => "127" case 128 => "128" case 129 => "129" + case 130 => "130" case 131 => "131" case 132 => "132" case 133 => "133" case 134 => "134" + case 135 => "135" case 136 => "136" case 137 => "137" case 138 => "138" case 139 => "139" + case 140 => "140" case 141 => "141" case 142 => "142" case 143 => "143" case 144 => "144" + case 145 => "145" case 146 => "146" case 147 => "147" case 148 => "148" case 149 => "149" + case 150 => "150" case 151 => "151" case 152 => "152" case 153 => "153" case 154 => "154" + case 155 => "155" case 156 => "156" case 157 => "157" case 158 => "158" case 159 => "159" + case 160 => "160" case 161 => "161" case 162 => "162" case 163 => "163" case 164 => "164" + case 165 => "165" case 166 => "166" case 167 => "167" case 168 => "168" case 169 => "169" + case 170 => "170" case 171 => "171" case 172 => "172" case 173 => "173" case 174 => "174" + case 175 => "175" case 176 => "176" case 177 => "177" case 178 => "178" case 179 => "179" + case 180 => "180" case 181 => "181" case 182 => "182" case 183 => "183" case 184 => "184" + case 185 => "185" case 186 => "186" case 187 => "187" case 188 => "188" case 189 => "189" + case 190 => "190" case 191 => "191" case 192 => "192" case 193 => "193" case 194 => "194" + case 195 => "195" case 196 => "196" case 197 => "197" case 198 => "198" case 199 => "199" + case 200 => "200" case 201 => "201" case 202 => "202" case 203 => "203" case 204 => "204" + case 205 => "205" case 206 => "206" case 207 => "207" case 208 => "208" case 209 => "209" + case 210 => "210" case 211 => "211" case 212 => "212" case 213 => "213" case 214 => "214" + case 215 => "215" case 216 => "216" case 217 => "217" case 218 => "218" case 219 => "219" + case 220 => "220" case 221 => "221" case 222 => "222" case 223 => "223" case 224 => "224" + case 225 => "225" case 226 => "226" case 227 => "227" case 228 => "228" case 229 => "229" + case 230 => "230" case 231 => "231" case 232 => "232" case 233 => "233" case 234 => "234" + case 235 => "235" case 236 => "236" case 237 => "237" case 238 => "238" case 239 => "239" + case 240 => "240" case 241 => "241" case 242 => "242" case 243 => "243" case 244 => "244" + case 245 => "245" case 246 => "246" case 247 => "247" case 248 => "248" case 249 => "249" + case 250 => "250" case 251 => "251" case 252 => "252" case 253 => "253" case 254 => "254" + case 255 => "255" + } + bh.consume(x) + i += 1 + } + } + + @Benchmark @OperationsPerInvocation(count) def classNameHashSwitchShow(bh: Blackhole): Unit = { + val names = this.names + var i = 0 + while (i < names.length) { + val name = names(i) + val cls = name.getClass + val x = ((cls.getName.##): @switch) match { + case -1200720095 => "0" + case -1200720094 => "1" + case -1200720093 => "2" + case -1200720092 => "3" + case -1200720091 => "4" + case -1200720090 => "5" + case -1200720089 => "6" + case -1200720088 => "7" + case -1200720087 => "8" + case -1200720086 => "9" + case 1432382798 => "10" + case 1432382799 => "11" + case 1432382800 => "12" + case 1432382801 => "13" + case 1432382802 => "14" + case 1432382803 => "15" + case 1432382804 => "16" + case 1432382805 => "17" + case 1432382806 => "18" + case 1432382807 => "19" + case 1432382829 => "20" + case 1432382830 => "21" + case 1432382831 => "22" + case 1432382832 => "23" + case 1432382833 => "24" + case 1432382834 => "25" + case 1432382835 => "26" + case 1432382836 => "27" + case 1432382837 => "28" + case 1432382838 => "29" + case 1432382860 => "30" + case 1432382861 => "31" + case 1432382862 => "32" + case 1432382863 => "33" + case 1432382864 => "34" + case 1432382865 => "35" + case 1432382866 => "36" + case 1432382867 => "37" + case 1432382868 => "38" + case 1432382869 => "39" + case 1432382891 => "40" + case 1432382892 => "41" + case 1432382893 => "42" + case 1432382894 => "43" + case 1432382895 => "44" + case 1432382896 => "45" + case 1432382897 => "46" + case 1432382898 => "47" + case 1432382899 => "48" + case 1432382900 => "49" + case 1432382922 => "50" + case 1432382923 => "51" + case 1432382924 => "52" + case 1432382925 => "53" + case 1432382926 => "54" + case 1432382927 => "55" + case 1432382928 => "56" + case 1432382929 => "57" + case 1432382930 => "58" + case 1432382931 => "59" + case 1432382953 => "60" + case 1432382954 => "61" + case 1432382955 => "62" + case 1432382956 => "63" + case 1432382957 => "64" + case 1432382958 => "65" + case 1432382959 => "66" + case 1432382960 => "67" + case 1432382961 => "68" + case 1432382962 => "69" + case 1432382984 => "70" + case 1432382985 => "71" + case 1432382986 => "72" + case 1432382987 => "73" + case 1432382988 => "74" + case 1432382989 => "75" + case 1432382990 => "76" + case 1432382991 => "77" + case 1432382992 => "78" + case 1432382993 => "79" + case 1432383015 => "80" + case 1432383016 => "81" + case 1432383017 => "82" + case 1432383018 => "83" + case 1432383019 => "84" + case 1432383020 => "85" + case 1432383021 => "86" + case 1432383022 => "87" + case 1432383023 => "88" + case 1432383024 => "89" + case 1432383046 => "90" + case 1432383047 => "91" + case 1432383048 => "92" + case 1432383049 => "93" + case 1432383050 => "94" + case 1432383051 => "95" + case 1432383052 => "96" + case 1432383053 => "97" + case 1432383054 => "98" + case 1432383055 => "99" + case 1454193826 => "100" + case 1454193827 => "101" + case 1454193828 => "102" + case 1454193829 => "103" + case 1454193830 => "104" + case 1454193831 => "105" + case 1454193832 => "106" + case 1454193833 => "107" + case 1454193834 => "108" + case 1454193835 => "109" + case 1454193857 => "110" + case 1454193858 => "111" + case 1454193859 => "112" + case 1454193860 => "113" + case 1454193861 => "114" + case 1454193862 => "115" + case 1454193863 => "116" + case 1454193864 => "117" + case 1454193865 => "118" + case 1454193866 => "119" + case 1454193888 => "120" + case 1454193889 => "121" + case 1454193890 => "122" + case 1454193891 => "123" + case 1454193892 => "124" + case 1454193893 => "125" + case 1454193894 => "126" + case 1454193895 => "127" + case 1454193896 => "128" + case 1454193897 => "129" + case 1454193919 => "130" + case 1454193920 => "131" + case 1454193921 => "132" + case 1454193922 => "133" + case 1454193923 => "134" + case 1454193924 => "135" + case 1454193925 => "136" + case 1454193926 => "137" + case 1454193927 => "138" + case 1454193928 => "139" + case 1454193950 => "140" + case 1454193951 => "141" + case 1454193952 => "142" + case 1454193953 => "143" + case 1454193954 => "144" + case 1454193955 => "145" + case 1454193956 => "146" + case 1454193957 => "147" + case 1454193958 => "148" + case 1454193959 => "149" + case 1454193981 => "150" + case 1454193982 => "151" + case 1454193983 => "152" + case 1454193984 => "153" + case 1454193985 => "154" + case 1454193986 => "155" + case 1454193987 => "156" + case 1454193988 => "157" + case 1454193989 => "158" + case 1454193990 => "159" + case 1454194012 => "160" + case 1454194013 => "161" + case 1454194014 => "162" + case 1454194015 => "163" + case 1454194016 => "164" + case 1454194017 => "165" + case 1454194018 => "166" + case 1454194019 => "167" + case 1454194020 => "168" + case 1454194021 => "169" + case 1454194043 => "170" + case 1454194044 => "171" + case 1454194045 => "172" + case 1454194046 => "173" + case 1454194047 => "174" + case 1454194048 => "175" + case 1454194049 => "176" + case 1454194050 => "177" + case 1454194051 => "178" + case 1454194052 => "179" + case 1454194074 => "180" + case 1454194075 => "181" + case 1454194076 => "182" + case 1454194077 => "183" + case 1454194078 => "184" + case 1454194079 => "185" + case 1454194080 => "186" + case 1454194081 => "187" + case 1454194082 => "188" + case 1454194083 => "189" + case 1454194105 => "190" + case 1454194106 => "191" + case 1454194107 => "192" + case 1454194108 => "193" + case 1454194109 => "194" + case 1454194110 => "195" + case 1454194111 => "196" + case 1454194112 => "197" + case 1454194113 => "198" + case 1454194114 => "199" + case 1454194787 => "200" + case 1454194788 => "201" + case 1454194789 => "202" + case 1454194790 => "203" + case 1454194791 => "204" + case 1454194792 => "205" + case 1454194793 => "206" + case 1454194794 => "207" + case 1454194795 => "208" + case 1454194796 => "209" + case 1454194818 => "210" + case 1454194819 => "211" + case 1454194820 => "212" + case 1454194821 => "213" + case 1454194822 => "214" + case 1454194823 => "215" + case 1454194824 => "216" + case 1454194825 => "217" + case 1454194826 => "218" + case 1454194827 => "219" + case 1454194849 => "220" + case 1454194850 => "221" + case 1454194851 => "222" + case 1454194852 => "223" + case 1454194853 => "224" + case 1454194854 => "225" + case 1454194855 => "226" + case 1454194856 => "227" + case 1454194857 => "228" + case 1454194858 => "229" + case 1454194880 => "230" + case 1454194881 => "231" + case 1454194882 => "232" + case 1454194883 => "233" + case 1454194884 => "234" + case 1454194885 => "235" + case 1454194886 => "236" + case 1454194887 => "237" + case 1454194888 => "238" + case 1454194889 => "239" + case 1454194911 => "240" + case 1454194912 => "241" + case 1454194913 => "242" + case 1454194914 => "243" + case 1454194915 => "244" + case 1454194916 => "245" + case 1454194917 => "246" + case 1454194918 => "247" + case 1454194919 => "248" + case 1454194920 => "249" + case 1454194942 => "250" + case 1454194943 => "251" + case 1454194944 => "252" + case 1454194945 => "253" + case 1454194946 => "254" + case 1454194947 => "255" + case hashCode => throw new MatchError(s"No case for: $name -> $cls -> $hashCode") + } + bh.consume(x) + i += 1 + } + } + +/* + This benchmark compares pattern matching to alternatives, specifically: + 1. using virtual methods instead (like our Tree#transform/traverse) + 2. doing a tableswitch on int field (like our Promise.Transformation) + 3. using a ClassValue as a more efficient way to store the int (like exotic's TypeSwitch) + 4. using the instance's class's name's hash, which are all memoised, in a jumptable + + The results appear to indicate that: + + 1. < 16 cases, patmat beats virtual method calls + 2. = 16 cases, patmat vs virtual overlap in error margins + 3. > 16 cases, patmat loses to virtual method calls + 4. int switching seems to only out perform virtual at 32+ cases + 5. class name hash switching beats class value, up to 32 cases (and matches performance at 64) +*/ +} + +final class NameClassValue extends ClassValue[Int] { + def computeValue(runtimeClass: Class[_]) = runtimeClass match { + case ClsName0 => 0 case ClsName1 => 1 case ClsName2 => 2 case ClsName3 => 3 case ClsName4 => 4 + case ClsName5 => 5 case ClsName6 => 6 case ClsName7 => 7 case ClsName8 => 8 case ClsName9 => 9 + case ClsName10 => 10 case ClsName11 => 11 case ClsName12 => 12 case ClsName13 => 13 case ClsName14 => 14 + case ClsName15 => 15 case ClsName16 => 16 case ClsName17 => 17 case ClsName18 => 18 case ClsName19 => 19 + case ClsName20 => 20 case ClsName21 => 21 case ClsName22 => 22 case ClsName23 => 23 case ClsName24 => 24 + case ClsName25 => 25 case ClsName26 => 26 case ClsName27 => 27 case ClsName28 => 28 case ClsName29 => 29 + case ClsName30 => 30 case ClsName31 => 31 case ClsName32 => 32 case ClsName33 => 33 case ClsName34 => 34 + case ClsName35 => 35 case ClsName36 => 36 case ClsName37 => 37 case ClsName38 => 38 case ClsName39 => 39 + case ClsName40 => 40 case ClsName41 => 41 case ClsName42 => 42 case ClsName43 => 43 case ClsName44 => 44 + case ClsName45 => 45 case ClsName46 => 46 case ClsName47 => 47 case ClsName48 => 48 case ClsName49 => 49 + case ClsName50 => 50 case ClsName51 => 51 case ClsName52 => 52 case ClsName53 => 53 case ClsName54 => 54 + case ClsName55 => 55 case ClsName56 => 56 case ClsName57 => 57 case ClsName58 => 58 case ClsName59 => 59 + case ClsName60 => 60 case ClsName61 => 61 case ClsName62 => 62 case ClsName63 => 63 case ClsName64 => 64 + case ClsName65 => 65 case ClsName66 => 66 case ClsName67 => 67 case ClsName68 => 68 case ClsName69 => 69 + case ClsName70 => 70 case ClsName71 => 71 case ClsName72 => 72 case ClsName73 => 73 case ClsName74 => 74 + case ClsName75 => 75 case ClsName76 => 76 case ClsName77 => 77 case ClsName78 => 78 case ClsName79 => 79 + case ClsName80 => 80 case ClsName81 => 81 case ClsName82 => 82 case ClsName83 => 83 case ClsName84 => 84 + case ClsName85 => 85 case ClsName86 => 86 case ClsName87 => 87 case ClsName88 => 88 case ClsName89 => 89 + case ClsName90 => 90 case ClsName91 => 91 case ClsName92 => 92 case ClsName93 => 93 case ClsName94 => 94 + case ClsName95 => 95 case ClsName96 => 96 case ClsName97 => 97 case ClsName98 => 98 case ClsName99 => 99 + case ClsName100 => 100 case ClsName101 => 101 case ClsName102 => 102 case ClsName103 => 103 case ClsName104 => 104 + case ClsName105 => 105 case ClsName106 => 106 case ClsName107 => 107 case ClsName108 => 108 case ClsName109 => 109 + case ClsName110 => 110 case ClsName111 => 111 case ClsName112 => 112 case ClsName113 => 113 case ClsName114 => 114 + case ClsName115 => 115 case ClsName116 => 116 case ClsName117 => 117 case ClsName118 => 118 case ClsName119 => 119 + case ClsName120 => 120 case ClsName121 => 121 case ClsName122 => 122 case ClsName123 => 123 case ClsName124 => 124 + case ClsName125 => 125 case ClsName126 => 126 case ClsName127 => 127 case ClsName128 => 128 case ClsName129 => 129 + case ClsName130 => 130 case ClsName131 => 131 case ClsName132 => 132 case ClsName133 => 133 case ClsName134 => 134 + case ClsName135 => 135 case ClsName136 => 136 case ClsName137 => 137 case ClsName138 => 138 case ClsName139 => 139 + case ClsName140 => 140 case ClsName141 => 141 case ClsName142 => 142 case ClsName143 => 143 case ClsName144 => 144 + case ClsName145 => 145 case ClsName146 => 146 case ClsName147 => 147 case ClsName148 => 148 case ClsName149 => 149 + case ClsName150 => 150 case ClsName151 => 151 case ClsName152 => 152 case ClsName153 => 153 case ClsName154 => 154 + case ClsName155 => 155 case ClsName156 => 156 case ClsName157 => 157 case ClsName158 => 158 case ClsName159 => 159 + case ClsName160 => 160 case ClsName161 => 161 case ClsName162 => 162 case ClsName163 => 163 case ClsName164 => 164 + case ClsName165 => 165 case ClsName166 => 166 case ClsName167 => 167 case ClsName168 => 168 case ClsName169 => 169 + case ClsName170 => 170 case ClsName171 => 171 case ClsName172 => 172 case ClsName173 => 173 case ClsName174 => 174 + case ClsName175 => 175 case ClsName176 => 176 case ClsName177 => 177 case ClsName178 => 178 case ClsName179 => 179 + case ClsName180 => 180 case ClsName181 => 181 case ClsName182 => 182 case ClsName183 => 183 case ClsName184 => 184 + case ClsName185 => 185 case ClsName186 => 186 case ClsName187 => 187 case ClsName188 => 188 case ClsName189 => 189 + case ClsName190 => 190 case ClsName191 => 191 case ClsName192 => 192 case ClsName193 => 193 case ClsName194 => 194 + case ClsName195 => 195 case ClsName196 => 196 case ClsName197 => 197 case ClsName198 => 198 case ClsName199 => 199 + case ClsName200 => 200 case ClsName201 => 201 case ClsName202 => 202 case ClsName203 => 203 case ClsName204 => 204 + case ClsName205 => 205 case ClsName206 => 206 case ClsName207 => 207 case ClsName208 => 208 case ClsName209 => 209 + case ClsName210 => 210 case ClsName211 => 211 case ClsName212 => 212 case ClsName213 => 213 case ClsName214 => 214 + case ClsName215 => 215 case ClsName216 => 216 case ClsName217 => 217 case ClsName218 => 218 case ClsName219 => 219 + case ClsName220 => 220 case ClsName221 => 221 case ClsName222 => 222 case ClsName223 => 223 case ClsName224 => 224 + case ClsName225 => 225 case ClsName226 => 226 case ClsName227 => 227 case ClsName228 => 228 case ClsName229 => 229 + case ClsName230 => 230 case ClsName231 => 231 case ClsName232 => 232 case ClsName233 => 233 case ClsName234 => 234 + case ClsName235 => 235 case ClsName236 => 236 case ClsName237 => 237 case ClsName238 => 238 case ClsName239 => 239 + case ClsName240 => 240 case ClsName241 => 241 case ClsName242 => 242 case ClsName243 => 243 case ClsName244 => 244 + case ClsName245 => 245 case ClsName246 => 246 case ClsName247 => 247 case ClsName248 => 248 case ClsName249 => 249 + case ClsName250 => 250 case ClsName251 => 251 case ClsName252 => 252 case ClsName253 => 253 case ClsName254 => 254 + case ClsName255 => 255 + } + + private val ClsName0 = classOf[Name0] + private val ClsName1 = classOf[Name1] + private val ClsName2 = classOf[Name2] + private val ClsName3 = classOf[Name3] + private val ClsName4 = classOf[Name4] + private val ClsName5 = classOf[Name5] + private val ClsName6 = classOf[Name6] + private val ClsName7 = classOf[Name7] + private val ClsName8 = classOf[Name8] + private val ClsName9 = classOf[Name9] + private val ClsName10 = classOf[Name10] + private val ClsName11 = classOf[Name11] + private val ClsName12 = classOf[Name12] + private val ClsName13 = classOf[Name13] + private val ClsName14 = classOf[Name14] + private val ClsName15 = classOf[Name15] + private val ClsName16 = classOf[Name16] + private val ClsName17 = classOf[Name17] + private val ClsName18 = classOf[Name18] + private val ClsName19 = classOf[Name19] + private val ClsName20 = classOf[Name20] + private val ClsName21 = classOf[Name21] + private val ClsName22 = classOf[Name22] + private val ClsName23 = classOf[Name23] + private val ClsName24 = classOf[Name24] + private val ClsName25 = classOf[Name25] + private val ClsName26 = classOf[Name26] + private val ClsName27 = classOf[Name27] + private val ClsName28 = classOf[Name28] + private val ClsName29 = classOf[Name29] + private val ClsName30 = classOf[Name30] + private val ClsName31 = classOf[Name31] + private val ClsName32 = classOf[Name32] + private val ClsName33 = classOf[Name33] + private val ClsName34 = classOf[Name34] + private val ClsName35 = classOf[Name35] + private val ClsName36 = classOf[Name36] + private val ClsName37 = classOf[Name37] + private val ClsName38 = classOf[Name38] + private val ClsName39 = classOf[Name39] + private val ClsName40 = classOf[Name40] + private val ClsName41 = classOf[Name41] + private val ClsName42 = classOf[Name42] + private val ClsName43 = classOf[Name43] + private val ClsName44 = classOf[Name44] + private val ClsName45 = classOf[Name45] + private val ClsName46 = classOf[Name46] + private val ClsName47 = classOf[Name47] + private val ClsName48 = classOf[Name48] + private val ClsName49 = classOf[Name49] + private val ClsName50 = classOf[Name50] + private val ClsName51 = classOf[Name51] + private val ClsName52 = classOf[Name52] + private val ClsName53 = classOf[Name53] + private val ClsName54 = classOf[Name54] + private val ClsName55 = classOf[Name55] + private val ClsName56 = classOf[Name56] + private val ClsName57 = classOf[Name57] + private val ClsName58 = classOf[Name58] + private val ClsName59 = classOf[Name59] + private val ClsName60 = classOf[Name60] + private val ClsName61 = classOf[Name61] + private val ClsName62 = classOf[Name62] + private val ClsName63 = classOf[Name63] + private val ClsName64 = classOf[Name64] + private val ClsName65 = classOf[Name65] + private val ClsName66 = classOf[Name66] + private val ClsName67 = classOf[Name67] + private val ClsName68 = classOf[Name68] + private val ClsName69 = classOf[Name69] + private val ClsName70 = classOf[Name70] + private val ClsName71 = classOf[Name71] + private val ClsName72 = classOf[Name72] + private val ClsName73 = classOf[Name73] + private val ClsName74 = classOf[Name74] + private val ClsName75 = classOf[Name75] + private val ClsName76 = classOf[Name76] + private val ClsName77 = classOf[Name77] + private val ClsName78 = classOf[Name78] + private val ClsName79 = classOf[Name79] + private val ClsName80 = classOf[Name80] + private val ClsName81 = classOf[Name81] + private val ClsName82 = classOf[Name82] + private val ClsName83 = classOf[Name83] + private val ClsName84 = classOf[Name84] + private val ClsName85 = classOf[Name85] + private val ClsName86 = classOf[Name86] + private val ClsName87 = classOf[Name87] + private val ClsName88 = classOf[Name88] + private val ClsName89 = classOf[Name89] + private val ClsName90 = classOf[Name90] + private val ClsName91 = classOf[Name91] + private val ClsName92 = classOf[Name92] + private val ClsName93 = classOf[Name93] + private val ClsName94 = classOf[Name94] + private val ClsName95 = classOf[Name95] + private val ClsName96 = classOf[Name96] + private val ClsName97 = classOf[Name97] + private val ClsName98 = classOf[Name98] + private val ClsName99 = classOf[Name99] + private val ClsName100 = classOf[Name100] + private val ClsName101 = classOf[Name101] + private val ClsName102 = classOf[Name102] + private val ClsName103 = classOf[Name103] + private val ClsName104 = classOf[Name104] + private val ClsName105 = classOf[Name105] + private val ClsName106 = classOf[Name106] + private val ClsName107 = classOf[Name107] + private val ClsName108 = classOf[Name108] + private val ClsName109 = classOf[Name109] + private val ClsName110 = classOf[Name110] + private val ClsName111 = classOf[Name111] + private val ClsName112 = classOf[Name112] + private val ClsName113 = classOf[Name113] + private val ClsName114 = classOf[Name114] + private val ClsName115 = classOf[Name115] + private val ClsName116 = classOf[Name116] + private val ClsName117 = classOf[Name117] + private val ClsName118 = classOf[Name118] + private val ClsName119 = classOf[Name119] + private val ClsName120 = classOf[Name120] + private val ClsName121 = classOf[Name121] + private val ClsName122 = classOf[Name122] + private val ClsName123 = classOf[Name123] + private val ClsName124 = classOf[Name124] + private val ClsName125 = classOf[Name125] + private val ClsName126 = classOf[Name126] + private val ClsName127 = classOf[Name127] + private val ClsName128 = classOf[Name128] + private val ClsName129 = classOf[Name129] + private val ClsName130 = classOf[Name130] + private val ClsName131 = classOf[Name131] + private val ClsName132 = classOf[Name132] + private val ClsName133 = classOf[Name133] + private val ClsName134 = classOf[Name134] + private val ClsName135 = classOf[Name135] + private val ClsName136 = classOf[Name136] + private val ClsName137 = classOf[Name137] + private val ClsName138 = classOf[Name138] + private val ClsName139 = classOf[Name139] + private val ClsName140 = classOf[Name140] + private val ClsName141 = classOf[Name141] + private val ClsName142 = classOf[Name142] + private val ClsName143 = classOf[Name143] + private val ClsName144 = classOf[Name144] + private val ClsName145 = classOf[Name145] + private val ClsName146 = classOf[Name146] + private val ClsName147 = classOf[Name147] + private val ClsName148 = classOf[Name148] + private val ClsName149 = classOf[Name149] + private val ClsName150 = classOf[Name150] + private val ClsName151 = classOf[Name151] + private val ClsName152 = classOf[Name152] + private val ClsName153 = classOf[Name153] + private val ClsName154 = classOf[Name154] + private val ClsName155 = classOf[Name155] + private val ClsName156 = classOf[Name156] + private val ClsName157 = classOf[Name157] + private val ClsName158 = classOf[Name158] + private val ClsName159 = classOf[Name159] + private val ClsName160 = classOf[Name160] + private val ClsName161 = classOf[Name161] + private val ClsName162 = classOf[Name162] + private val ClsName163 = classOf[Name163] + private val ClsName164 = classOf[Name164] + private val ClsName165 = classOf[Name165] + private val ClsName166 = classOf[Name166] + private val ClsName167 = classOf[Name167] + private val ClsName168 = classOf[Name168] + private val ClsName169 = classOf[Name169] + private val ClsName170 = classOf[Name170] + private val ClsName171 = classOf[Name171] + private val ClsName172 = classOf[Name172] + private val ClsName173 = classOf[Name173] + private val ClsName174 = classOf[Name174] + private val ClsName175 = classOf[Name175] + private val ClsName176 = classOf[Name176] + private val ClsName177 = classOf[Name177] + private val ClsName178 = classOf[Name178] + private val ClsName179 = classOf[Name179] + private val ClsName180 = classOf[Name180] + private val ClsName181 = classOf[Name181] + private val ClsName182 = classOf[Name182] + private val ClsName183 = classOf[Name183] + private val ClsName184 = classOf[Name184] + private val ClsName185 = classOf[Name185] + private val ClsName186 = classOf[Name186] + private val ClsName187 = classOf[Name187] + private val ClsName188 = classOf[Name188] + private val ClsName189 = classOf[Name189] + private val ClsName190 = classOf[Name190] + private val ClsName191 = classOf[Name191] + private val ClsName192 = classOf[Name192] + private val ClsName193 = classOf[Name193] + private val ClsName194 = classOf[Name194] + private val ClsName195 = classOf[Name195] + private val ClsName196 = classOf[Name196] + private val ClsName197 = classOf[Name197] + private val ClsName198 = classOf[Name198] + private val ClsName199 = classOf[Name199] + private val ClsName200 = classOf[Name200] + private val ClsName201 = classOf[Name201] + private val ClsName202 = classOf[Name202] + private val ClsName203 = classOf[Name203] + private val ClsName204 = classOf[Name204] + private val ClsName205 = classOf[Name205] + private val ClsName206 = classOf[Name206] + private val ClsName207 = classOf[Name207] + private val ClsName208 = classOf[Name208] + private val ClsName209 = classOf[Name209] + private val ClsName210 = classOf[Name210] + private val ClsName211 = classOf[Name211] + private val ClsName212 = classOf[Name212] + private val ClsName213 = classOf[Name213] + private val ClsName214 = classOf[Name214] + private val ClsName215 = classOf[Name215] + private val ClsName216 = classOf[Name216] + private val ClsName217 = classOf[Name217] + private val ClsName218 = classOf[Name218] + private val ClsName219 = classOf[Name219] + private val ClsName220 = classOf[Name220] + private val ClsName221 = classOf[Name221] + private val ClsName222 = classOf[Name222] + private val ClsName223 = classOf[Name223] + private val ClsName224 = classOf[Name224] + private val ClsName225 = classOf[Name225] + private val ClsName226 = classOf[Name226] + private val ClsName227 = classOf[Name227] + private val ClsName228 = classOf[Name228] + private val ClsName229 = classOf[Name229] + private val ClsName230 = classOf[Name230] + private val ClsName231 = classOf[Name231] + private val ClsName232 = classOf[Name232] + private val ClsName233 = classOf[Name233] + private val ClsName234 = classOf[Name234] + private val ClsName235 = classOf[Name235] + private val ClsName236 = classOf[Name236] + private val ClsName237 = classOf[Name237] + private val ClsName238 = classOf[Name238] + private val ClsName239 = classOf[Name239] + private val ClsName240 = classOf[Name240] + private val ClsName241 = classOf[Name241] + private val ClsName242 = classOf[Name242] + private val ClsName243 = classOf[Name243] + private val ClsName244 = classOf[Name244] + private val ClsName245 = classOf[Name245] + private val ClsName246 = classOf[Name246] + private val ClsName247 = classOf[Name247] + private val ClsName248 = classOf[Name248] + private val ClsName249 = classOf[Name249] + private val ClsName250 = classOf[Name250] + private val ClsName251 = classOf[Name251] + private val ClsName252 = classOf[Name252] + private val ClsName253 = classOf[Name253] + private val ClsName254 = classOf[Name254] + private val ClsName255 = classOf[Name255] +} + +sealed abstract class Name(val _id: Int) { + def virtualShow: String +} + +final case class Name0() extends Name(0) { def virtualShow = "0" } +final case class Name1() extends Name(1) { def virtualShow = "1" } +final case class Name2() extends Name(2) { def virtualShow = "2" } +final case class Name3() extends Name(3) { def virtualShow = "3" } +final case class Name4() extends Name(4) { def virtualShow = "4" } +final case class Name5() extends Name(5) { def virtualShow = "5" } +final case class Name6() extends Name(6) { def virtualShow = "6" } +final case class Name7() extends Name(7) { def virtualShow = "7" } +final case class Name8() extends Name(8) { def virtualShow = "8" } +final case class Name9() extends Name(9) { def virtualShow = "9" } +final case class Name10() extends Name(10) { def virtualShow = "10" } +final case class Name11() extends Name(11) { def virtualShow = "11" } +final case class Name12() extends Name(12) { def virtualShow = "12" } +final case class Name13() extends Name(13) { def virtualShow = "13" } +final case class Name14() extends Name(14) { def virtualShow = "14" } +final case class Name15() extends Name(15) { def virtualShow = "15" } +final case class Name16() extends Name(16) { def virtualShow = "16" } +final case class Name17() extends Name(17) { def virtualShow = "17" } +final case class Name18() extends Name(18) { def virtualShow = "18" } +final case class Name19() extends Name(19) { def virtualShow = "19" } +final case class Name20() extends Name(20) { def virtualShow = "20" } +final case class Name21() extends Name(21) { def virtualShow = "21" } +final case class Name22() extends Name(22) { def virtualShow = "22" } +final case class Name23() extends Name(23) { def virtualShow = "23" } +final case class Name24() extends Name(24) { def virtualShow = "24" } +final case class Name25() extends Name(25) { def virtualShow = "25" } +final case class Name26() extends Name(26) { def virtualShow = "26" } +final case class Name27() extends Name(27) { def virtualShow = "27" } +final case class Name28() extends Name(28) { def virtualShow = "28" } +final case class Name29() extends Name(29) { def virtualShow = "29" } +final case class Name30() extends Name(30) { def virtualShow = "30" } +final case class Name31() extends Name(31) { def virtualShow = "31" } +final case class Name32() extends Name(32) { def virtualShow = "32" } +final case class Name33() extends Name(33) { def virtualShow = "33" } +final case class Name34() extends Name(34) { def virtualShow = "34" } +final case class Name35() extends Name(35) { def virtualShow = "35" } +final case class Name36() extends Name(36) { def virtualShow = "36" } +final case class Name37() extends Name(37) { def virtualShow = "37" } +final case class Name38() extends Name(38) { def virtualShow = "38" } +final case class Name39() extends Name(39) { def virtualShow = "39" } +final case class Name40() extends Name(40) { def virtualShow = "40" } +final case class Name41() extends Name(41) { def virtualShow = "41" } +final case class Name42() extends Name(42) { def virtualShow = "42" } +final case class Name43() extends Name(43) { def virtualShow = "43" } +final case class Name44() extends Name(44) { def virtualShow = "44" } +final case class Name45() extends Name(45) { def virtualShow = "45" } +final case class Name46() extends Name(46) { def virtualShow = "46" } +final case class Name47() extends Name(47) { def virtualShow = "47" } +final case class Name48() extends Name(48) { def virtualShow = "48" } +final case class Name49() extends Name(49) { def virtualShow = "49" } +final case class Name50() extends Name(50) { def virtualShow = "50" } +final case class Name51() extends Name(51) { def virtualShow = "51" } +final case class Name52() extends Name(52) { def virtualShow = "52" } +final case class Name53() extends Name(53) { def virtualShow = "53" } +final case class Name54() extends Name(54) { def virtualShow = "54" } +final case class Name55() extends Name(55) { def virtualShow = "55" } +final case class Name56() extends Name(56) { def virtualShow = "56" } +final case class Name57() extends Name(57) { def virtualShow = "57" } +final case class Name58() extends Name(58) { def virtualShow = "58" } +final case class Name59() extends Name(59) { def virtualShow = "59" } +final case class Name60() extends Name(60) { def virtualShow = "60" } +final case class Name61() extends Name(61) { def virtualShow = "61" } +final case class Name62() extends Name(62) { def virtualShow = "62" } +final case class Name63() extends Name(63) { def virtualShow = "63" } +final case class Name64() extends Name(64) { def virtualShow = "64" } +final case class Name65() extends Name(65) { def virtualShow = "65" } +final case class Name66() extends Name(66) { def virtualShow = "66" } +final case class Name67() extends Name(67) { def virtualShow = "67" } +final case class Name68() extends Name(68) { def virtualShow = "68" } +final case class Name69() extends Name(69) { def virtualShow = "69" } +final case class Name70() extends Name(70) { def virtualShow = "70" } +final case class Name71() extends Name(71) { def virtualShow = "71" } +final case class Name72() extends Name(72) { def virtualShow = "72" } +final case class Name73() extends Name(73) { def virtualShow = "73" } +final case class Name74() extends Name(74) { def virtualShow = "74" } +final case class Name75() extends Name(75) { def virtualShow = "75" } +final case class Name76() extends Name(76) { def virtualShow = "76" } +final case class Name77() extends Name(77) { def virtualShow = "77" } +final case class Name78() extends Name(78) { def virtualShow = "78" } +final case class Name79() extends Name(79) { def virtualShow = "79" } +final case class Name80() extends Name(80) { def virtualShow = "80" } +final case class Name81() extends Name(81) { def virtualShow = "81" } +final case class Name82() extends Name(82) { def virtualShow = "82" } +final case class Name83() extends Name(83) { def virtualShow = "83" } +final case class Name84() extends Name(84) { def virtualShow = "84" } +final case class Name85() extends Name(85) { def virtualShow = "85" } +final case class Name86() extends Name(86) { def virtualShow = "86" } +final case class Name87() extends Name(87) { def virtualShow = "87" } +final case class Name88() extends Name(88) { def virtualShow = "88" } +final case class Name89() extends Name(89) { def virtualShow = "89" } +final case class Name90() extends Name(90) { def virtualShow = "90" } +final case class Name91() extends Name(91) { def virtualShow = "91" } +final case class Name92() extends Name(92) { def virtualShow = "92" } +final case class Name93() extends Name(93) { def virtualShow = "93" } +final case class Name94() extends Name(94) { def virtualShow = "94" } +final case class Name95() extends Name(95) { def virtualShow = "95" } +final case class Name96() extends Name(96) { def virtualShow = "96" } +final case class Name97() extends Name(97) { def virtualShow = "97" } +final case class Name98() extends Name(98) { def virtualShow = "98" } +final case class Name99() extends Name(99) { def virtualShow = "99" } +final case class Name100() extends Name(100) { def virtualShow = "100" } +final case class Name101() extends Name(101) { def virtualShow = "101" } +final case class Name102() extends Name(102) { def virtualShow = "102" } +final case class Name103() extends Name(103) { def virtualShow = "103" } +final case class Name104() extends Name(104) { def virtualShow = "104" } +final case class Name105() extends Name(105) { def virtualShow = "105" } +final case class Name106() extends Name(106) { def virtualShow = "106" } +final case class Name107() extends Name(107) { def virtualShow = "107" } +final case class Name108() extends Name(108) { def virtualShow = "108" } +final case class Name109() extends Name(109) { def virtualShow = "109" } +final case class Name110() extends Name(110) { def virtualShow = "110" } +final case class Name111() extends Name(111) { def virtualShow = "111" } +final case class Name112() extends Name(112) { def virtualShow = "112" } +final case class Name113() extends Name(113) { def virtualShow = "113" } +final case class Name114() extends Name(114) { def virtualShow = "114" } +final case class Name115() extends Name(115) { def virtualShow = "115" } +final case class Name116() extends Name(116) { def virtualShow = "116" } +final case class Name117() extends Name(117) { def virtualShow = "117" } +final case class Name118() extends Name(118) { def virtualShow = "118" } +final case class Name119() extends Name(119) { def virtualShow = "119" } +final case class Name120() extends Name(120) { def virtualShow = "120" } +final case class Name121() extends Name(121) { def virtualShow = "121" } +final case class Name122() extends Name(122) { def virtualShow = "122" } +final case class Name123() extends Name(123) { def virtualShow = "123" } +final case class Name124() extends Name(124) { def virtualShow = "124" } +final case class Name125() extends Name(125) { def virtualShow = "125" } +final case class Name126() extends Name(126) { def virtualShow = "126" } +final case class Name127() extends Name(127) { def virtualShow = "127" } +final case class Name128() extends Name(128) { def virtualShow = "128" } +final case class Name129() extends Name(129) { def virtualShow = "129" } +final case class Name130() extends Name(130) { def virtualShow = "130" } +final case class Name131() extends Name(131) { def virtualShow = "131" } +final case class Name132() extends Name(132) { def virtualShow = "132" } +final case class Name133() extends Name(133) { def virtualShow = "133" } +final case class Name134() extends Name(134) { def virtualShow = "134" } +final case class Name135() extends Name(135) { def virtualShow = "135" } +final case class Name136() extends Name(136) { def virtualShow = "136" } +final case class Name137() extends Name(137) { def virtualShow = "137" } +final case class Name138() extends Name(138) { def virtualShow = "138" } +final case class Name139() extends Name(139) { def virtualShow = "139" } +final case class Name140() extends Name(140) { def virtualShow = "140" } +final case class Name141() extends Name(141) { def virtualShow = "141" } +final case class Name142() extends Name(142) { def virtualShow = "142" } +final case class Name143() extends Name(143) { def virtualShow = "143" } +final case class Name144() extends Name(144) { def virtualShow = "144" } +final case class Name145() extends Name(145) { def virtualShow = "145" } +final case class Name146() extends Name(146) { def virtualShow = "146" } +final case class Name147() extends Name(147) { def virtualShow = "147" } +final case class Name148() extends Name(148) { def virtualShow = "148" } +final case class Name149() extends Name(149) { def virtualShow = "149" } +final case class Name150() extends Name(150) { def virtualShow = "150" } +final case class Name151() extends Name(151) { def virtualShow = "151" } +final case class Name152() extends Name(152) { def virtualShow = "152" } +final case class Name153() extends Name(153) { def virtualShow = "153" } +final case class Name154() extends Name(154) { def virtualShow = "154" } +final case class Name155() extends Name(155) { def virtualShow = "155" } +final case class Name156() extends Name(156) { def virtualShow = "156" } +final case class Name157() extends Name(157) { def virtualShow = "157" } +final case class Name158() extends Name(158) { def virtualShow = "158" } +final case class Name159() extends Name(159) { def virtualShow = "159" } +final case class Name160() extends Name(160) { def virtualShow = "160" } +final case class Name161() extends Name(161) { def virtualShow = "161" } +final case class Name162() extends Name(162) { def virtualShow = "162" } +final case class Name163() extends Name(163) { def virtualShow = "163" } +final case class Name164() extends Name(164) { def virtualShow = "164" } +final case class Name165() extends Name(165) { def virtualShow = "165" } +final case class Name166() extends Name(166) { def virtualShow = "166" } +final case class Name167() extends Name(167) { def virtualShow = "167" } +final case class Name168() extends Name(168) { def virtualShow = "168" } +final case class Name169() extends Name(169) { def virtualShow = "169" } +final case class Name170() extends Name(170) { def virtualShow = "170" } +final case class Name171() extends Name(171) { def virtualShow = "171" } +final case class Name172() extends Name(172) { def virtualShow = "172" } +final case class Name173() extends Name(173) { def virtualShow = "173" } +final case class Name174() extends Name(174) { def virtualShow = "174" } +final case class Name175() extends Name(175) { def virtualShow = "175" } +final case class Name176() extends Name(176) { def virtualShow = "176" } +final case class Name177() extends Name(177) { def virtualShow = "177" } +final case class Name178() extends Name(178) { def virtualShow = "178" } +final case class Name179() extends Name(179) { def virtualShow = "179" } +final case class Name180() extends Name(180) { def virtualShow = "180" } +final case class Name181() extends Name(181) { def virtualShow = "181" } +final case class Name182() extends Name(182) { def virtualShow = "182" } +final case class Name183() extends Name(183) { def virtualShow = "183" } +final case class Name184() extends Name(184) { def virtualShow = "184" } +final case class Name185() extends Name(185) { def virtualShow = "185" } +final case class Name186() extends Name(186) { def virtualShow = "186" } +final case class Name187() extends Name(187) { def virtualShow = "187" } +final case class Name188() extends Name(188) { def virtualShow = "188" } +final case class Name189() extends Name(189) { def virtualShow = "189" } +final case class Name190() extends Name(190) { def virtualShow = "190" } +final case class Name191() extends Name(191) { def virtualShow = "191" } +final case class Name192() extends Name(192) { def virtualShow = "192" } +final case class Name193() extends Name(193) { def virtualShow = "193" } +final case class Name194() extends Name(194) { def virtualShow = "194" } +final case class Name195() extends Name(195) { def virtualShow = "195" } +final case class Name196() extends Name(196) { def virtualShow = "196" } +final case class Name197() extends Name(197) { def virtualShow = "197" } +final case class Name198() extends Name(198) { def virtualShow = "198" } +final case class Name199() extends Name(199) { def virtualShow = "199" } +final case class Name200() extends Name(200) { def virtualShow = "200" } +final case class Name201() extends Name(201) { def virtualShow = "201" } +final case class Name202() extends Name(202) { def virtualShow = "202" } +final case class Name203() extends Name(203) { def virtualShow = "203" } +final case class Name204() extends Name(204) { def virtualShow = "204" } +final case class Name205() extends Name(205) { def virtualShow = "205" } +final case class Name206() extends Name(206) { def virtualShow = "206" } +final case class Name207() extends Name(207) { def virtualShow = "207" } +final case class Name208() extends Name(208) { def virtualShow = "208" } +final case class Name209() extends Name(209) { def virtualShow = "209" } +final case class Name210() extends Name(210) { def virtualShow = "210" } +final case class Name211() extends Name(211) { def virtualShow = "211" } +final case class Name212() extends Name(212) { def virtualShow = "212" } +final case class Name213() extends Name(213) { def virtualShow = "213" } +final case class Name214() extends Name(214) { def virtualShow = "214" } +final case class Name215() extends Name(215) { def virtualShow = "215" } +final case class Name216() extends Name(216) { def virtualShow = "216" } +final case class Name217() extends Name(217) { def virtualShow = "217" } +final case class Name218() extends Name(218) { def virtualShow = "218" } +final case class Name219() extends Name(219) { def virtualShow = "219" } +final case class Name220() extends Name(220) { def virtualShow = "220" } +final case class Name221() extends Name(221) { def virtualShow = "221" } +final case class Name222() extends Name(222) { def virtualShow = "222" } +final case class Name223() extends Name(223) { def virtualShow = "223" } +final case class Name224() extends Name(224) { def virtualShow = "224" } +final case class Name225() extends Name(225) { def virtualShow = "225" } +final case class Name226() extends Name(226) { def virtualShow = "226" } +final case class Name227() extends Name(227) { def virtualShow = "227" } +final case class Name228() extends Name(228) { def virtualShow = "228" } +final case class Name229() extends Name(229) { def virtualShow = "229" } +final case class Name230() extends Name(230) { def virtualShow = "230" } +final case class Name231() extends Name(231) { def virtualShow = "231" } +final case class Name232() extends Name(232) { def virtualShow = "232" } +final case class Name233() extends Name(233) { def virtualShow = "233" } +final case class Name234() extends Name(234) { def virtualShow = "234" } +final case class Name235() extends Name(235) { def virtualShow = "235" } +final case class Name236() extends Name(236) { def virtualShow = "236" } +final case class Name237() extends Name(237) { def virtualShow = "237" } +final case class Name238() extends Name(238) { def virtualShow = "238" } +final case class Name239() extends Name(239) { def virtualShow = "239" } +final case class Name240() extends Name(240) { def virtualShow = "240" } +final case class Name241() extends Name(241) { def virtualShow = "241" } +final case class Name242() extends Name(242) { def virtualShow = "242" } +final case class Name243() extends Name(243) { def virtualShow = "243" } +final case class Name244() extends Name(244) { def virtualShow = "244" } +final case class Name245() extends Name(245) { def virtualShow = "245" } +final case class Name246() extends Name(246) { def virtualShow = "246" } +final case class Name247() extends Name(247) { def virtualShow = "247" } +final case class Name248() extends Name(248) { def virtualShow = "248" } +final case class Name249() extends Name(249) { def virtualShow = "249" } +final case class Name250() extends Name(250) { def virtualShow = "250" } +final case class Name251() extends Name(251) { def virtualShow = "251" } +final case class Name252() extends Name(252) { def virtualShow = "252" } +final case class Name253() extends Name(253) { def virtualShow = "253" } +final case class Name254() extends Name(254) { def virtualShow = "254" } +final case class Name255() extends Name(255) { def virtualShow = "255" } diff --git a/test/files/instrumented/t11882a.check b/test/files/instrumented/t11882a.check new file mode 100644 index 000000000000..cfb515adcc98 --- /dev/null +++ b/test/files/instrumented/t11882a.check @@ -0,0 +1,8 @@ +Method call statistics: + 1 OptimusSeq$.apply1(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lscala/reflect/ClassTag;)LOptimusSeq; + 1 OptimusSeq$.unsafeFromAnyArray1([Ljava/lang/Object;)LOptimusSeq; + 1 Test$.doIt$1()LOptimusSeq; + 1 scala/reflect/ClassTag$.AnyRef()Lscala/reflect/ClassTag; + 1 scala/reflect/ManifestFactory$ObjectManifest.newArray(I)Ljava/lang/Object; + 1 scala/reflect/ManifestFactory$ObjectManifest.newArray(I)[Ljava/lang/Object; + 4 scala/runtime/ScalaRunTime$.array_update(Ljava/lang/Object;ILjava/lang/Object;)V diff --git a/test/files/instrumented/t11882a.scala b/test/files/instrumented/t11882a.scala new file mode 100644 index 000000000000..2988791c320b --- /dev/null +++ b/test/files/instrumented/t11882a.scala @@ -0,0 +1,23 @@ +import scala.reflect.ClassTag +import scala.tools.partest.instrumented._ +import scala.tools.partest.instrumented.Instrumentation._ + +class OptimusSeq[T] + +object OptimusSeq { + private def unsafeFromAnyArray1[T <: AnyRef](ts: Array[T]): OptimusSeq[T] = null; + def apply1[T <: AnyRef : ClassTag](p1: T, p2: T, p3: T, p4: T): OptimusSeq[T] = { + unsafeFromAnyArray1(Array(p1, p2, p3, p4)) + } +} + +object Test { + def main(args: Array[String]): Unit = { + def doIt = OptimusSeq.apply1[AnyRef](null, null, null, null) + doIt + startProfiling() + doIt + stopProfiling() + printStatistics() + } +} diff --git a/test/files/instrumented/t11882b.check b/test/files/instrumented/t11882b.check new file mode 100644 index 000000000000..2b4a809e6e7e --- /dev/null +++ b/test/files/instrumented/t11882b.check @@ -0,0 +1,8 @@ +Method call statistics: + 1 OptimusSeq$.apply1(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lscala/reflect/ClassTag;)LOptimusSeq; + 1 OptimusSeq$.unsafeFromAnyArray1(Ljava/lang/Object;)LOptimusSeq; + 1 Test$.doIt$1()LOptimusSeq; + 1 scala/reflect/ClassTag$.AnyRef()Lscala/reflect/ClassTag; + 1 scala/reflect/ManifestFactory$ObjectManifest.newArray(I)Ljava/lang/Object; + 1 scala/reflect/ManifestFactory$ObjectManifest.newArray(I)[Ljava/lang/Object; + 4 scala/runtime/ScalaRunTime$.array_update(Ljava/lang/Object;ILjava/lang/Object;)V diff --git a/test/files/instrumented/t11882b.scala b/test/files/instrumented/t11882b.scala new file mode 100644 index 000000000000..2f1c33d72a2d --- /dev/null +++ b/test/files/instrumented/t11882b.scala @@ -0,0 +1,23 @@ +import scala.reflect.ClassTag +import scala.tools.partest.instrumented._ +import scala.tools.partest.instrumented.Instrumentation._ + +class OptimusSeq[T] + +object OptimusSeq { + private def unsafeFromAnyArray1[T](ts: Array[T]): OptimusSeq[T] = null; + def apply1[T : ClassTag](p1: T, p2: T, p3: T, p4: T): OptimusSeq[T] = { + unsafeFromAnyArray1(Array(p1, p2, p3, p4)) + } +} + +object Test { + def main(args: Array[String]): Unit = { + def doIt = OptimusSeq.apply1[AnyRef](null, null, null, null) + doIt + startProfiling() + doIt + stopProfiling() + printStatistics() + } +} diff --git a/test/files/instrumented/t11882c.check b/test/files/instrumented/t11882c.check new file mode 100644 index 000000000000..f80f495cf1d3 --- /dev/null +++ b/test/files/instrumented/t11882c.check @@ -0,0 +1,2 @@ +Method call statistics: + 1 Test$.doIt$1()[Ljava/lang/String; diff --git a/test/files/instrumented/t11882c.scala b/test/files/instrumented/t11882c.scala new file mode 100644 index 000000000000..f7b33ad864ed --- /dev/null +++ b/test/files/instrumented/t11882c.scala @@ -0,0 +1,14 @@ +import scala.reflect.ClassTag +import scala.tools.partest.instrumented._ +import scala.tools.partest.instrumented.Instrumentation._ + +object Test { + def main(args: Array[String]): Unit = { + def doIt = Array[String](null, null, null, null) + doIt + startProfiling() + doIt + stopProfiling() + printStatistics() + } +} diff --git a/test/files/instrumented/t12201.check b/test/files/instrumented/t12201.check new file mode 100644 index 000000000000..ba4c268ba7ac --- /dev/null +++ b/test/files/instrumented/t12201.check @@ -0,0 +1,3 @@ +Method call statistics: + 1 scala/runtime/BoxedUnit.()V + 1 scala/runtime/BoxedUnit.()V diff --git a/test/files/instrumented/t12201.scala b/test/files/instrumented/t12201.scala new file mode 100644 index 000000000000..a5a1d1860bdb --- /dev/null +++ b/test/files/instrumented/t12201.scala @@ -0,0 +1,29 @@ +import scala.tools.partest.instrumented.Instrumentation._ + +object Test { + def main(args: Array[String]): Unit = { + startProfiling() + + // to optimized + val x = Array[Double](1) + val y = Array[Double](1.0) + + // Currently correctly optimized + val i = Array(1.0) + val j: Array[Double] = Array(1) + + //others case + val a: Array[Double] = Array[Double](1.0) + val b: Array[Double] = Array[Double](1) + val c: Array[Double] = Array[Double](1: Double) + val d: Array[Double] = Array(1: Double) + val e = Array(1: Double) + val f = Array(1: Int) + val g = Array[Int](1) + val h = Array(1) + val k = Array[Unit](()) + + stopProfiling() + printStatistics() + } +} diff --git a/test/files/jvm/methvsfield.javaopts b/test/files/jvm/methvsfield.javaopts deleted file mode 100644 index 9740f07b079b..000000000000 --- a/test/files/jvm/methvsfield.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm \ No newline at end of file diff --git a/test/files/jvm/methvsfield/Test_2.scala b/test/files/jvm/methvsfield/Test_2.scala index 5389836be277..b9ad46ac7426 100644 --- a/test/files/jvm/methvsfield/Test_2.scala +++ b/test/files/jvm/methvsfield/Test_2.scala @@ -1,3 +1,4 @@ +// java: -Dneeds.forked.jvm // bug #1062 object Test extends App { println((new MethVsField_1).three) diff --git a/test/files/jvm/natives.javaopts b/test/files/jvm/natives.javaopts deleted file mode 100644 index 57b2283c7fb3..000000000000 --- a/test/files/jvm/natives.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.to.fork \ No newline at end of file diff --git a/test/files/jvm/natives.scala b/test/files/jvm/natives.scala index 2d19f3cbfda0..15a8b298f343 100644 --- a/test/files/jvm/natives.scala +++ b/test/files/jvm/natives.scala @@ -1,3 +1,5 @@ +// java: -Dneeds.to.fork + object Test { //println("java.library.path=" + System.getProperty("java.library.path")) diff --git a/test/files/jvm/t1600.javaopts b/test/files/jvm/t1600.javaopts deleted file mode 100644 index f4038254ba29..000000000000 --- a/test/files/jvm/t1600.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm.maybe.because.context.classloader \ No newline at end of file diff --git a/test/files/jvm/t1600.scala b/test/files/jvm/t1600.scala index b434862adb1e..da04a5f7c923 100644 --- a/test/files/jvm/t1600.scala +++ b/test/files/jvm/t1600.scala @@ -1,3 +1,4 @@ +// java: -Dneeds.forked.jvm.maybe.because.context.classloader /** * Checks that serialization of hash-based collections works correctly if the hashCode diff --git a/test/files/jvm/t7994s.check b/test/files/jvm/t7994s.check new file mode 100644 index 000000000000..5f68d930550c --- /dev/null +++ b/test/files/jvm/t7994s.check @@ -0,0 +1,4 @@ +Test$$anon$1 +null +Test$$anon$1$$anon$2 +null diff --git a/test/files/jvm/t7994s.scala b/test/files/jvm/t7994s.scala new file mode 100644 index 000000000000..36b8068018a5 --- /dev/null +++ b/test/files/jvm/t7994s.scala @@ -0,0 +1,12 @@ +object Test { + def main(args: Array[String]): Unit = { + val o = new MyTest() { + val i: MyTest = new MyTest() {} + } + } +} + +class MyTest { + println(this.getClass.getName) + println(this.getClass.getDeclaringClass) +} \ No newline at end of file diff --git a/test/files/jvm/t8689.javaopts b/test/files/jvm/t8689.javaopts deleted file mode 100644 index 9740f07b079b..000000000000 --- a/test/files/jvm/t8689.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm \ No newline at end of file diff --git a/test/files/jvm/t8689.scala b/test/files/jvm/t8689.scala index 3ee20d711a92..2eeb12a12cf1 100644 --- a/test/files/jvm/t8689.scala +++ b/test/files/jvm/t8689.scala @@ -1,3 +1,4 @@ +// java: -Dneeds.forked.jvm object Test { def main(args: Array[String]): Unit = { import scala.concurrent._ diff --git a/test/files/neg/and-future.check b/test/files/neg/and-future.check new file mode 100644 index 000000000000..c7992b38964e --- /dev/null +++ b/test/files/neg/and-future.check @@ -0,0 +1,7 @@ +and-future.scala:9: error: Cannot parse infix type combining `&` and `Map`, please use `Map` as the head of a regular type application. + val b: Int Map X & Int Map Y = Map[Int, X & Y]() // error: unsupported + ^ +and-future.scala:13: error: Cannot parse infix type combining `&` and `Map`, please use `Map` as the head of a regular type application. + val c: (Int Map X) & (Int Map Y) = Map[Int, X & Y]() // error: unsupported + ^ +2 errors diff --git a/test/files/neg/and-future.scala b/test/files/neg/and-future.scala new file mode 100644 index 000000000000..1092c013b186 --- /dev/null +++ b/test/files/neg/and-future.scala @@ -0,0 +1,14 @@ +// scalac: -Xsource:3 +// + +trait X +trait Y + +class Test { + val a: Map[Int, X] & Map[Int, Y] = Map[Int, X & Y]() // ok + val b: Int Map X & Int Map Y = Map[Int, X & Y]() // error: unsupported + + // This one is unambiguous but it's hard to check whether parens were present + // from the parser output so we also emit an error there. + val c: (Int Map X) & (Int Map Y) = Map[Int, X & Y]() // error: unsupported +} diff --git a/test/files/neg/annotated-literal-annotation-arg.check b/test/files/neg/annotated-literal-annotation-arg.check new file mode 100644 index 000000000000..220ab9a992f3 --- /dev/null +++ b/test/files/neg/annotated-literal-annotation-arg.check @@ -0,0 +1,7 @@ +annotated-literal-annotation-arg.scala:14: error: $foo + implicitly[Foo] + ^ +annotated-literal-annotation-arg.scala:15: error: bar + implicitly[Bar] + ^ +2 errors diff --git a/test/files/neg/annotated-literal-annotation-arg.scala b/test/files/neg/annotated-literal-annotation-arg.scala new file mode 100644 index 000000000000..92a089f5948f --- /dev/null +++ b/test/files/neg/annotated-literal-annotation-arg.scala @@ -0,0 +1,16 @@ + +import annotation.implicitNotFound +import scala.annotation.nowarn + +// Ensure that an annotation doesn't break the message +@implicitNotFound("$foo": @nowarn) +trait Foo + +// Ensure that a type ascription doesn't break the message +@implicitNotFound("bar": String) +trait Bar + +object Example { + implicitly[Foo] + implicitly[Bar] +} diff --git a/test/files/neg/annots-constant-neg.check b/test/files/neg/annots-constant-neg.check index 800e06c70489..f531b2a98540 100644 --- a/test/files/neg/annots-constant-neg.check +++ b/test/files/neg/annots-constant-neg.check @@ -79,7 +79,7 @@ Test.scala:71: error: annotation argument needs to be a constant; found: new sca Test.scala:76: error: multiple constructors for Ann1 with alternatives: (s: String)Ann1 (value: Int)Ann1 - cannot be invoked with (x: String) + [which have no such parameter x] cannot be invoked with (x: String) @Ann1(x = "") def v4 = 0 // err ^ Test.scala:78: error: Ann1 does not take parameters diff --git a/test/files/neg/catch-all.check b/test/files/neg/catch-all.check index ac20a14164ae..e56376138e0a 100644 --- a/test/files/neg/catch-all.check +++ b/test/files/neg/catch-all.check @@ -7,6 +7,9 @@ catch-all.scala:6: warning: This catches all Throwables. If this is really inten catch-all.scala:8: warning: This catches all Throwables. If this is really intended, use `case x : Throwable` to clear this warning. try { "warn" } catch { case _: RuntimeException => ; case x => } ^ +catch-all.scala:36: warning: This catches all Throwables. If this is really intended, use `case _ : Throwable` to clear this warning. + try "okay" catch discarder // warn total function + ^ error: No warnings can be incurred under -Werror. -3 warnings +4 warnings 1 error diff --git a/test/files/neg/catch-all.scala b/test/files/neg/catch-all.scala index c8308e1d8939..eb9f9b506716 100644 --- a/test/files/neg/catch-all.scala +++ b/test/files/neg/catch-all.scala @@ -1,4 +1,4 @@ -// scalac: -Xfatal-warnings +// scalac: -Werror // object CatchAll { try { "warn" } catch { case _ => } @@ -28,6 +28,12 @@ object CatchAll { try { "okay" } catch { case _ if "".isEmpty => } "okay" match { case _ => "" } + + val handler: PartialFunction[Throwable, String] = { case _ => "hello, world" } + val discarder = (_: Throwable) => "goodbye, cruel world" + + try "okay" catch handler + try "okay" catch discarder // warn total function } object T extends Throwable diff --git a/test/files/neg/for-comprehension-case-future.check b/test/files/neg/for-comprehension-case-future.check new file mode 100644 index 000000000000..9ce9a9456882 --- /dev/null +++ b/test/files/neg/for-comprehension-case-future.check @@ -0,0 +1,7 @@ +for-comprehension-case-future.scala:22: error: '<-' expected but '=' found. + case y = x + 1 + ^ +for-comprehension-case-future.scala:23: error: illegal start of simple expression + } yield x + y + ^ +2 errors diff --git a/test/files/neg/for-comprehension-case-future.scala b/test/files/neg/for-comprehension-case-future.scala new file mode 100644 index 000000000000..05602e537759 --- /dev/null +++ b/test/files/neg/for-comprehension-case-future.scala @@ -0,0 +1,24 @@ +// scalac: -Xsource:3 +// +class A { + // ok + val a = + for { + case Some(x) <- List(Some(1), None) + y = x + 1 + } yield x + y + + // ok + val b = + for { + Some(x) <- List(Some(1), None) + Some(y) <- List(None, Some(2)) + } yield x+y + + // fail + val c = + for { + case Some(x) <- List(Some(1), None) + case y = x + 1 + } yield x + y +} diff --git a/test/files/neg/for-comprehension-case.check b/test/files/neg/for-comprehension-case.check new file mode 100644 index 000000000000..2e86e5d367b0 --- /dev/null +++ b/test/files/neg/for-comprehension-case.check @@ -0,0 +1,13 @@ +for-comprehension-case.scala:5: error: `case` keyword in for comprehension requires the -Xsource:3 flag. + case Some(x) <- List(Some(1), None) + ^ +for-comprehension-case.scala:12: error: `case` keyword in for comprehension requires the -Xsource:3 flag. + case y = x + 1 + ^ +for-comprehension-case.scala:12: error: '<-' expected but '=' found. + case y = x + 1 + ^ +for-comprehension-case.scala:13: error: illegal start of simple expression + } yield x+y + ^ +4 errors diff --git a/test/files/neg/for-comprehension-case.scala b/test/files/neg/for-comprehension-case.scala new file mode 100644 index 000000000000..55e8d44a40e3 --- /dev/null +++ b/test/files/neg/for-comprehension-case.scala @@ -0,0 +1,14 @@ +class A { + // fail + val a = + for { + case Some(x) <- List(Some(1), None) + } yield x + + // fail + val b = + for { + Some(x) <- List(Some(1), None) + case y = x + 1 + } yield x+y +} diff --git a/test/files/neg/implicit-any2stringadd.scala b/test/files/neg/implicit-any2stringadd.scala index 80f1ab29bd85..7e86c89cd31f 100644 --- a/test/files/neg/implicit-any2stringadd.scala +++ b/test/files/neg/implicit-any2stringadd.scala @@ -1,4 +1,4 @@ -// scalac: -Xsource:3 -Xlog-implicits +// scalac: -Xsource:3 -Vimplicits // object Test { true + "what" diff --git a/test/files/neg/implicit-log.check b/test/files/neg/implicit-log.check index c0115c6291a9..541aa6251b25 100644 --- a/test/files/neg/implicit-log.check +++ b/test/files/neg/implicit-log.check @@ -1,13 +1,3 @@ -implicit-log.scala:61: byVal is not a valid implicit value for Int(7) => ?{def unwrap: ?} because: -incompatible: (x: 7): 7 does not match expected type Int(7) => ?{def unwrap: ?} - val res = 7.unwrap() // doesn't work - ^ -implicit-log.scala:70: materializing requested scala.reflect.type.ClassTag[String] using scala.reflect.`package`.materializeClassTag[String]() - val x: java.util.List[String] = List("foo") - ^ -implicit-log.scala:96: materializing requested reflect.runtime.universe.type.TypeTag[Class[_]] using scala.reflect.api.`package`.materializeTypeTag[Class[_]](scala.reflect.runtime.`package`.universe) - println(implicitly[TypeTag[Class[_]]]) - ^ implicit-log.scala:100: error: value baa is not a member of Int 1.baa ^ diff --git a/test/files/neg/implicit-log.scala b/test/files/neg/implicit-log.scala index adfe3acbf0e3..f77085e3c2af 100644 --- a/test/files/neg/implicit-log.scala +++ b/test/files/neg/implicit-log.scala @@ -1,4 +1,4 @@ -/* scalac: -Xlog-implicits -Xsource:3 -Xfatal-warnings */ +/* scalac: -Vimplicits -Xsource:3 -Xfatal-warnings */ package foo diff --git a/test/files/neg/implicit-shadow.check b/test/files/neg/implicit-shadow.check index 423f7c56aa99..d7909b9c3a11 100644 --- a/test/files/neg/implicit-shadow.check +++ b/test/files/neg/implicit-shadow.check @@ -1,10 +1,3 @@ -implicit-shadow.scala:6: is not a valid implicit value for Int(1) => ?{def isEmpty: ?} because: -reference to i2s is ambiguous; -it is imported twice in the same scope by -import C._ -and import B._ - 1.isEmpty - ^ implicit-shadow.scala:6: error: value isEmpty is not a member of Int 1.isEmpty ^ diff --git a/test/files/neg/implicit-shadow.scala b/test/files/neg/implicit-shadow.scala index 7fea7d5d32a0..33725ece13f1 100644 --- a/test/files/neg/implicit-shadow.scala +++ b/test/files/neg/implicit-shadow.scala @@ -1,4 +1,4 @@ -// scalac: -Xlog-implicits +// scalac: -Vimplicits // object Test { import B._, C._ diff --git a/test/files/neg/import-future.check b/test/files/neg/import-future.check new file mode 100644 index 000000000000..000601f45b7d --- /dev/null +++ b/test/files/neg/import-future.check @@ -0,0 +1,4 @@ +import-future.scala:15: error: not found: value unrelated + unrelated(1) // error + ^ +1 error diff --git a/test/files/neg/import-future.scala b/test/files/neg/import-future.scala new file mode 100644 index 000000000000..288fd3d0e240 --- /dev/null +++ b/test/files/neg/import-future.scala @@ -0,0 +1,27 @@ +// scalac: -Xsource:3 +// + +class D { + def *(y: Int): Int = y + def unrelated(y: Int): Int = y +} + +object Test { + val d = new D + + def one: Int = { + import d.`*` + + unrelated(1) // error + + *(1) + } + + def two: Int = { + import d.* + + unrelated(1) + + *(1) + } +} diff --git a/test/files/neg/import-syntax.check b/test/files/neg/import-syntax.check new file mode 100644 index 000000000000..887677e3cfd9 --- /dev/null +++ b/test/files/neg/import-syntax.check @@ -0,0 +1,7 @@ +import-syntax.scala:10: error: Wildcard import cannot be renamed + import d.{* => huh} + ^ +import-syntax.scala:11: error: Wildcard import cannot be renamed + import d.{_ => also_no} + ^ +2 errors diff --git a/test/files/neg/import-syntax.scala b/test/files/neg/import-syntax.scala new file mode 100644 index 000000000000..0e3deb00cce0 --- /dev/null +++ b/test/files/neg/import-syntax.scala @@ -0,0 +1,12 @@ +// scalac: -Xsource:3 + +class D { + def *(y: Int): Int = y + def unrelated(y: Int): Int = y +} + +object nope { + val d = new D + import d.{* => huh} + import d.{_ => also_no} +} diff --git a/test/files/neg/macro-invalidret.check b/test/files/neg/macro-invalidret.check index ceba1b88c927..11097f429909 100644 --- a/test/files/neg/macro-invalidret.check +++ b/test/files/neg/macro-invalidret.check @@ -39,9 +39,9 @@ type mismatch for return type: reflect.runtime.universe.Literal does not conform def bar2: Int = macro Impls.foo2 ^ Macros_Test_2.scala:33: error: exception during macro expansion: -#partest !java15 +#partest !java15+ java.lang.NullPointerException -#partest java15 +#partest java15+ java.lang.NullPointerException: Cannot throw exception because "null" is null #partest at Impls$.foo3(Impls_1.scala:7) diff --git a/test/files/neg/main1.check b/test/files/neg/main1.check index e1ba37b9ac90..4d9ef2fba906 100644 --- a/test/files/neg/main1.check +++ b/test/files/neg/main1.check @@ -29,7 +29,7 @@ main1.scala:41: warning: Foo has a valid main method (args: Array[String]): Unit object Foo extends Foo { // Overrides main from the class ^ main1.scala:53: warning: not a valid main method for p6.Main, - because main methods must have the exact signature (Array[String])Unit. + because main methods must have the exact signature `(Array[String]): Unit`. To define an entry point, please define the main method as: def main(args: Array[String]): Unit @@ -42,7 +42,7 @@ main1.scala:59: warning: Main has a main method (args: Array[Int]): Unit, object Main { ^ main1.scala:60: warning: not a valid main method for p7.Main, - because main methods must have the exact signature (Array[String])Unit. + because main methods must have the exact signature `(Array[String]): Unit`. To define an entry point, please define the main method as: def main(args: Array[String]): Unit @@ -55,19 +55,26 @@ main1.scala:66: warning: Main has a main method, object Main { ^ main1.scala:68: warning: not a valid main method for p8.Main, - because main methods must have the exact signature (Array[String])Unit. + because main methods must have the exact signature `(Array[String]): Unit`. To define an entry point, please define the main method as: def main(args: Array[String]): Unit def main(args: Array[Double]) = () ^ main1.scala:67: warning: not a valid main method for p8.Main, - because main methods must have the exact signature (Array[String])Unit. + because main methods must have the exact signature `(Array[String]): Unit`. To define an entry point, please define the main method as: def main(args: Array[String]): Unit def main(args: Array[Int]) = () ^ +main1.scala:74: warning: not a valid main method for t7448.Main, + because main methods must have the exact signature `(Array[String]): Unit`, though Scala runners will forgive a non-Unit result. + To define an entry point, please define the main method as: + def main(args: Array[String]): Unit + + def main(args: Array[String]) = ??? + ^ error: No warnings can be incurred under -Werror. -11 warnings +12 warnings 1 error diff --git a/test/files/neg/main1.scala b/test/files/neg/main1.scala index 88a94d85bbb2..295920808350 100644 --- a/test/files/neg/main1.scala +++ b/test/files/neg/main1.scala @@ -68,3 +68,9 @@ package p8 { def main(args: Array[Double]) = () } } + +package t7448 { + object Main { + def main(args: Array[String]) = ??? + } +} diff --git a/test/files/neg/multiLineOps.check b/test/files/neg/multiLineOps.check index c9882d57e1c2..e3d865c984d4 100644 --- a/test/files/neg/multiLineOps.check +++ b/test/files/neg/multiLineOps.check @@ -1,5 +1,5 @@ multiLineOps.scala:6: warning: a pure expression does nothing in statement position; multiline expressions may require enclosing parentheses - +3 // error: Expected a toplevel definition + +3 // warning: a pure expression does nothing in statement position ^ error: No warnings can be incurred under -Werror. 1 warning diff --git a/test/files/neg/multiLineOps.scala b/test/files/neg/multiLineOps.scala index 792528620773..4a92fd9f2c0c 100644 --- a/test/files/neg/multiLineOps.scala +++ b/test/files/neg/multiLineOps.scala @@ -1,7 +1,7 @@ -// scalac: -Werror -Xsource:3 +// scalac: -Werror -Xlint -Xsource:3 class Test { val x = 1 + 2 - +3 // error: Expected a toplevel definition + +3 // warning: a pure expression does nothing in statement position } diff --git a/test/files/neg/open-infix-future.check b/test/files/neg/open-infix-future.check new file mode 100644 index 000000000000..15515fc2ef50 --- /dev/null +++ b/test/files/neg/open-infix-future.check @@ -0,0 +1,22 @@ +open-infix-future.scala:4: error: expected class or object definition +open trait A // error +^ +open-infix-future.scala:5: error: expected class or object definition +open object B // error +^ +open-infix-future.scala:8: error: ';' expected but 'val' found. + infix val a: Int = 1 // error + ^ +open-infix-future.scala:9: error: ';' expected but 'var' found. + infix var b: Int = 1 // error + ^ +open-infix-future.scala:11: error: ';' expected but 'type' found. + open type D // error + ^ +open-infix-future.scala:14: error: illegal start of statement + open class E // error + ^ +open-infix-future.scala:15: error: ';' expected but 'def' found. + open def bla(y: Int) = y // error + ^ +7 errors diff --git a/test/files/neg/open-infix-future.scala b/test/files/neg/open-infix-future.scala new file mode 100644 index 000000000000..2a250f3b006e --- /dev/null +++ b/test/files/neg/open-infix-future.scala @@ -0,0 +1,17 @@ +// scalac: -Xsource:3 +// + +open trait A // error +open object B // error + +class C { + infix val a: Int = 1 // error + infix var b: Int = 1 // error + + open type D // error + + def foo: Unit = { + open class E // error + open def bla(y: Int) = y // error + } +} diff --git a/test/files/neg/qmark-deprecated.check b/test/files/neg/qmark-deprecated.check new file mode 100644 index 000000000000..f1b7f333478a --- /dev/null +++ b/test/files/neg/qmark-deprecated.check @@ -0,0 +1,42 @@ +qmark-deprecated.scala:4: warning: using `?` as a type name will require backticks in the future. +class Foo[?] // error + ^ +qmark-deprecated.scala:6: warning: using `?` as a type name will require backticks in the future. +class Bar[M[?] <: List[?]] // errors + ^ +qmark-deprecated.scala:6: warning: `?` in a type will be interpreted as a wildcard in the future, wrap it in backticks to keep the current meaning. +class Bar[M[?] <: List[?]] // errors + ^ +qmark-deprecated.scala:10: warning: using `?` as a type name will require backticks in the future. + class ? { val x = 1 } // error + ^ +qmark-deprecated.scala:16: warning: using `?` as a type name will require backticks in the future. + trait ? // error + ^ +qmark-deprecated.scala:22: warning: using `?` as a type name will require backticks in the future. + type ? = Int // error + ^ +qmark-deprecated.scala:27: warning: `?` in a type will be interpreted as a wildcard in the future, wrap it in backticks to keep the current meaning. + val x: Array[?] = new Array[?](0) // errors + ^ +qmark-deprecated.scala:27: warning: `?` in a type will be interpreted as a wildcard in the future, wrap it in backticks to keep the current meaning. + val x: Array[?] = new Array[?](0) // errors + ^ +qmark-deprecated.scala:30: warning: `?` in a type will be interpreted as a wildcard in the future, wrap it in backticks to keep the current meaning. + def foo1[T <: Array[?]](x: T): Array[?] = x // errors + ^ +qmark-deprecated.scala:30: warning: `?` in a type will be interpreted as a wildcard in the future, wrap it in backticks to keep the current meaning. + def foo1[T <: Array[?]](x: T): Array[?] = x // errors + ^ +qmark-deprecated.scala:33: warning: using `?` as a type name will require backticks in the future. + def bar1[?] = {} // error + ^ +qmark-deprecated.scala:35: warning: using `?` as a type name will require backticks in the future. + def bar3[M[?]] = {} // error + ^ +qmark-deprecated.scala:38: warning: using `?` as a type name will require backticks in the future. + type A[?] = Int // error + ^ +error: No warnings can be incurred under -Werror. +13 warnings +1 error diff --git a/test/files/neg/qmark-deprecated.scala b/test/files/neg/qmark-deprecated.scala new file mode 100644 index 000000000000..c370cfcb2673 --- /dev/null +++ b/test/files/neg/qmark-deprecated.scala @@ -0,0 +1,40 @@ +// scalac: -deprecation -Xfatal-warnings +// + +class Foo[?] // error +class Foo2[`?`] // ok +class Bar[M[?] <: List[?]] // errors +class Bar2[M[`?`] <: List[`?`]] // ok + +object G { + class ? { val x = 1 } // error +} +object G2 { + class `?` { val x = 1 } // ok +} +object H { + trait ? // error +} +object H2 { + trait `?` // ok +} +object I { + type ? = Int // error +} +object I2 { + type `?` = Int // ok + + val x: Array[?] = new Array[?](0) // errors + val y: Array[`?`] = new Array[`?`](0) // ok + + def foo1[T <: Array[?]](x: T): Array[?] = x // errors + def foo2[T <: Array[`?`]](x: T): Array[`?`] = x // ok + + def bar1[?] = {} // error + def bar2[`?`] = {} // ok + def bar3[M[?]] = {} // error + def bar4[M[`?`]] = {} // error + + type A[?] = Int // error + type B[`?`] = Int // ok +} diff --git a/test/files/neg/stmt-expr-discard.check b/test/files/neg/stmt-expr-discard.check index 250de20f98d2..cc22eb1d843b 100644 --- a/test/files/neg/stmt-expr-discard.check +++ b/test/files/neg/stmt-expr-discard.check @@ -1,15 +1,3 @@ -stmt-expr-discard.scala:5: warning: Line starts with an operator that in future -will be taken as an infix expression continued from the previous line. -To force the previous interpretation as a separate statement, -add an explicit `;`, add an empty line, or remove spaces after the operator. - + 2 - ^ -stmt-expr-discard.scala:6: warning: Line starts with an operator that in future -will be taken as an infix expression continued from the previous line. -To force the previous interpretation as a separate statement, -add an explicit `;`, add an empty line, or remove spaces after the operator. - - 4 - ^ stmt-expr-discard.scala:5: warning: a pure expression does nothing in statement position; multiline expressions may require enclosing parentheses + 2 ^ @@ -17,5 +5,5 @@ stmt-expr-discard.scala:6: warning: a pure expression does nothing in statement - 4 ^ error: No warnings can be incurred under -Werror. -4 warnings +2 warnings 1 error diff --git a/test/files/neg/symbol-literal-removal.check b/test/files/neg/symbol-literal-removal.check deleted file mode 100644 index 839b635950fd..000000000000 --- a/test/files/neg/symbol-literal-removal.check +++ /dev/null @@ -1,4 +0,0 @@ -symbol-literal-removal.scala:4: error: symbol literal is unsupported; use Symbol("TestSymbol") instead - val foo = 'TestSymbol - ^ -1 error diff --git a/test/files/neg/symbol-literal-removal.scala b/test/files/neg/symbol-literal-removal.scala deleted file mode 100644 index 0d95ded21fd1..000000000000 --- a/test/files/neg/symbol-literal-removal.scala +++ /dev/null @@ -1,5 +0,0 @@ -// scalac: -Xsource:3 -// -abstract class Foo { - val foo = 'TestSymbol -} diff --git a/test/files/neg/t10729.check b/test/files/neg/t10729.check index 4942ca1bdfc2..a4143cb6b0e6 100644 --- a/test/files/neg/t10729.check +++ b/test/files/neg/t10729.check @@ -10,11 +10,11 @@ SeqAsAnnotation.scala:2: error: trait Seq is abstract; cannot be instantiated Switch.scala:4: error: class switch does not extend scala.annotation.Annotation def test(x: Int) = (x: @switch) match { ^ -TraitAnnotation.scala:6: error: trait TraitAnnotation is abstract; cannot be instantiated - 1: @TraitAnnotation - ^ Switch.scala:1: warning: imported `switch` is permanently hidden by definition of class switch import annotation.switch ^ +TraitAnnotation.scala:6: error: trait TraitAnnotation is abstract; cannot be instantiated + 1: @TraitAnnotation + ^ 1 warning 5 errors diff --git a/test/files/neg/t12071.check b/test/files/neg/t12071.check new file mode 100644 index 000000000000..88198baf3274 --- /dev/null +++ b/test/files/neg/t12071.check @@ -0,0 +1,41 @@ +t12071.scala:15: error: not found: value c c + `c c` i + ^ +t12071.scala:15: error: postfix operator i needs to be enabled +by making the implicit value scala.language.postfixOps visible. +This can be achieved by adding the import clause 'import scala.language.postfixOps' +or by setting the compiler option -language:postfixOps. +See the Scaladoc for value scala.language.postfixOps for a discussion +why the feature needs to be explicitly enabled. +Line starts with an operator that in future +will be taken as an infix expression continued from the previous line. +To force the previous interpretation as a separate statement, +add an explicit `;`, add an empty line, or remove spaces after the operator. + `c c` i + ^ +t12071.scala:20: warning: Line starts with an operator that in future +will be taken as an infix expression continued from the previous line. +To force the previous interpretation as a separate statement, +add an explicit `;`, add an empty line, or remove spaces after the operator. + + 2 + ^ +t12071.scala:25: warning: Line starts with an operator that in future +will be taken as an infix expression continued from the previous line. +To force the previous interpretation as a separate statement, +add an explicit `;`, add an empty line, or remove spaces after the operator. + + 1 + ^ +t12071.scala:28: warning: Line starts with an operator that in future +will be taken as an infix expression continued from the previous line. +To force the previous interpretation as a separate statement, +add an explicit `;`, add an empty line, or remove spaces after the operator. + `test-1` + `test-2` + ^ +t12071.scala:31: warning: Line starts with an operator that in future +will be taken as an infix expression continued from the previous line. +To force the previous interpretation as a separate statement, +add an explicit `;`, add an empty line, or remove spaces after the operator. + `compareTo` (2 - 1) + ^ +4 warnings +2 errors diff --git a/test/files/neg/t12071.scala b/test/files/neg/t12071.scala new file mode 100644 index 000000000000..f3f9529c147b --- /dev/null +++ b/test/files/neg/t12071.scala @@ -0,0 +1,51 @@ +// scalac: -Werror -Xlint -Xmigration:2.13 + +class C { + def `c c`(n: Int): Int = n + 1 +} + +// backticked operator is candidate for multiline infix, +// but backticked value is an innocent bystander. +// +class t12071 { + def c: C = ??? + def i: Int = 42 + def `n n`: Int = 17 + def f = c + `c c` i + def g = i + + `n n` + def basic = + 1 + + 2 +} + +object C { + def x = 42 + + 1 + + def y = 1 + + `test-1` + `test-2` + + def z = 2 + `compareTo` (2 - 1) + + def `test-1`: Int = 23 + def `test-2`: Int = 42 + def compareTo(x: Int) = println("lol") + + def yy = 1 + /* fails in scala 3 + + + `test-1` + + + `test-2` + */ +} + +object Test extends App { + println(C.x) + println(C.y) + println(C.z) + println(C.yy) +} diff --git a/test/files/neg/t12233.check b/test/files/neg/t12233.check new file mode 100644 index 000000000000..ffa267af2701 --- /dev/null +++ b/test/files/neg/t12233.check @@ -0,0 +1,7 @@ +t12233.scala:4: error: ambiguous implicit values: + both value hehe of type TypeClass[T] + and value evidence$2 of type TypeClass[T] + match expected type TypeClass[T] + def this(i: Int)(implicit hehe: TypeClass[T], j: Int) = this(i, j) + ^ +1 error diff --git a/test/files/neg/t12233.scala b/test/files/neg/t12233.scala new file mode 100644 index 000000000000..b2ad76732461 --- /dev/null +++ b/test/files/neg/t12233.scala @@ -0,0 +1,20 @@ + +trait TypeClass[T] +class Hehe[T: TypeClass](i: Int, j: Int) { + def this(i: Int)(implicit hehe: TypeClass[T], j: Int) = this(i, j) +} + +/* was +t12233.scala:4: error: too many arguments (found 3, expected 1) for constructor Hehe: (implicit evidence$1: TypeClass[T]): Hehe[T] + def this(i: Int)(implicit hehe: TypeClass[T], j: Int) = this(i, j) + ^ +1 error + * now +t12233.scala:4: error: ambiguous implicit values: + both value hehe of type TypeClass[T] + and value evidence$2 of type TypeClass[T] + match expected type TypeClass[T] + def this(i: Int)(implicit hehe: TypeClass[T], j: Int) = this(i, j) + ^ +1 error + */ diff --git a/test/files/neg/t12237.check b/test/files/neg/t12237.check new file mode 100644 index 000000000000..dbe091243e5e --- /dev/null +++ b/test/files/neg/t12237.check @@ -0,0 +1,10 @@ +t12237.scala:24: warning: Exhaustivity analysis reached max recursion depth, not all missing cases are reported. +(Please try with scalac -Ypatmat-exhaust-depth 40 or -Ypatmat-exhaust-depth off.) + (pq: PathAndQuery) match { + ^ +t12237.scala:24: warning: match may not be exhaustive. + (pq: PathAndQuery) match { + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/t12237.scala b/test/files/neg/t12237.scala new file mode 100644 index 000000000000..480634cf86f6 --- /dev/null +++ b/test/files/neg/t12237.scala @@ -0,0 +1,30 @@ +// scalac: -Werror +sealed trait PathAndQuery +sealed trait Path extends PathAndQuery +sealed trait Query extends PathAndQuery + +object PathAndQuery { + case object Root extends Path + case class /(prev: Path, value: String) extends Path + + case class ===(k: String, v: String) extends Query + case class :&(prev: Query, next: (===)) extends Query + case class +?(path: Path, next: (===)) extends Query +} + +object Main { + def main(args: Array[String]): Unit = { + import PathAndQuery._ + + val path = /(/(Root, "page"), "1") + val q1 = ===("k1", "v1") + val q2 = ===("k2", "v2") + val pq = :&(+?(path, q1), q2) + + (pq: PathAndQuery) match { + case Root / "page" / "1" => println("match 1") + case Root / "page" / "1" +? ("k1" === "v1") => println("match 2") + case Root / "page" / "1" +? ("k1" === "v1") :& ("k2" === "v2") => println("match 3") + } + } +} diff --git a/test/files/neg/t12347.check b/test/files/neg/t12347.check new file mode 100644 index 000000000000..0476089c1c4e --- /dev/null +++ b/test/files/neg/t12347.check @@ -0,0 +1,10 @@ +t12347.scala:14: error: unknown parameter name: x + X.f(n = count, x = text) + ^ +t12347.scala:15: error: overloaded method f with alternatives: + (s: String)String + (n: Int,s: String)String + [which have no such parameter x] cannot be applied to (n: Int, x: String) + Y.f(n = count, x = text) + ^ +2 errors diff --git a/test/files/neg/t12347.scala b/test/files/neg/t12347.scala new file mode 100644 index 000000000000..1795ecfc8320 --- /dev/null +++ b/test/files/neg/t12347.scala @@ -0,0 +1,16 @@ + +object X { + def f(n: Int, s: String) = s * n +} + +object Y { + def f(n: Int, s: String) = s * n + def f(s: String) = s * 3 +} + +object Test extends App { + def count = 2 + def text = "hi" + X.f(n = count, x = text) + Y.f(n = count, x = text) +} diff --git a/test/files/neg/t12349.check b/test/files/neg/t12349.check new file mode 100644 index 000000000000..ed6d1b26451d --- /dev/null +++ b/test/files/neg/t12349.check @@ -0,0 +1,248 @@ +t12349b.scala:7: error: weaker access privileges in overriding +def a2(): Unit (defined in class t12349a) + override should be public + protected override def a2(): Unit = println("Inner12349b#a2()") // weaker access privileges + ^ +t12349b.scala:8: error: weaker access privileges in overriding +def a3(): Unit (defined in class t12349a) + override should not be private + private override def a3(): Unit = println("Inner12349b#a3()") // weaker access privileges + ^ +t12349b.scala:9: error: weaker access privileges in overriding +def a4(): Unit (defined in class t12349a) + override should be public + protected[t12349b] override def a4(): Unit = println("Inner12349b#a4()") // weaker access privileges + ^ +t12349b.scala:10: error: weaker access privileges in overriding +def a5(): Unit (defined in class t12349a) + override should be public + private[t12349b] override def a5(): Unit = println("Inner12349b#a5()") // weaker access privileges + ^ +t12349b.scala:11: error: weaker access privileges in overriding +def a6(): Unit (defined in class t12349a) + override should be public + protected[t12349] override def a6(): Unit = println("Inner12349b#a6()") // weaker access privileges + ^ +t12349b.scala:12: error: weaker access privileges in overriding +def a7(): Unit (defined in class t12349a) + override should be public + private[t12349] override def a7(): Unit = println("Inner12349b#a7()") // weaker access privileges + ^ +t12349b.scala:13: error: weaker access privileges in overriding +def a8(): Unit (defined in class t12349a) + override should be public + protected[this] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges + ^ +t12349b.scala:14: error: weaker access privileges in overriding +def a9(): Unit (defined in class t12349a) + override should not be private + private[this] override def a9(): Unit = println("Inner12349b#a9()") // weaker access privileges + ^ +t12349b.scala:18: error: weaker access privileges in overriding +protected[package t12349] def b3(): Unit (defined in class t12349a) + override should not be private + private override def b3(): Unit = println("Inner12349b#b3()") // weaker access privileges + ^ +t12349b.scala:20: error: weaker access privileges in overriding +protected[package t12349] def b5(): Unit (defined in class t12349a) + override should at least be protected[t12349] + private[t12349b] override def b5(): Unit = println("Inner12349b#b5()") // weaker access privileges + ^ +t12349b.scala:22: error: weaker access privileges in overriding +protected[package t12349] def b7(): Unit (defined in class t12349a) + override should at least be protected[t12349] + private[t12349] override def b7(): Unit = println("Inner12349b#b7()") // weaker access privileges + ^ +t12349b.scala:24: error: weaker access privileges in overriding +protected[package t12349] def b9(): Unit (defined in class t12349a) + override should not be private + private[this] override def b9(): Unit = println("Inner12349b#b9()") // weaker access privileges + ^ +t12349b.scala:27: error: weaker access privileges in overriding +private[package t12349] def c2(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected override def c2(): Unit = println("Inner12349b#c2()") // weaker access privileges + ^ +t12349b.scala:28: error: weaker access privileges in overriding +private[package t12349] def c3(): Unit (defined in class t12349a) + override should not be private + private override def c3(): Unit = println("Inner12349b#c3()") // weaker access privileges + ^ +t12349b.scala:29: error: weaker access privileges in overriding +private[package t12349] def c4(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[t12349b] override def c4(): Unit = println("Inner12349b#c4()") // weaker access privileges + ^ +t12349b.scala:30: error: weaker access privileges in overriding +private[package t12349] def c5(): Unit (defined in class t12349a) + override should at least be private[t12349] + private[t12349b] override def c5(): Unit = println("Inner12349b#c5()") // weaker access privileges + ^ +t12349b.scala:33: error: weaker access privileges in overriding +private[package t12349] def c8(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[this] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges + ^ +t12349b.scala:34: error: weaker access privileges in overriding +private[package t12349] def c9(): Unit (defined in class t12349a) + override should not be private + private[this] override def c9(): Unit = println("Inner12349b#c9()") // weaker access privileges + ^ +t12349b.scala:36: error: method d1 overrides nothing + override def d1(): Unit = println("Inner12349b#d1()") // overrides nothing + ^ +t12349b.scala:37: error: method d2 overrides nothing + protected override def d2(): Unit = println("Inner12349b#d2()") // overrides nothing + ^ +t12349b.scala:38: error: method d3 overrides nothing + private override def d3(): Unit = println("Inner12349b#d3()") // overrides nothing + ^ +t12349b.scala:39: error: method d4 overrides nothing + protected[t12349b] override def d4(): Unit = println("Inner12349b#d4()") // overrides nothing + ^ +t12349b.scala:40: error: method d5 overrides nothing + private[t12349b] override def d5(): Unit = println("Inner12349b#d5()") // overrides nothing + ^ +t12349b.scala:41: error: method d6 overrides nothing + protected[t12349] override def d6(): Unit = println("Inner12349b#d6()") // overrides nothing + ^ +t12349b.scala:42: error: method d7 overrides nothing + private[t12349] override def d7(): Unit = println("Inner12349b#d7()") // overrides nothing + ^ +t12349b.scala:43: error: method d8 overrides nothing + protected[this] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing + ^ +t12349b.scala:44: error: method d9 overrides nothing + private[this] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing + ^ +t12349c.scala:11: error: weaker access privileges in overriding +def a2(): Unit (defined in class t12349a) + override should be public + protected override def a2(): Unit = println("Inner12349c#a2()") // weaker access privileges + ^ +t12349c.scala:12: error: weaker access privileges in overriding +def a3(): Unit (defined in class t12349a) + override should not be private + private override def a3(): Unit = println("Inner12349c#a3()") // weaker access privileges + ^ +t12349c.scala:13: error: weaker access privileges in overriding +def a4(): Unit (defined in class t12349a) + override should be public + protected[t12349c] override def a4(): Unit = println("Inner12349c#a4()") // weaker access privileges + ^ +t12349c.scala:14: error: weaker access privileges in overriding +def a5(): Unit (defined in class t12349a) + override should be public + private[t12349c] override def a5(): Unit = println("Inner12349c#a5()") // weaker access privileges + ^ +t12349c.scala:15: error: weaker access privileges in overriding +def a6(): Unit (defined in class t12349a) + override should be public + protected[pkg] override def a6(): Unit = println("Inner12349c#a6()") // weaker access privileges + ^ +t12349c.scala:16: error: weaker access privileges in overriding +def a7(): Unit (defined in class t12349a) + override should be public + private[pkg] override def a7(): Unit = println("Inner12349c#a7()") // weaker access privileges + ^ +t12349c.scala:17: error: weaker access privileges in overriding +def a8(): Unit (defined in class t12349a) + override should be public + protected[this] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges + ^ +t12349c.scala:18: error: weaker access privileges in overriding +def a9(): Unit (defined in class t12349a) + override should not be private + private[this] override def a9(): Unit = println("Inner12349c#a9()") // weaker access privileges + ^ +t12349c.scala:22: error: weaker access privileges in overriding +protected[package t12349] def b3(): Unit (defined in class t12349a) + override should not be private + private override def b3(): Unit = println("Inner12349c#b3()") // weaker access privileges + ^ +t12349c.scala:24: error: weaker access privileges in overriding +protected[package t12349] def b5(): Unit (defined in class t12349a) + override should at least be protected[t12349] + private[t12349c] override def b5(): Unit = println("Inner12349c#b5()") // weaker access privileges + ^ +t12349c.scala:26: error: weaker access privileges in overriding +protected[package t12349] def b7(): Unit (defined in class t12349a) + override should at least be protected[t12349] + private[pkg] override def b7(): Unit = println("Inner12349c#b7()") // weaker access privileges + ^ +t12349c.scala:28: error: weaker access privileges in overriding +protected[package t12349] def b9(): Unit (defined in class t12349a) + override should not be private + private[this] override def b9(): Unit = println("Inner12349c#b9()") // weaker access privileges + ^ +t12349c.scala:31: error: weaker access privileges in overriding +private[package t12349] def c2(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected override def c2(): Unit = println("Inner12349c#c2()") // weaker access privileges + ^ +t12349c.scala:32: error: weaker access privileges in overriding +private[package t12349] def c3(): Unit (defined in class t12349a) + override should not be private + private override def c3(): Unit = println("Inner12349c#c3()") // weaker access privileges + ^ +t12349c.scala:33: error: weaker access privileges in overriding +private[package t12349] def c4(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[t12349c] override def c4(): Unit = println("Inner12349c#c4()") // weaker access privileges + ^ +t12349c.scala:34: error: weaker access privileges in overriding +private[package t12349] def c5(): Unit (defined in class t12349a) + override should at least be private[t12349] + private[t12349c] override def c5(): Unit = println("Inner12349c#c5()") // weaker access privileges + ^ +t12349c.scala:35: error: weaker access privileges in overriding +private[package t12349] def c6(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[pkg] override def c6(): Unit = println("Inner12349c#c6()") // weaker access privileges + ^ +t12349c.scala:36: error: weaker access privileges in overriding +private[package t12349] def c7(): Unit (defined in class t12349a) + override should at least be private[t12349] + private[pkg] override def c7(): Unit = println("Inner12349c#c7()") // weaker access privileges + ^ +t12349c.scala:37: error: weaker access privileges in overriding +private[package t12349] def c8(): Unit (defined in class t12349a) + override should at least be private[t12349] + protected[this] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges + ^ +t12349c.scala:38: error: weaker access privileges in overriding +private[package t12349] def c9(): Unit (defined in class t12349a) + override should not be private + private[this] override def c9(): Unit = println("Inner12349c#c9()") // overrides nothing (invisible) + ^ +t12349c.scala:30: error: method c1 overrides nothing + override def c1(): Unit = println("Inner12349c#c1()") // overrides nothing (invisible) + ^ +t12349c.scala:40: error: method d1 overrides nothing + override def d1(): Unit = println("Inner12349c#d1()") // overrides nothing + ^ +t12349c.scala:41: error: method d2 overrides nothing + protected override def d2(): Unit = println("Inner12349c#d2()") // overrides nothing + ^ +t12349c.scala:42: error: method d3 overrides nothing + private override def d3(): Unit = println("Inner12349c#d3()") // overrides nothing + ^ +t12349c.scala:43: error: method d4 overrides nothing + protected[t12349c] override def d4(): Unit = println("Inner12349c#d4()") // overrides nothing + ^ +t12349c.scala:44: error: method d5 overrides nothing + private[t12349c] override def d5(): Unit = println("Inner12349c#d5()") // overrides nothing + ^ +t12349c.scala:45: error: method d6 overrides nothing + protected[pkg] override def d6(): Unit = println("Inner12349c#d6()") // overrides nothing + ^ +t12349c.scala:46: error: method d7 overrides nothing + private[pkg] override def d7(): Unit = println("Inner12349c#d7()") // overrides nothing + ^ +t12349c.scala:47: error: method d8 overrides nothing + protected[this] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing + ^ +t12349c.scala:48: error: method d9 overrides nothing + private[this] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing + ^ +57 errors diff --git a/test/files/neg/t12349/t12349a.java b/test/files/neg/t12349/t12349a.java new file mode 100644 index 000000000000..db9de0b0a539 --- /dev/null +++ b/test/files/neg/t12349/t12349a.java @@ -0,0 +1,45 @@ +package t12349; + +public class t12349a { + + public void a1() { System.out.println("t12349a#a1()"); } + public void a2() { System.out.println("t12349a#a2()"); } + public void a3() { System.out.println("t12349a#a3()"); } + public void a4() { System.out.println("t12349a#a4()"); } + public void a5() { System.out.println("t12349a#a5()"); } + public void a6() { System.out.println("t12349a#a6()"); } + public void a7() { System.out.println("t12349a#a7()"); } + public void a8() { System.out.println("t12349a#a8()"); } + public void a9() { System.out.println("t12349a#a9()"); } + + protected void b1() { System.out.println("t12349a#b1()"); } + protected void b2() { System.out.println("t12349a#b2()"); } + protected void b3() { System.out.println("t12349a#b3()"); } + protected void b4() { System.out.println("t12349a#b4()"); } + protected void b5() { System.out.println("t12349a#b5()"); } + protected void b6() { System.out.println("t12349a#b6()"); } + protected void b7() { System.out.println("t12349a#b7()"); } + protected void b8() { System.out.println("t12349a#b8()"); } + protected void b9() { System.out.println("t12349a#b9()"); } + + void c1() { System.out.println("t12349a#c1()"); } + void c2() { System.out.println("t12349a#c2()"); } + void c3() { System.out.println("t12349a#c3()"); } + void c4() { System.out.println("t12349a#c4()"); } + void c5() { System.out.println("t12349a#c5()"); } + void c6() { System.out.println("t12349a#c6()"); } + void c7() { System.out.println("t12349a#c7()"); } + void c8() { System.out.println("t12349a#c8()"); } + void c9() { System.out.println("t12349a#c9()"); } + + private void d1() { System.out.println("t12349a#d1()"); } + private void d2() { System.out.println("t12349a#d2()"); } + private void d3() { System.out.println("t12349a#d3()"); } + private void d4() { System.out.println("t12349a#d4()"); } + private void d5() { System.out.println("t12349a#d5()"); } + private void d6() { System.out.println("t12349a#d6()"); } + private void d7() { System.out.println("t12349a#d7()"); } + private void d8() { System.out.println("t12349a#d8()"); } + private void d9() { System.out.println("t12349a#d9()"); } + +} diff --git a/test/files/neg/t12349/t12349b.scala b/test/files/neg/t12349/t12349b.scala new file mode 100644 index 000000000000..38b3309779b3 --- /dev/null +++ b/test/files/neg/t12349/t12349b.scala @@ -0,0 +1,47 @@ +package t12349 + +object t12349b { + + class Inner12349b extends t12349a { + override def a1(): Unit = println("Inner12349b#a1()") + protected override def a2(): Unit = println("Inner12349b#a2()") // weaker access privileges + private override def a3(): Unit = println("Inner12349b#a3()") // weaker access privileges + protected[t12349b] override def a4(): Unit = println("Inner12349b#a4()") // weaker access privileges + private[t12349b] override def a5(): Unit = println("Inner12349b#a5()") // weaker access privileges + protected[t12349] override def a6(): Unit = println("Inner12349b#a6()") // weaker access privileges + private[t12349] override def a7(): Unit = println("Inner12349b#a7()") // weaker access privileges + protected[this] override def a8(): Unit = println("Inner12349b#a8()") // weaker access privileges + private[this] override def a9(): Unit = println("Inner12349b#a9()") // weaker access privileges + + override def b1(): Unit = println("Inner12349b#b1()") + protected override def b2(): Unit = println("Inner12349b#b2()") + private override def b3(): Unit = println("Inner12349b#b3()") // weaker access privileges + protected[t12349b] override def b4(): Unit = println("Inner12349b#b4()") + private[t12349b] override def b5(): Unit = println("Inner12349b#b5()") // weaker access privileges + protected[t12349] override def b6(): Unit = println("Inner12349b#b6()") + private[t12349] override def b7(): Unit = println("Inner12349b#b7()") // weaker access privileges + protected[this] override def b8(): Unit = println("Inner12349b#b8()") // [#12349] - not fixed by PR #9525 + private[this] override def b9(): Unit = println("Inner12349b#b9()") // weaker access privileges + + override def c1(): Unit = println("Inner12349b#c1()") + protected override def c2(): Unit = println("Inner12349b#c2()") // weaker access privileges + private override def c3(): Unit = println("Inner12349b#c3()") // weaker access privileges + protected[t12349b] override def c4(): Unit = println("Inner12349b#c4()") // weaker access privileges + private[t12349b] override def c5(): Unit = println("Inner12349b#c5()") // weaker access privileges + protected[t12349] override def c6(): Unit = println("Inner12349b#c6()") + private[t12349] override def c7(): Unit = println("Inner12349b#c7()") + protected[this] override def c8(): Unit = println("Inner12349b#c8()") // weaker access privileges + private[this] override def c9(): Unit = println("Inner12349b#c9()") // weaker access privileges + + override def d1(): Unit = println("Inner12349b#d1()") // overrides nothing + protected override def d2(): Unit = println("Inner12349b#d2()") // overrides nothing + private override def d3(): Unit = println("Inner12349b#d3()") // overrides nothing + protected[t12349b] override def d4(): Unit = println("Inner12349b#d4()") // overrides nothing + private[t12349b] override def d5(): Unit = println("Inner12349b#d5()") // overrides nothing + protected[t12349] override def d6(): Unit = println("Inner12349b#d6()") // overrides nothing + private[t12349] override def d7(): Unit = println("Inner12349b#d7()") // overrides nothing + protected[this] override def d8(): Unit = println("Inner12349b#d8()") // overrides nothing + private[this] override def d9(): Unit = println("Inner12349b#d9()") // overrides nothing + } + +} diff --git a/test/files/neg/t12349/t12349c.scala b/test/files/neg/t12349/t12349c.scala new file mode 100644 index 000000000000..942991a22430 --- /dev/null +++ b/test/files/neg/t12349/t12349c.scala @@ -0,0 +1,53 @@ +package t12349 + +import t12349.t12349a + +package pkg { + + object t12349c { + + class Inner12349c extends t12349a { + override def a1(): Unit = println("Inner12349c#a1()") + protected override def a2(): Unit = println("Inner12349c#a2()") // weaker access privileges + private override def a3(): Unit = println("Inner12349c#a3()") // weaker access privileges + protected[t12349c] override def a4(): Unit = println("Inner12349c#a4()") // weaker access privileges + private[t12349c] override def a5(): Unit = println("Inner12349c#a5()") // weaker access privileges + protected[pkg] override def a6(): Unit = println("Inner12349c#a6()") // weaker access privileges + private[pkg] override def a7(): Unit = println("Inner12349c#a7()") // weaker access privileges + protected[this] override def a8(): Unit = println("Inner12349c#a8()") // weaker access privileges + private[this] override def a9(): Unit = println("Inner12349c#a9()") // weaker access privileges + + override def b1(): Unit = println("Inner12349c#b1()") + protected override def b2(): Unit = println("Inner12349c#b2()") + private override def b3(): Unit = println("Inner12349c#b3()") // weaker access privileges + protected[t12349c] override def b4(): Unit = println("Inner12349c#b4()") + private[t12349c] override def b5(): Unit = println("Inner12349c#b5()") // weaker access privileges + protected[pkg] override def b6(): Unit = println("Inner12349c#b6()") + private[pkg] override def b7(): Unit = println("Inner12349c#b7()") // weaker access privileges + protected[this] override def b8(): Unit = println("Inner12349c#b8()") // [#12349] - not fixed by PR #9525 + private[this] override def b9(): Unit = println("Inner12349c#b9()") // weaker access privileges + + override def c1(): Unit = println("Inner12349c#c1()") // overrides nothing (invisible) + protected override def c2(): Unit = println("Inner12349c#c2()") // weaker access privileges + private override def c3(): Unit = println("Inner12349c#c3()") // weaker access privileges + protected[t12349c] override def c4(): Unit = println("Inner12349c#c4()") // weaker access privileges + private[t12349c] override def c5(): Unit = println("Inner12349c#c5()") // weaker access privileges + protected[pkg] override def c6(): Unit = println("Inner12349c#c6()") // weaker access privileges + private[pkg] override def c7(): Unit = println("Inner12349c#c7()") // weaker access privileges + protected[this] override def c8(): Unit = println("Inner12349c#c8()") // weaker access privileges + private[this] override def c9(): Unit = println("Inner12349c#c9()") // overrides nothing (invisible) + + override def d1(): Unit = println("Inner12349c#d1()") // overrides nothing + protected override def d2(): Unit = println("Inner12349c#d2()") // overrides nothing + private override def d3(): Unit = println("Inner12349c#d3()") // overrides nothing + protected[t12349c] override def d4(): Unit = println("Inner12349c#d4()") // overrides nothing + private[t12349c] override def d5(): Unit = println("Inner12349c#d5()") // overrides nothing + protected[pkg] override def d6(): Unit = println("Inner12349c#d6()") // overrides nothing + private[pkg] override def d7(): Unit = println("Inner12349c#d7()") // overrides nothing + protected[this] override def d8(): Unit = println("Inner12349c#d8()") // overrides nothing + private[this] override def d9(): Unit = println("Inner12349c#d9()") // overrides nothing + } + + } + +} diff --git a/test/files/neg/t12380.check b/test/files/neg/t12380.check new file mode 100644 index 000000000000..4b9f7ae63a68 --- /dev/null +++ b/test/files/neg/t12380.check @@ -0,0 +1,8 @@ +Test.scala:1: error: incompatible type in overriding +def m(): String (defined in trait I) + with def m(): Object (defined in class C); + found : (): Object + required: (): String +object Test extends p.J.C with p.J.I { + ^ +1 error diff --git a/test/files/neg/t12380/J.java b/test/files/neg/t12380/J.java new file mode 100644 index 000000000000..280cea1286b1 --- /dev/null +++ b/test/files/neg/t12380/J.java @@ -0,0 +1,14 @@ +package p; + +public class J { + public static class C { + public Object m() { return new Object(); } + } + public interface I { + public String m(); + } + + public static class Test extends C implements I { + @Override public String m() { return ""; } + } +} diff --git a/test/files/neg/t12380/Test.scala b/test/files/neg/t12380/Test.scala new file mode 100644 index 000000000000..976b42ffdb93 --- /dev/null +++ b/test/files/neg/t12380/Test.scala @@ -0,0 +1,5 @@ +object Test extends p.J.C with p.J.I { + def main(args: Array[String]): Unit = { + println((this: p.J.I).m.trim) + } +} diff --git a/test/files/neg/t12394.check b/test/files/neg/t12394.check new file mode 100644 index 000000000000..7dbf4d49d9e5 --- /dev/null +++ b/test/files/neg/t12394.check @@ -0,0 +1,11 @@ +Test.scala:2: error: cannot override final member: +final def m(): Int (defined in class C) + with def m(): Int (defined in trait J) +class S2 extends p.A.C with p.A.J + ^ +Test.scala:4: error: cannot override final member: +final def m(): Int (defined in class C) + with def m(): Int (defined in trait J) +class S3 extends p.A.C with K + ^ +2 errors diff --git a/test/files/neg/t12394/A.java b/test/files/neg/t12394/A.java new file mode 100644 index 000000000000..cf3188018d93 --- /dev/null +++ b/test/files/neg/t12394/A.java @@ -0,0 +1,17 @@ +package p; + +public class A { + public static interface I { + default int m() { return 1; } + } + + public static interface J extends I { + @Override default int m() { return 2; } + } + + public static class C implements I { + @Override public final int m() { return 3; } + } + + public static class D extends C implements J { } +} diff --git a/test/files/neg/t12394/Test.scala b/test/files/neg/t12394/Test.scala new file mode 100644 index 000000000000..8a272c5127cd --- /dev/null +++ b/test/files/neg/t12394/Test.scala @@ -0,0 +1,4 @@ +class S1 extends p.A.D +class S2 extends p.A.C with p.A.J +trait K extends p.A.J +class S3 extends p.A.C with K diff --git a/test/files/neg/t2488.check b/test/files/neg/t2488.check index f69ca0a939dc..03b6838519d1 100644 --- a/test/files/neg/t2488.check +++ b/test/files/neg/t2488.check @@ -7,19 +7,19 @@ t2488.scala:7: error: overloaded method f with alternatives: t2488.scala:8: error: overloaded method f with alternatives: ()Int (a: Int,b: Int)Int - cannot be applied to (a: Int, c: Int) + [which have no such parameter c] cannot be applied to (a: Int, c: Int) println(c.f(a = 2, c = 2)) ^ t2488.scala:9: error: overloaded method f with alternatives: ()Int (a: Int,b: Int)Int - cannot be applied to (Int, c: Int) + [which have no such parameter c] cannot be applied to (Int, c: Int) println(c.f(2, c = 2)) ^ t2488.scala:10: error: overloaded method f with alternatives: ()Int (a: Int,b: Int)Int - cannot be applied to (c: Int, Int) + [which have no such parameter c] cannot be applied to (c: Int, Int) println(c.f(c = 2, 2)) ^ t2488.scala:11: error: overloaded method f with alternatives: diff --git a/test/files/neg/t2509-3.scala b/test/files/neg/t2509-3.scala index c141066a94ad..619be4e439b2 100644 --- a/test/files/neg/t2509-3.scala +++ b/test/files/neg/t2509-3.scala @@ -17,7 +17,7 @@ object ZA extends Z[A] { } object XB extends X[B] { - def y(b: B) = new Y { def value = s"S{b.getClass}: BValue" } + def y(b: B) = new Y { def value = s"${b.getClass}: BValue" } } object Test { diff --git a/test/files/neg/t4749.check b/test/files/neg/t4749.check index 2799d8ddc0b7..ee5967c2cd0c 100644 --- a/test/files/neg/t4749.check +++ b/test/files/neg/t4749.check @@ -1,5 +1,5 @@ t4749.scala:5: warning: not a valid main method for bippy.Fail1, - because main methods must have the exact signature (Array[String])Unit. + because main methods must have the exact signature `(Array[String]): Unit`, though Scala runners will forgive a non-Unit result. To define an entry point, please define the main method as: def main(args: Array[String]): Unit @@ -38,7 +38,7 @@ t4749.scala:28: warning: Fail6 has a valid main method (args: Array[String]): Un object Fail6 { ^ t4749.scala:44: warning: not a valid main method for bippy.Win3, - because main methods must have the exact signature (Array[String])Unit. + because main methods must have the exact signature `(Array[String]): Unit`, though Scala runners will forgive a non-Unit result. To define an entry point, please define the main method as: def main(args: Array[String]): Unit diff --git a/test/files/neg/t4762.check b/test/files/neg/t4762.check index bd1c9ebff690..aa7bdcec39eb 100644 --- a/test/files/neg/t4762.check +++ b/test/files/neg/t4762.check @@ -4,6 +4,10 @@ t4762.scala:17: warning: private[this] value x in class B shadows mutable x inhe t4762.scala:50: warning: private[this] value x in class Derived shadows mutable x inherited from class Base. Changes to x will not be visible within class Derived - you may want to give them distinct names. class Derived( x : Int ) extends Base( x ) { override def toString = x.toString } ^ -error: No warnings can be incurred under -Werror. +t4762.scala:13: error: weaker access privileges in overriding +val y: Int (defined in class A) + override should not be private + private[this] def y: Int = 99 + ^ 2 warnings 1 error diff --git a/test/files/neg/t5856.check b/test/files/neg/t5856.check index 8b968f173f9c..3d035a87e15c 100644 --- a/test/files/neg/t5856.check +++ b/test/files/neg/t5856.check @@ -1,9 +1,6 @@ -t5856.scala:10: error: invalid string interpolation $", expected: $$, $identifier or ${expression} - val s9 = s"$" - ^ t5856.scala:10: error: unclosed string literal val s9 = s"$" - ^ + ^ t5856.scala:2: error: error in interpolated string: identifier or block expected val s1 = s"$null" ^ @@ -28,4 +25,4 @@ t5856.scala:8: error: error in interpolated string: identifier or block expected t5856.scala:9: error: error in interpolated string: identifier or block expected val s8 = s"$super" ^ -10 errors +9 errors diff --git a/test/files/neg/t5887.check b/test/files/neg/t5887.check index aec5beed78a2..21bedc99d721 100644 --- a/test/files/neg/t5887.check +++ b/test/files/neg/t5887.check @@ -1,13 +1,29 @@ +t5887.scala:6: error: type mismatch; + found : Int(22) + required: Throwable => ? + def f = try ??? catch 22 + ^ t5887.scala:10: error: missing parameter type for expanded function The argument types of an anonymous function must be fully known. (SLS 8.5) Expected type was: ? def h = List("x") map (s => try { case _ => 7 }) ^ +t5887.scala:29: error: type mismatch; + found : TheOldCollegeTry.this.catcher.type + required: Throwable => Int + def noLongerAllower: Int = try 42 catch catcher + ^ t5887.scala:8: warning: A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled. def g = try 42 ^ t5887.scala:10: warning: A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled. def h = List("x") map (s => try { case _ => 7 }) ^ -2 warnings -1 error +t5887.scala:12: warning: This catches all Throwables. If this is really intended, use `case _ : Throwable` to clear this warning. + def j = try ??? catch (_ => 42) + ^ +t5887.scala:18: warning: This catches all Throwables. If this is really intended, use `case _ : Throwable` to clear this warning. + def k2 = try 27 catch recover + ^ +4 warnings +3 errors diff --git a/test/files/neg/t5887.scala b/test/files/neg/t5887.scala index d9493adc2e80..e8dc51f91af8 100644 --- a/test/files/neg/t5887.scala +++ b/test/files/neg/t5887.scala @@ -2,10 +2,29 @@ trait TheOldCollegeTry { // was: value isDefinedAt is not a member of Int - // now: required: PartialFunction[Throwable,?] - //def f = try ??? catch 22 + // now: required: Function[Throwable,?] + def f = try ??? catch 22 def g = try 42 def h = List("x") map (s => try { case _ => 7 }) + + def j = try ??? catch (_ => 42) + + import PartialFunction.fromFunction + + def recover(t: Throwable): Int = 42 + def k = try 27 catch fromFunction(recover) + def k2 = try 27 catch recover + + def parseErrorHandler[T]: PartialFunction[Throwable, T] = ??? + def pushBusy[T](body: => T): T = + try body + catch parseErrorHandler + + object catcher { + def isDefinedAt(x: Any) = true + def apply(x: Any) = 27 + } + def noLongerAllower: Int = try 42 catch catcher } diff --git a/test/files/neg/t6323a.check b/test/files/neg/t6323a.check index 83966449e7aa..d8622cd22e1c 100644 --- a/test/files/neg/t6323a.check +++ b/test/files/neg/t6323a.check @@ -1,15 +1,7 @@ -t6323a.scala:12: materializing requested scala.reflect.type.ClassTag[Test] using scala.reflect.`package`.materializeClassTag[Test]() - val lookAtMe = m.reflect(Test("a",List(5))) - ^ -t6323a.scala:13: materializing requested reflect.runtime.universe.type.TypeTag[Test] using scala.reflect.api.`package`.materializeTypeTag[Test](scala.reflect.runtime.`package`.universe) - val value = u.typeOf[Test] - ^ -t6323a.scala:13: scala.reflect.api.`package`.materializeTypeTag[Test](scala.reflect.runtime.`package`.universe) is not a valid implicit value for reflect.runtime.universe.TypeTag[Test] because: -failed to typecheck the materialized tag: -cannot create a TypeTag referring to class Test.Test local to the reifee: use WeakTypeTag instead - val value = u.typeOf[Test] - ^ -t6323a.scala:13: error: No TypeTag available for Test +t6323a.scala:13: error: implicit error; +!I ttag: TypeTag[Test] + No TypeTag available for Test + val value = u.typeOf[Test] ^ 1 error diff --git a/test/files/neg/t6323a.scala b/test/files/neg/t6323a.scala index 34305c69028b..182c31c609a1 100644 --- a/test/files/neg/t6323a.scala +++ b/test/files/neg/t6323a.scala @@ -1,4 +1,4 @@ -// scalac: -Xlog-implicits +// scalac: -Vimplicits // import scala.reflect.runtime.universe._ import scala.reflect.runtime.{currentMirror => m} diff --git a/test/files/neg/t6476.check b/test/files/neg/t6476.check new file mode 100644 index 000000000000..bf0c65efc6b8 --- /dev/null +++ b/test/files/neg/t6476.check @@ -0,0 +1,4 @@ +t6476.scala:8: error: unclosed string literal; note that `\"` no longer closes single-quoted interpolated string literals since 2.13.6, you can use a triple-quoted string instead + mimi"\" + ^ +1 error diff --git a/test/files/neg/t6476.scala b/test/files/neg/t6476.scala new file mode 100644 index 000000000000..9b88e43593cb --- /dev/null +++ b/test/files/neg/t6476.scala @@ -0,0 +1,9 @@ +// only the last one doesn't parse +class C { + mimi"""\ """ + mimi"""\\""" + mimi"""\""" + mimi"\ " + mimi"\\" + mimi"\" +} diff --git a/test/files/neg/t6476b.check b/test/files/neg/t6476b.check new file mode 100644 index 000000000000..e6aa3e441214 --- /dev/null +++ b/test/files/neg/t6476b.check @@ -0,0 +1,7 @@ +t6476b.scala:2: error: invalid escape at terminal index 0 in "\". Use \\ for literal \. + val sa = s"""\""" + ^ +t6476b.scala:4: error: invalid escape '\ ' not one of [\b, \t, \n, \f, \r, \\, \", \', \uxxxx] at index 0 in "\ ". Use \\ for literal \. + val sc = s"""\ """ + ^ +2 errors diff --git a/test/files/neg/t6476b.scala b/test/files/neg/t6476b.scala new file mode 100644 index 000000000000..d601091972ce --- /dev/null +++ b/test/files/neg/t6476b.scala @@ -0,0 +1,8 @@ +class C { + val sa = s"""\""" + val sb = s"""\\""" + val sc = s"""\ """ + val ra = raw"""\""" + val rb = raw"""\\""" + val rc = raw"""\ """ +} diff --git a/test/files/neg/t7052.check b/test/files/neg/t7052.check new file mode 100644 index 000000000000..6816f79bde81 --- /dev/null +++ b/test/files/neg/t7052.check @@ -0,0 +1,7 @@ +t7052.scala:9: error: name clash between defined and inherited member: +def apply(xs: Int*): Int in class A and +def apply(xs: Seq[Int]): Int at line 9 +have same type after erasure: (xs: Seq): Int + def apply(xs: Seq[Int]) = 27 + ^ +1 error diff --git a/test/files/neg/t7052.scala b/test/files/neg/t7052.scala new file mode 100644 index 000000000000..0cfad0dce678 --- /dev/null +++ b/test/files/neg/t7052.scala @@ -0,0 +1,21 @@ + +class A { + def apply(xs: Int*) = 42 +} + +/* name clash between defined and inherited member: + */ +class B extends A { + def apply(xs: Seq[Int]) = 27 +} + +/* method apply overrides nothing. +class C extends A { + override def apply(xs: Seq[Int]) = 17 +} + */ + +// ok because different return type +class D extends A { + def apply(xs: Seq[Int]) = "42" +} diff --git a/test/files/neg/t7052b.check b/test/files/neg/t7052b.check new file mode 100644 index 000000000000..c45d895b65c0 --- /dev/null +++ b/test/files/neg/t7052b.check @@ -0,0 +1,6 @@ +t7052b.scala:15: error: method apply overrides nothing. +Note: the super classes of class C contain the following, non final members named apply: +def apply(xs: Int*): Int + override def apply(xs: Seq[Int]) = 17 + ^ +1 error diff --git a/test/files/neg/t7052b.scala b/test/files/neg/t7052b.scala new file mode 100644 index 000000000000..8c410e8bf0ef --- /dev/null +++ b/test/files/neg/t7052b.scala @@ -0,0 +1,21 @@ + +class A { + def apply(xs: Int*) = 42 +} + +/* name clash between defined and inherited member: +class B extends A { + def apply(xs: Seq[Int]) = 27 +} + */ + +/* method apply overrides nothing. + */ +class C extends A { + override def apply(xs: Seq[Int]) = 17 +} + +// ok because different return type +class D extends A { + def apply(xs: Seq[Int]) = "42" +} diff --git a/test/files/neg/t7721.check b/test/files/neg/t7721.check index 04ef4858356c..2fa50df39c8d 100644 --- a/test/files/neg/t7721.check +++ b/test/files/neg/t7721.check @@ -22,6 +22,24 @@ t7721.scala:49: warning: abstract type pattern B.this.Foo is unchecked since it t7721.scala:49: warning: abstract type pattern B.this.Bar is unchecked since it is eliminated by erasure case x: Foo with Bar with Concrete => x.bippy + x.barry + x.dingo + x.conco + x.bongo ^ +t7721.scala:13: warning: The outer reference in this type test cannot be checked at run time. + case x: Foo with Concrete => x.bippy + x.conco + ^ +t7721.scala:17: warning: The outer reference in this type test cannot be checked at run time. + case x: Concrete with Foo => x.bippy + x.conco + ^ +t7721.scala:21: warning: The outer reference in this type test cannot be checked at run time. + case x: Foo with Bar => x.bippy + x.barry + ^ +t7721.scala:41: warning: The outer reference in this type test cannot be checked at run time. + case x: Foo with Concrete => x.bippy + x.dingo + x.conco + ^ +t7721.scala:45: warning: The outer reference in this type test cannot be checked at run time. + case x: Concrete with Foo => x.bippy + x.dingo + x.conco + ^ +t7721.scala:49: warning: The outer reference in this type test cannot be checked at run time. + case x: Foo with Bar with Concrete => x.bippy + x.barry + x.dingo + x.conco + x.bongo + ^ error: No warnings can be incurred under -Werror. -8 warnings +14 warnings 1 error diff --git a/test/files/neg/t8035-removed.check b/test/files/neg/t8035-removed.check index 1938c010d557..7c444dcd6840 100644 --- a/test/files/neg/t8035-removed.check +++ b/test/files/neg/t8035-removed.check @@ -13,4 +13,11 @@ t8035-removed.scala:11: error: adaptation of an empty argument list by inserting given arguments: sdf.format() ^ +t8035-removed.scala:14: warning: adapted the argument list to the expected 2-tuple: add additional parens instead + signature: List.::[B >: A](elem: B): List[B] + given arguments: 42, 27 + after adaptation: List.::((42, 27): (Int, Int)) + Nil.::(42, 27) // yeswarn + ^ +1 warning 3 errors diff --git a/test/files/neg/t8035-removed.scala b/test/files/neg/t8035-removed.scala index e3bc04d8ea10..bada37b7d2f1 100644 --- a/test/files/neg/t8035-removed.scala +++ b/test/files/neg/t8035-removed.scala @@ -1,4 +1,4 @@ -// scalac: -Xsource:3.0 +// scalac: -Xsource:3.0 -Xlint -Werror // object Foo { List(1,2,3).toSet() @@ -9,4 +9,7 @@ object Foo { import java.text.SimpleDateFormat val sdf = new SimpleDateFormat("yyyyMMdd-HH0000") sdf.format() + + (42, 27) :: Nil // nowarn + Nil.::(42, 27) // yeswarn } diff --git a/test/files/neg/t8266-invalid-interp.check b/test/files/neg/t8266-invalid-interp.check index 0f55ef3eaf42..bdfcd97d6039 100644 --- a/test/files/neg/t8266-invalid-interp.check +++ b/test/files/neg/t8266-invalid-interp.check @@ -1,6 +1,6 @@ t8266-invalid-interp.scala:4: error: Trailing '\' escapes nothing. - f"a\", - ^ + f"""a\""", + ^ t8266-invalid-interp.scala:5: error: invalid escape '\x' not one of [\b, \t, \n, \f, \r, \\, \", \', \uxxxx] at index 1 in "a\xc". Use \\ for literal \. f"a\xc", ^ diff --git a/test/files/neg/t8266-invalid-interp.scala b/test/files/neg/t8266-invalid-interp.scala index 4b26546880a3..87579a68691b 100644 --- a/test/files/neg/t8266-invalid-interp.scala +++ b/test/files/neg/t8266-invalid-interp.scala @@ -1,7 +1,7 @@ trait X { def f = Seq( - f"a\", + f"""a\""", f"a\xc", // following could suggest \u000b for vertical tab, similar for \a alert f"a\vc" diff --git a/test/files/neg/t9125.check b/test/files/neg/t9125.check new file mode 100644 index 000000000000..cf58ee5c812a --- /dev/null +++ b/test/files/neg/t9125.check @@ -0,0 +1,6 @@ +t9125.scala:10: error: reference to p is ambiguous; +it is both defined in package q and imported subsequently by +import _root_.p + def f() = new p.C + ^ +1 error diff --git a/test/files/neg/t9125.scala b/test/files/neg/t9125.scala new file mode 100644 index 000000000000..78af55cb91e9 --- /dev/null +++ b/test/files/neg/t9125.scala @@ -0,0 +1,13 @@ + +package p { + class C +} + +package q { + object p { + class K { + import _root_.p + def f() = new p.C + } + } +} diff --git a/test/files/neg/t9334.check b/test/files/neg/t9334.check new file mode 100644 index 000000000000..e5fe6ef6d0ed --- /dev/null +++ b/test/files/neg/t9334.check @@ -0,0 +1,6 @@ +t9334.scala:5: error: weaker access privileges in overriding +def aaa: Int (defined in class A) + override should not be private + private[this] def aaa: Int = 42 + ^ +1 error diff --git a/test/files/neg/t9334.scala b/test/files/neg/t9334.scala new file mode 100644 index 000000000000..c8838e855db2 --- /dev/null +++ b/test/files/neg/t9334.scala @@ -0,0 +1,6 @@ +class A { + def aaa: Int = 10 +} +class B extends A { + private[this] def aaa: Int = 42 +} diff --git a/test/files/neg/t9847.check b/test/files/neg/t9847.check index 27899eb467be..d3c6c485f72c 100644 --- a/test/files/neg/t9847.check +++ b/test/files/neg/t9847.check @@ -1,15 +1,3 @@ -t9847.scala:10: warning: Line starts with an operator that in future -will be taken as an infix expression continued from the previous line. -To force the previous interpretation as a separate statement, -add an explicit `;`, add an empty line, or remove spaces after the operator. - + 1 - ^ -t9847.scala:14: warning: Line starts with an operator that in future -will be taken as an infix expression continued from the previous line. -To force the previous interpretation as a separate statement, -add an explicit `;`, add an empty line, or remove spaces after the operator. - + 1 - ^ t9847.scala:6: warning: discarded non-Unit value def f(): Unit = 42 ^ @@ -47,5 +35,5 @@ t9847.scala:24: warning: a pure expression does nothing in statement position; m class D { 42 ; 17 } ^ error: No warnings can be incurred under -Werror. -14 warnings +12 warnings 1 error diff --git a/test/files/neg/text-blocks.check b/test/files/neg/text-blocks.check new file mode 100644 index 000000000000..8a9af6292a04 --- /dev/null +++ b/test/files/neg/text-blocks.check @@ -0,0 +1,13 @@ +text-blocks/Invalid1.java:4: error: illegal text block open delimiter sequence, missing line terminator + public static final String badOpeningDelimiter = """non-whitespace + ^ +text-blocks/Invalid1.java:4: error: expected + public static final String badOpeningDelimiter = """non-whitespace + ^ +text-blocks/Invalid1.java:6: error: illegal text block open delimiter sequence, missing line terminator + """; + ^ +text-blocks/Invalid2.java:6: error: unclosed string literal + foo""""; + ^ +4 errors diff --git a/test/files/neg/text-blocks/Invalid1.java b/test/files/neg/text-blocks/Invalid1.java new file mode 100644 index 000000000000..54c7e98d9219 --- /dev/null +++ b/test/files/neg/text-blocks/Invalid1.java @@ -0,0 +1,7 @@ +// javaVersion: 15+ +class Invalid1 { + + public static final String badOpeningDelimiter = """non-whitespace + foo + """; +} diff --git a/test/files/neg/text-blocks/Invalid2.java b/test/files/neg/text-blocks/Invalid2.java new file mode 100644 index 000000000000..08b0a57548aa --- /dev/null +++ b/test/files/neg/text-blocks/Invalid2.java @@ -0,0 +1,7 @@ +// javaVersion: 15+ +class Invalid2 { + + // Closing delimiter is first three eligible `"""`, not last + public static final String closingDelimiterIsNotScalas = """ + foo""""; +} diff --git a/test/files/neg/varargs2.check b/test/files/neg/varargs2.check new file mode 100644 index 000000000000..23d13ec6bf09 --- /dev/null +++ b/test/files/neg/varargs2.check @@ -0,0 +1,13 @@ +varargs2.scala:7: error: Only methods can be marked @varargs + @varargs val x = 42 // nok + ^ +varargs2.scala:8: error: Only methods can be marked @varargs + def f(@varargs y: Int) = 42 // nok + ^ +varargs2.scala:9: error: Only methods can be marked @varargs + def g(z: Int @varargs) = 42 // nok + ^ +varargs2.scala:10: error: Only methods can be marked @varargs + def h(z: Int) = 42: @varargs // nok + ^ +4 errors diff --git a/test/files/neg/varargs2.scala b/test/files/neg/varargs2.scala new file mode 100644 index 000000000000..82ccf97cb03a --- /dev/null +++ b/test/files/neg/varargs2.scala @@ -0,0 +1,13 @@ +// scalac: -Xsource:3 + +import annotation.* + +trait T { + @varargs def d(n: Int*) = 42 // ok + @varargs val x = 42 // nok + def f(@varargs y: Int) = 42 // nok + def g(z: Int @varargs) = 42 // nok + def h(z: Int) = 42: @varargs // nok + + lazy val VarargsClass = List.empty[varargs] // good one +} diff --git a/test/files/neg/variant-placeholders-future.check b/test/files/neg/variant-placeholders-future.check new file mode 100644 index 000000000000..d166e8d577a9 --- /dev/null +++ b/test/files/neg/variant-placeholders-future.check @@ -0,0 +1,7 @@ +variant-placeholders-future.scala:4: error: `=`, `>:`, or `<:` expected + type -_ = Int // error -_ not allowed as a type def name without backticks + ^ +variant-placeholders-future.scala:5: error: `=`, `>:`, or `<:` expected + type +_ = Int // error +_ not allowed as a type def name without backticks + ^ +2 errors diff --git a/test/files/neg/variant-placeholders-future.scala b/test/files/neg/variant-placeholders-future.scala new file mode 100644 index 000000000000..75296ff945b4 --- /dev/null +++ b/test/files/neg/variant-placeholders-future.scala @@ -0,0 +1,6 @@ +// scalac: -Xsource:3 +// +object Test { + type -_ = Int // error -_ not allowed as a type def name without backticks + type +_ = Int // error +_ not allowed as a type def name without backticks +} diff --git a/test/files/neg/variant-placeholders-nofuture.check b/test/files/neg/variant-placeholders-nofuture.check new file mode 100644 index 000000000000..8cf591d0a32f --- /dev/null +++ b/test/files/neg/variant-placeholders-nofuture.check @@ -0,0 +1,7 @@ +variant-placeholders-nofuture.scala:5: error: ';' expected but '_' found. + val fnMinusPlus1: -_ => +_ = (_: Int).toLong // error -_/+_ won't parse without -Xsource:3 + ^ +variant-placeholders-nofuture.scala:6: error: ')' expected but '_' found. + val fnMinusPlus2: (-_) => +_ = fnMinusPlus1 // error -_/+_ won't parse without -Xsource:3 + ^ +2 errors diff --git a/test/files/neg/variant-placeholders-nofuture.scala b/test/files/neg/variant-placeholders-nofuture.scala new file mode 100644 index 000000000000..5f638f68a84a --- /dev/null +++ b/test/files/neg/variant-placeholders-nofuture.scala @@ -0,0 +1,8 @@ +object Test { + type `-_` = Int + type `+_` = Long + + val fnMinusPlus1: -_ => +_ = (_: Int).toLong // error -_/+_ won't parse without -Xsource:3 + val fnMinusPlus2: (-_) => +_ = fnMinusPlus1 // error -_/+_ won't parse without -Xsource:3 + val fnMinusPlus3: -_ => (+_) = fnMinusPlus2 // error -_/+_ won't parse without -Xsource:3 +} diff --git a/test/files/neg/wildcards-future.check b/test/files/neg/wildcards-future.check new file mode 100644 index 000000000000..0aedb6dd8b01 --- /dev/null +++ b/test/files/neg/wildcards-future.check @@ -0,0 +1,11 @@ +wildcards-future.scala:7: error: type mismatch; + found : Map[_$1,_$2] where type _$2 >: Null, type _$1 <: AnyRef + required: Map[String,String] + underscores : Map[String, String] // error wildcard variables starting with `_` + ^ +wildcards-future.scala:9: error: type mismatch; + found : Map[?$1,?$2] where type ?$2 >: Null, type ?$1 <: AnyRef + required: Map[String,String] + qmarks : Map[String, String] // error – wildcard variables should start with `?` to differentiate from the old syntax + ^ +2 errors diff --git a/test/files/neg/wildcards-future.scala b/test/files/neg/wildcards-future.scala new file mode 100644 index 000000000000..54b7675813e7 --- /dev/null +++ b/test/files/neg/wildcards-future.scala @@ -0,0 +1,11 @@ +// scalac: -Xsource:3 +// +object Test { + val underscores: Map[_ <: AnyRef, _ >: Null] = Map() + val qmarks: Map[? <: AnyRef, ? >: Null] = Map() + + underscores : Map[String, String] // error wildcard variables starting with `_` + + qmarks : Map[String, String] // error – wildcard variables should start with `?` to differentiate from the old syntax + // (and have a mildly more readable error...) +} diff --git a/test/files/pos/and-future.scala b/test/files/pos/and-future.scala new file mode 100644 index 000000000000..f7e15e822ecc --- /dev/null +++ b/test/files/pos/and-future.scala @@ -0,0 +1,17 @@ +// scalac: -Xsource:3 +// + +trait X +trait Y + +class Test[A, B <: A & AnyRef] { + def foo[T >: A & Null <: A & AnyRef & Any](x: T & ""): "" & T = x + + val a: X & Y & AnyRef = new X with Y {} + val b: (X & Y) & AnyRef = new X with Y {} + val c: X & (Y & AnyRef) = new X with Y {} + + val d: X & Y = c match { + case xy: (X & Y) => xy + } +} diff --git a/test/files/pos/i11371.scala b/test/files/pos/i11371.scala new file mode 100644 index 000000000000..74156b777c9f --- /dev/null +++ b/test/files/pos/i11371.scala @@ -0,0 +1,21 @@ +// scalac: -Xsource:3 +// +object HelloWorld { + def whileLoop: Int = { + var i = 0 + var acc = 0 + while (i < 3) { + var `i'` = 0 + while (`i'` < 4) { + acc += (i * `i'`) + `i'` += 1 + } + i += 1 + } + acc + } + + def main(args: Array[String]): Unit = { + println(s"hello world: ${whileLoop}") + } +} diff --git a/test/files/pos/import-future.scala b/test/files/pos/import-future.scala new file mode 100644 index 000000000000..1c0c3410f36a --- /dev/null +++ b/test/files/pos/import-future.scala @@ -0,0 +1,33 @@ +// scalac: -Xsource:3 +// + +import java.io as jio +import scala.{collection as c} + +import c.mutable as mut +import mut.ArrayBuffer as Buf + +object O { + val x: jio.IOException = ??? + val y = Buf(1, 2, 3) + + type OString = String + def foo22(x: Int) = x +} + +class C { + import O.{ foo22 as foo, OString as OS } + println(foo(22)) + val s: OS = "" + + import mut.* + val ab = ArrayBuffer(1) +} + +object starring { + + import scala.concurrent.*, duration.{Duration as D, *}, ExecutionContext.Implicits.* + + val f = Future(42) + val r = Await.result(f, D.Inf) +} diff --git a/test/files/pos/leading-infix-op.scala b/test/files/pos/leading-infix-op.scala new file mode 100644 index 000000000000..4b60aa67b8c1 --- /dev/null +++ b/test/files/pos/leading-infix-op.scala @@ -0,0 +1,19 @@ + +// scalac: -Xsource:3 + +trait T { + def f(x: Int): Boolean = + x < 0 + || + x > 0 + && + x != 3 + + def g(x: Option[Int]) = x match { + case Some(err) => + println("hi") + ??? + case None => + ??? + } +} diff --git a/test/files/pos/macro-annot/t12366.check b/test/files/pos/macro-annot/t12366.check new file mode 100644 index 000000000000..de47a31a6b4e --- /dev/null +++ b/test/files/pos/macro-annot/t12366.check @@ -0,0 +1 @@ +warning: 2 deprecations; re-run with -deprecation for details diff --git a/test/files/pos/macro-annot/t12366.scala b/test/files/pos/macro-annot/t12366.scala new file mode 100644 index 000000000000..9b75bb3c6d1f --- /dev/null +++ b/test/files/pos/macro-annot/t12366.scala @@ -0,0 +1,15 @@ +// scalac: -Ymacro-annotations +object Test extends App { + + @deprecated + class Inner() { + } + + lazy val Inner = new Inner() + + @deprecated + class Inner2() { + } + + val Inner2 = new Inner2() +} diff --git a/test/files/pos/open-infix-future.scala b/test/files/pos/open-infix-future.scala new file mode 100644 index 000000000000..8fee778d40cb --- /dev/null +++ b/test/files/pos/open-infix-future.scala @@ -0,0 +1,36 @@ +// scalac: -Xsource:3 +// + +open class A +infix class B[T, S] + +open infix class C[T, S] +open infix case class CC[T, S](x: Int) +infix open class D[T, S] +infix trait DT[T, S] + +open +infix +private +class E + +class F { + open infix class C1[T, S] + infix type X + + infix def foo(x: Int): Int = x +} + +object G { + open infix class C2[T, S] +} + +object Test { + val infix: Int = 1 + infix + 1 + val open: Int => Int = x => x + open(1) + open { + 2 + } +} diff --git a/test/files/pos/sammy_java8/F.java b/test/files/pos/sammy_java8/F.java new file mode 100644 index 000000000000..5dac57a1e2ae --- /dev/null +++ b/test/files/pos/sammy_java8/F.java @@ -0,0 +1,6 @@ +public interface F { + U apply(T t); + default void yadayada() { + throw new UnsupportedOperationException("yadayada"); + } +} diff --git a/test/files/pos/sammy_java8/Test.scala b/test/files/pos/sammy_java8/Test.scala new file mode 100644 index 000000000000..61fcf4f0ce4f --- /dev/null +++ b/test/files/pos/sammy_java8/Test.scala @@ -0,0 +1,4 @@ +class T { + def app[T, U](x: T)(f: F[T, U]): U = f(x) + app(1)(x => List(x)) +} diff --git a/test/files/pos/t11534.scala b/test/files/pos/t11534.scala new file mode 100644 index 000000000000..bab4bd956d87 --- /dev/null +++ b/test/files/pos/t11534.scala @@ -0,0 +1,8 @@ +// scalac: -Werror +object Test1 { + val g: scala.tools.nsc.Global = ??? + import g._ + def test(sym: Symbol) = sym.name match { + case _: TermName => + } +} diff --git a/test/files/pos/t11966.scala b/test/files/pos/t11966.scala index 2e9632a34869..b662e71322da 100644 --- a/test/files/pos/t11966.scala +++ b/test/files/pos/t11966.scala @@ -3,5 +3,5 @@ object Test { val original = """\/ \/ /\""" val minimal = """\1234\""" - val alternative = raw"\1234\" + val alternative = raw"""\1234\""" } \ No newline at end of file diff --git a/test/files/pos/t12210.scala b/test/files/pos/t12210.scala new file mode 100644 index 000000000000..35d6cdbf8c87 --- /dev/null +++ b/test/files/pos/t12210.scala @@ -0,0 +1,20 @@ +trait SpecFun[@specialized T] { + type Res + def res: Res +} + +object Test { + def m[@specialized T](op: SpecFun[T]): op.Res = op.res +} + +trait ValuesVisitor[A] { + def visit(a: A): Unit + def visitArray(arr: Array[A]): Unit = ??? +} + +class OpArray[@specialized A] { + def traverse(from: Array[A], fn: ValuesVisitor[A]): fn.type = { + fn.visitArray(from) + fn + } +} diff --git a/test/files/pos/t12225.scala b/test/files/pos/t12225.scala new file mode 100644 index 000000000000..baae67d36bf8 --- /dev/null +++ b/test/files/pos/t12225.scala @@ -0,0 +1,6 @@ +// scalac: -Ydebug +object Test { + def foo(arr: Array[Int]): Unit = { + val Array(x, y) = arr + } +} diff --git a/test/files/pos/t12233.scala b/test/files/pos/t12233.scala new file mode 100644 index 000000000000..481b5258d2d5 --- /dev/null +++ b/test/files/pos/t12233.scala @@ -0,0 +1,12 @@ + +trait TypeClass[T] +class Hehe[T: TypeClass](i: Int, j: Int) { + def this(i: Int)(implicit j: Int) = this(i, j) +} + +/* was +test/files/pos/t12233.scala:4: error: too many arguments (found 2, expected 1) for constructor Hehe: (implicit evidence$1: TypeClass[T]): Hehe[T] + def this(i: Int)(implicit j: Int) = this(i, j) + ^ +1 error + */ diff --git a/test/files/pos/t12312-hmm.scala b/test/files/pos/t12312-hmm.scala new file mode 100644 index 000000000000..16decd4f9325 --- /dev/null +++ b/test/files/pos/t12312-hmm.scala @@ -0,0 +1,45 @@ +package hmm + +// Taken from https://github.com/typelevel/kind-projector/blob/7ad46d6ca995976ae2ff18215dbb32cd7ad0dd7a/src/test/scala/hmm.scala +// As a regression test for the issue spotted in https://github.com/scala/community-build/pull/1400 + +class TC[A] + +object TC { + def apply[A]: Unit = () +} + +object test { + + sealed trait HList extends Product with Serializable + case class ::[+H, +T <: HList](head : H, tail : T) extends HList + sealed trait HNil extends HList + case object HNil extends HNil + + TC[Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: HNil] + + TC[Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: HNil] + + TC[Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: + Int :: Int :: Int :: Int :: Int :: Int :: Int :: Int :: HNil] +} diff --git a/test/files/pos/t12349b/A.java b/test/files/pos/t12349b/A.java new file mode 100644 index 000000000000..aab1185d87ac --- /dev/null +++ b/test/files/pos/t12349b/A.java @@ -0,0 +1,7 @@ +package p; + +public class A { + public static class R { } + + /* package-protected */ R foo() { return null; } +} diff --git a/test/files/pos/t12349b/B.java b/test/files/pos/t12349b/B.java new file mode 100644 index 000000000000..735c91372a03 --- /dev/null +++ b/test/files/pos/t12349b/B.java @@ -0,0 +1,7 @@ +package q; + +public class B extends p.A { + public static class RR extends p.A.R { } + + /* package-protected */ RR foo() { return null; } +} diff --git a/test/files/pos/t12349b/Test.scala b/test/files/pos/t12349b/Test.scala new file mode 100644 index 000000000000..3f22fa033e08 --- /dev/null +++ b/test/files/pos/t12349b/Test.scala @@ -0,0 +1 @@ +class Test extends q.B diff --git a/test/files/pos/t12392.scala b/test/files/pos/t12392.scala new file mode 100644 index 000000000000..78496e1aa392 --- /dev/null +++ b/test/files/pos/t12392.scala @@ -0,0 +1,14 @@ +import scala.reflect.api.Universe + +object Test { + type SingletonUniverse = Universe with Singleton + def deepIntersectionTypeMembers[U <: SingletonUniverse](targetType: U#Type): List[U#Type] = { + def go(tpe: U#Type): List[U#Type] = { + tpe match { + case r: U#RefinedTypeApi => r.parents.flatMap(t => deepIntersectionTypeMembers[U]((t.dealias): U#Type)) + case _ => List(tpe) + } + } + go(targetType).distinct + } +} diff --git a/test/files/pos/t283.scala b/test/files/pos/t283.scala new file mode 100644 index 000000000000..8691404db6ae --- /dev/null +++ b/test/files/pos/t283.scala @@ -0,0 +1,5 @@ + +import _root_._ // _root_.java._ is OK +object Test extends App { + println(java.util.Locale.getDefault().toString) // static call +} diff --git a/test/files/pos/t7398/Iterator.java b/test/files/pos/t7398/Iterator.java new file mode 100644 index 000000000000..75b5a8b303b7 --- /dev/null +++ b/test/files/pos/t7398/Iterator.java @@ -0,0 +1,10 @@ +public interface Iterator { + boolean hasNext(); + E next(); + default void remove() { + throw new UnsupportedOperationException("remove"); + } + default void forEachRemaining(java.util.function.Consumer action) { + throw new UnsupportedOperationException("forEachRemaining"); + } +} diff --git a/test/files/pos/t7398/Test.scala b/test/files/pos/t7398/Test.scala new file mode 100644 index 000000000000..2068acaa6dc7 --- /dev/null +++ b/test/files/pos/t7398/Test.scala @@ -0,0 +1,5 @@ +class Test extends Iterator[String] { + def hasNext = true + def next() = "" + def test = this.remove() +} diff --git a/test/files/pos/t8852/Interface.java b/test/files/pos/t8852/Interface.java new file mode 100644 index 000000000000..7b35f3b12f1e --- /dev/null +++ b/test/files/pos/t8852/Interface.java @@ -0,0 +1,5 @@ +public interface Interface { + public static int staticMethod() { + return 42; + } +} diff --git a/test/files/pos/t8852/Test.scala b/test/files/pos/t8852/Test.scala new file mode 100644 index 000000000000..acd36ec2a5a0 --- /dev/null +++ b/test/files/pos/t8852/Test.scala @@ -0,0 +1,5 @@ +object Test { + val x: Int = Interface.staticMethod() +} + +class C extends Interface // expect no errors about unimplemented members. diff --git a/test/files/pos/t9125.scala b/test/files/pos/t9125.scala new file mode 100644 index 000000000000..f1d3e67f618f --- /dev/null +++ b/test/files/pos/t9125.scala @@ -0,0 +1,14 @@ + +package p { + class C +} + +package q { + package p { + class K { + import _root_.{p => pp} + def f() = new pp.C + def g() = new _root_.p.C + } + } +} diff --git a/test/files/pos/varargs-future.scala b/test/files/pos/varargs-future.scala new file mode 100644 index 000000000000..e8c9057e564b --- /dev/null +++ b/test/files/pos/varargs-future.scala @@ -0,0 +1,22 @@ +// scalac: -Xsource:3 +// + +class Test { + def foo(xs: Int*): Seq[Int] = xs + + val s: Seq[Int] = Seq(1, 2, 3) + foo(s*) + + // not very useful, but supported by Scala 3 (and matches what works with `: _*` syntax) + foo( + s*, + ) + + s match { + case Seq(elems*) => println(elems) + } + + s match { + case Seq(x, rest*) => println(rest) + } +} diff --git a/test/files/pos/variant-placeholders-future.scala b/test/files/pos/variant-placeholders-future.scala new file mode 100644 index 000000000000..383db8420f85 --- /dev/null +++ b/test/files/pos/variant-placeholders-future.scala @@ -0,0 +1,35 @@ +// scalac: -Xsource:3 +// +object Test { + type `-_` = Int + type `+_` = Long + + val fnMinusPlus1: -_ => +_ = (_: Int).toLong + val fnMinusPlus2: (-_) => +_ = fnMinusPlus1 + val fnMinusPlus3: -_ => (+_) = fnMinusPlus2 + + val fnTupMinusPlus2: (=> -_, -_) => +_ = (a, b) => ((a: Int) + (b: Int)).toLong + def defMinusPlus2(byname: => -_, vararg: -_*): +_ = ((vararg.sum: Int) + (byname: -_)).toLong + val infixMinusPlus2: -_ Either +_ = Right[-_, +_](1L) + + val optPlus: Option[+_] = Some[ + _ ](1L) // spaces allowed + optPlus match { + case opt: Option[ + _ ] => + val opt1: + _ = opt.get + val opt2: Long = opt1 + } + + val optMinus: Option[-_] = Some[ - _ ](1) // spaces allowed + optMinus match { + case opt: Option[ - _ ] => + val opt1: `-_` = opt.get + val optErr: - _ = opt.get + val opt2: Int = opt1 + } + + locally { + type `-_`[A] = A + type `+_`[A] = Option[A] + val optOpt: Option[ + _ [+_[-_[Int]]]] = Some(Some(Some(1))) + } +} diff --git a/test/files/pos/wildcards-future.scala b/test/files/pos/wildcards-future.scala new file mode 100644 index 000000000000..928cab3648b0 --- /dev/null +++ b/test/files/pos/wildcards-future.scala @@ -0,0 +1,21 @@ +// scalac: -Xsource:3 +// +object Test { + val xs: List[?] = List(1, 2, 3) + val ys: Map[? <: AnyRef, ? >: Null] = Map() + + def foo(x: Any) = x match { + case x: List[?] => x + case _ => x + } + + // Only allowed in Scala 3 under -source 3.0-migration + type ? = Int + + val xs2: List[`?`] = List(1) + val xs3: List[Int] = xs2 + + def foo2(x: List[`?`]): List[Int] = x match { + case x: List[`?`] => x + } +} diff --git a/test/files/presentation/infix-completion.check b/test/files/presentation/infix-completion.check index 9d0723e882c4..a6549c83911b 100644 --- a/test/files/presentation/infix-completion.check +++ b/test/files/presentation/infix-completion.check @@ -3,9 +3,9 @@ reload: Snippet.scala askTypeCompletion at Snippet.scala(1,34) ================================================================================ [response] askTypeCompletion at (1,34) -#partest !java15 +#partest !java15+ retrieved 203 members -#partest java15 +#partest java15+ retrieved 205 members #partest [inaccessible] protected def num: Fractional[Double] @@ -123,7 +123,7 @@ def compareTo(x$1: Double): Int def compareTo(x$1: Float): Int def compareTo(x$1: Integer): Int def compareTo(x$1: Long): Int -#partest java15 +#partest java15+ def describeConstable(): java.util.Optional[Double] #partest def doubleValue(): Double @@ -145,7 +145,7 @@ def isNegInfinity: Boolean def isPosInfinity: Boolean def isValidLong: Boolean def longValue(): Long -#partest java15 +#partest java15+ def resolveConstantDesc(x$1: java.lang.invoke.MethodHandles.Lookup): Double #partest def round: Long diff --git a/test/files/presentation/infix-completion2.check b/test/files/presentation/infix-completion2.check index 9d0723e882c4..a6549c83911b 100644 --- a/test/files/presentation/infix-completion2.check +++ b/test/files/presentation/infix-completion2.check @@ -3,9 +3,9 @@ reload: Snippet.scala askTypeCompletion at Snippet.scala(1,34) ================================================================================ [response] askTypeCompletion at (1,34) -#partest !java15 +#partest !java15+ retrieved 203 members -#partest java15 +#partest java15+ retrieved 205 members #partest [inaccessible] protected def num: Fractional[Double] @@ -123,7 +123,7 @@ def compareTo(x$1: Double): Int def compareTo(x$1: Float): Int def compareTo(x$1: Integer): Int def compareTo(x$1: Long): Int -#partest java15 +#partest java15+ def describeConstable(): java.util.Optional[Double] #partest def doubleValue(): Double @@ -145,7 +145,7 @@ def isNegInfinity: Boolean def isPosInfinity: Boolean def isValidLong: Boolean def longValue(): Long -#partest java15 +#partest java15+ def resolveConstantDesc(x$1: java.lang.invoke.MethodHandles.Lookup): Double #partest def round: Long diff --git a/test/files/presentation/t12308.check b/test/files/presentation/t12308.check new file mode 100644 index 000000000000..80792e4a7f27 --- /dev/null +++ b/test/files/presentation/t12308.check @@ -0,0 +1,50 @@ +reload: Foo.scala +askLoadedTyped 1 +Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1) +askLoadedTyped 2 +Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1) +reload: Foo.scala +askLoadedTyped 3 +Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1) +targeted 1 + +askType at Foo.scala(2,37) +================================================================================ +[response] askTypeAt (2,37) +1 +================================================================================ + +askType at Foo.scala(3,17) +================================================================================ +[response] askTypeAt (3,17) +1 +================================================================================ + +askType at Foo.scala(4,37) +================================================================================ +[response] askTypeAt (4,37) +1 +================================================================================ +Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1) +reload: Foo.scala +targeted 2 - doesn't handle nowarn correctly + +askType at Foo.scala(2,37) +================================================================================ +[response] askTypeAt (2,37) +1 +================================================================================ + +askType at Foo.scala(3,17) +================================================================================ +[response] askTypeAt (3,17) +1 +================================================================================ + +askType at Foo.scala(4,37) +================================================================================ +[response] askTypeAt (4,37) +1 +================================================================================ +Problem(RangePosition(t12308/src/Foo.scala, 67, 67, 72),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1) +Problem(RangePosition(t12308/src/Foo.scala, 109, 109, 114),A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.,1) diff --git a/test/files/presentation/t12308/Test.scala b/test/files/presentation/t12308/Test.scala new file mode 100644 index 000000000000..102b06170d69 --- /dev/null +++ b/test/files/presentation/t12308/Test.scala @@ -0,0 +1,50 @@ +import scala.tools.nsc.interactive.tests.InteractiveTest + +object Test extends InteractiveTest { + + def ws(): Unit = { + println(compiler.unitOfFile.values.flatMap(_.problems).mkString("", "\n", "")) + } + + override def runDefaultTests(): Unit = { + val run = compiler.currentRun + + println("askLoadedTyped 1") + sourceFiles foreach (src => askLoadedTyped(src).get) + ws() + assert(run eq compiler.currentRun) + + println("askLoadedTyped 2") + sourceFiles foreach (src => askLoadedTyped(src).get) // tree is already typed, typer is not called + ws() + assert(run eq compiler.currentRun) + + askReload(sourceFiles.toIndexedSeq) // new run, new tree, type checking again + println("askLoadedTyped 3") + sourceFiles foreach (src => askLoadedTyped(src).get) + ws() + val run1 = compiler.currentRun + assert(run ne run1) + + println("targeted 1") + // tree is already typed, typer not called + new TypeAction(compiler).runTest() + assert(run1 eq compiler.currentRun) + ws() + + askReload(sourceFiles.toIndexedSeq) + + + // what happens here: + // 1. targeted type check of `foo`, warningin is suspended, then *not* reported because of the nowarn. + // once that type check is finished, `reportSuspendedMessages` is called + // 2. targeted type check of `bar`, warning is directly issued because `reportSuspendedMessages` was called + // before in that run, for that source file; `suppressions` are considered known. + // 3. targeted type check of `baz`, warning is directly issued, though it should be filtered out... + println("targeted 2 - doesn't handle nowarn correctly") + // tree not yet typed + new TypeAction(compiler).runTest() + assert(run1 ne compiler.currentRun) + ws() + } +} diff --git a/test/files/presentation/t12308/src/Foo.scala b/test/files/presentation/t12308/src/Foo.scala new file mode 100644 index 000000000000..5a5d918cb3bd --- /dev/null +++ b/test/files/presentation/t12308/src/Foo.scala @@ -0,0 +1,5 @@ +class Foo { + @annotation.nowarn def foo = try 1 /*?*/ + def bar = try 1/*?*/ + @annotation.nowarn def bzz = try 1 /*?*/ +} diff --git a/test/files/presentation/t8941/Runner.scala b/test/files/presentation/t8941/Runner.scala index 6401a830a22a..14a6aa835064 100644 --- a/test/files/presentation/t8941/Runner.scala +++ b/test/files/presentation/t8941/Runner.scala @@ -1,11 +1,3 @@ import scala.tools.nsc.interactive.tests.InteractiveTest -object Test extends InteractiveTest { - override def runDefaultTests(): Unit = { - // make sure typer is done.. the virtual pattern matcher might translate - // some trees and mess up positions. But we'll catch it red handed! - // sourceFiles foreach (src => askLoadedTyped(src).get) - super.runDefaultTests() - } - -} +object Test extends InteractiveTest diff --git a/test/files/run/array-cleanup-optimation-specialized.scala b/test/files/run/array-cleanup-optimation-specialized.scala new file mode 100644 index 000000000000..5db397752df3 --- /dev/null +++ b/test/files/run/array-cleanup-optimation-specialized.scala @@ -0,0 +1,12 @@ +import scala.reflect.ClassTag + +object Test { + def main(args: Array[String]): Unit = { + assert(apply[String]("") == classOf[Array[String]]) + assert(apply[Double](1d) == classOf[Array[Double]]) + } + + def apply[@specialized(Double) C: ClassTag](c: C): Class[_] = { + Array(c).getClass + } +} diff --git a/test/files/run/bridges.javaopts b/test/files/run/bridges.javaopts deleted file mode 100644 index 3a63111bf2fd..000000000000 --- a/test/files/run/bridges.javaopts +++ /dev/null @@ -1 +0,0 @@ --Xss128M diff --git a/test/files/run/bridges.scala b/test/files/run/bridges.scala index 53494500a4d5..de641f03f6b5 100644 --- a/test/files/run/bridges.scala +++ b/test/files/run/bridges.scala @@ -1,3 +1,5 @@ +// java: -Xss128M + //############################################################################ // Test bridge methods //############################################################################ diff --git a/test/files/run/icode-reader-dead-code.scala b/test/files/run/icode-reader-dead-code.scala index d089f6967a0c..31f5c06f388d 100644 --- a/test/files/run/icode-reader-dead-code.scala +++ b/test/files/run/icode-reader-dead-code.scala @@ -7,7 +7,7 @@ import scala.tools.partest.DirectTest import scala.jdk.CollectionConverters._ /** - * Test that the ICodeReader does not crash if the bytecode of a method has unreachable code. + * Test that ClassReader does not crash if the bytecode of a method has unreachable code. */ object Test extends DirectTest { def code: String = ??? diff --git a/test/files/run/idempotency-case-classes.check b/test/files/run/idempotency-case-classes.check index 78ee0af219a2..7339a68be71b 100644 --- a/test/files/run/idempotency-case-classes.check +++ b/test/files/run/idempotency-case-classes.check @@ -40,7 +40,7 @@ C(2,3) case _ => false }.&&({ val C$1: C = x$1.asInstanceOf[C]; - C.this.x.==(C$1.x).&&(C.this.y.==(C$1.y)).&&(C$1.canEqual(C.this)) + C.this.x.==(C$1.x).&&(C.this.y.==(C$1.y).&&(C$1.canEqual(C.this))) })) }; object C extends scala.runtime.AbstractFunction2[Int,Int,C] with java.io.Serializable { diff --git a/test/files/run/interpolation-repl.check b/test/files/run/interpolation-repl.check new file mode 100644 index 000000000000..c6e246c806b1 --- /dev/null +++ b/test/files/run/interpolation-repl.check @@ -0,0 +1,12 @@ + +scala> raw"\"" +val res0: String = \" + +scala> raw"\" // this used to be a comment, but after scala/pull#8830 it's part of the string! " +val res1: String = "\" // this used to be a comment, but after scala/pull#8830 it's part of the string! " + +scala> raw"\" // this used to compile, now it's unclosed + ^ + error: unclosed string literal; note that `\"` no longer closes single-quoted interpolated string literals since 2.13.6, you can use a triple-quoted string instead + +scala> :quit diff --git a/test/files/run/interpolation-repl.scala b/test/files/run/interpolation-repl.scala new file mode 100644 index 000000000000..ba84178ce92c --- /dev/null +++ b/test/files/run/interpolation-repl.scala @@ -0,0 +1,9 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ +raw"\"" +raw"\" // this used to be a comment, but after scala/pull#8830 it's part of the string! " +raw"\" // this used to compile, now it's unclosed +""" +} diff --git a/test/files/run/interpolation.check b/test/files/run/interpolation.check index 997abb449726..2ab952f46f75 100644 --- a/test/files/run/interpolation.check +++ b/test/files/run/interpolation.check @@ -30,3 +30,9 @@ Best price: 13.35 0 00 +"everybody loves escaped quotes" is a common sentiment. +hi"$" +hi"$" +hi"$" +hi"$" +hi"$" diff --git a/test/files/run/interpolation.scala b/test/files/run/interpolation.scala index 14d981934895..4dc85e9f1f56 100644 --- a/test/files/run/interpolation.scala +++ b/test/files/run/interpolation.scala @@ -29,4 +29,12 @@ object Test extends App { println(f"") println(f"${0}") println(f"${0}${0}") + + println(s"$"everybody loves escaped quotes$" is a common sentiment.") + println(f"hi$"$$$"") + println(raw"hi$"$$$"") + + println(s"""hi$"$$$"""") + println(f"""hi$"$$$"""") + println(raw"""hi$"$$$"""") } diff --git a/test/files/run/lambda-serialization-gc.javaopts b/test/files/run/lambda-serialization-gc.javaopts deleted file mode 100644 index 9ecdb8a4dafd..000000000000 --- a/test/files/run/lambda-serialization-gc.javaopts +++ /dev/null @@ -1 +0,0 @@ --Xmx512m \ No newline at end of file diff --git a/test/files/run/lambda-serialization-gc.scala b/test/files/run/lambda-serialization-gc.scala index 9a179d4ed5c1..529a32146302 100644 --- a/test/files/run/lambda-serialization-gc.scala +++ b/test/files/run/lambda-serialization-gc.scala @@ -1,3 +1,5 @@ +// java: -Xmx512m + import java.io._ import java.net.URLClassLoader diff --git a/test/files/run/multiLineOps.scala b/test/files/run/multiLineOps.scala index 0bba854027fc..ef319d9210dc 100644 --- a/test/files/run/multiLineOps.scala +++ b/test/files/run/multiLineOps.scala @@ -1,12 +1,15 @@ // scalac: -Xsource:3 // -// without backticks, "not found: value +" +// was: without backticks, "not found: value +" (but parsed here as +a * 6, where backticks fool the lexer) +// now: + is taken as "solo" infix op // object Test extends App { val a = 7 val x = 1 - + // - `a` * 6 + + + `a` + * + 6 - assert(x == 1) + assert(x == 1 + 7 * 6, x) // was: 1, now: successor(42) } diff --git a/test/files/run/position-val-def.check b/test/files/run/position-val-def.check index a92c77c68cff..b0ce48239ba9 100644 --- a/test/files/run/position-val-def.check +++ b/test/files/run/position-val-def.check @@ -6,14 +6,14 @@ var x = 0 val x, y = 0 [NoPosition]{ - [0:5]val x = [11]0; + [4]val x = [11]0; [7:12]val y = [11:12]0; [NoPosition]() } var x, y = 0 [NoPosition]{ - [0:5]var x = [11]0; + [4]var x = [11]0; [7:12]var y = [11:12]0; [NoPosition]() } diff --git a/test/files/run/reflection-magicsymbols-invoke.check b/test/files/run/reflection-magicsymbols-invoke.check index 7300a52e3068..6759edfecff3 100644 --- a/test/files/run/reflection-magicsymbols-invoke.check +++ b/test/files/run/reflection-magicsymbols-invoke.check @@ -64,10 +64,10 @@ testing Object.finalize: () testing Object.getClass: class java.lang.String testing Object.hashCode: 50 testing Object.ne: false -#partest !java15 +#partest !java15+ testing Object.notify: class java.lang.IllegalMonitorStateException: null testing Object.notifyAll: class java.lang.IllegalMonitorStateException: null -#partest java15 +#partest java15+ testing Object.notify: class java.lang.IllegalMonitorStateException: current thread is not owner testing Object.notifyAll: class java.lang.IllegalMonitorStateException: current thread is not owner #partest diff --git a/test/files/run/reflection-mem-glbs.javaopts b/test/files/run/reflection-mem-glbs.javaopts deleted file mode 100644 index 9ecdb8a4dafd..000000000000 --- a/test/files/run/reflection-mem-glbs.javaopts +++ /dev/null @@ -1 +0,0 @@ --Xmx512m \ No newline at end of file diff --git a/test/files/run/reflection-mem-glbs.scala b/test/files/run/reflection-mem-glbs.scala index 2a76f1db86b0..790a445cc6d7 100644 --- a/test/files/run/reflection-mem-glbs.scala +++ b/test/files/run/reflection-mem-glbs.scala @@ -1,3 +1,5 @@ +// java: -Xmx512m + import scala.tools.partest.MemoryTest trait A { type T <: A } diff --git a/test/files/run/reflection-mem-tags.javaopts b/test/files/run/reflection-mem-tags.javaopts deleted file mode 100644 index 9ecdb8a4dafd..000000000000 --- a/test/files/run/reflection-mem-tags.javaopts +++ /dev/null @@ -1 +0,0 @@ --Xmx512m \ No newline at end of file diff --git a/test/files/run/reflection-mem-tags.scala b/test/files/run/reflection-mem-tags.scala index 6ea3c34c86bf..0ae1b9406afb 100644 --- a/test/files/run/reflection-mem-tags.scala +++ b/test/files/run/reflection-mem-tags.scala @@ -1,3 +1,5 @@ +// java: -Xmx512m + import scala.tools.partest.MemoryTest trait A { type T <: A } diff --git a/test/files/run/reify-each-node-type.check b/test/files/run/reify-each-node-type.check index afc65add7af2..5cff9e63731c 100644 --- a/test/files/run/reify-each-node-type.check +++ b/test/files/run/reify-each-node-type.check @@ -23,7 +23,7 @@ 23 new r.List[Int]() New 24 0: @unchecked Annotated 25 (null: r.Outer#Inner) SelectFromTypeTree -26 (null: Nil.type) SingletonTypeTree +26 (null: r.Nil.type) SingletonTypeTree 27 (null: T forSome { type T }) ExistentialTypeTree 28 { import r.{A, B=>C}; () } Import 29 { def f: Int = return 0; () } Return diff --git a/test/files/run/reify_copypaste1.javaopts b/test/files/run/reify_copypaste1.javaopts deleted file mode 100644 index 9740f07b079b..000000000000 --- a/test/files/run/reify_copypaste1.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm \ No newline at end of file diff --git a/test/files/run/reify_copypaste1.scala b/test/files/run/reify_copypaste1.scala index 12cc7dfe19d1..16b6ffed21c7 100644 --- a/test/files/run/reify_copypaste1.scala +++ b/test/files/run/reify_copypaste1.scala @@ -1,3 +1,5 @@ +// java: -Dneeds.forked.jvm + import scala.reflect.runtime._ import scala.reflect.runtime.universe._ import scala.reflect.runtime.universe.definitions._ diff --git a/test/files/run/repl-errors.check b/test/files/run/repl-errors.check index ab259dd20aab..836a14911295 100644 --- a/test/files/run/repl-errors.check +++ b/test/files/run/repl-errors.check @@ -8,4 +8,7 @@ scala> def foo() { } warning: procedure syntax is deprecated: instead, add `: Unit =` to explicitly declare `foo`'s return type def foo(): Unit +scala> @annotation.nowarn def sshhh() { } +def sshhh(): Unit + scala> :quit diff --git a/test/files/run/repl-errors.scala b/test/files/run/repl-errors.scala index 5fbe994e8afa..cb7f2150465c 100644 --- a/test/files/run/repl-errors.scala +++ b/test/files/run/repl-errors.scala @@ -6,5 +6,6 @@ object Test extends ReplTest { def code = """ '\060' def foo() { } +@annotation.nowarn def sshhh() { } """.trim } diff --git a/test/files/run/repl-no-imports-no-predef.check b/test/files/run/repl-no-imports-no-predef.check index 01751daa0f5f..380a21a41ff7 100644 --- a/test/files/run/repl-no-imports-no-predef.check +++ b/test/files/run/repl-no-imports-no-predef.check @@ -251,7 +251,7 @@ scala> x1 + x2 + x3 val res35: Int = 6 scala> :reset -Resetting interpreter state. +Resetting REPL state. Forgetting this session history: 1 diff --git a/test/files/run/repl-reset.check b/test/files/run/repl-reset.check index 21acb4a8ebf1..d9541c01cc01 100644 --- a/test/files/run/repl-reset.check +++ b/test/files/run/repl-reset.check @@ -15,7 +15,7 @@ scala> x1 + x2 + x3 val res0: Int = 6 scala> :reset -Resetting interpreter state. +Resetting REPL state. Forgetting this session history: val x1 = 1 diff --git a/test/files/run/repl-trim-stack-trace.check b/test/files/run/repl-trim-stack-trace.check index 53609d85dcc5..ee27e0c4cec9 100644 --- a/test/files/run/repl-trim-stack-trace.check +++ b/test/files/run/repl-trim-stack-trace.check @@ -24,9 +24,9 @@ java.lang.Exception ... ??? elided scala> null.asInstanceOf -#partest !java15 +#partest !java15+ java.lang.NullPointerException -#partest java15 +#partest java15+ java.lang.NullPointerException: Cannot throw exception because the return value of "res3()" is null #partest at .lzycompute(:8) diff --git a/test/files/run/richs.check b/test/files/run/richs.check deleted file mode 100644 index 48812eb28939..000000000000 --- a/test/files/run/richs.check +++ /dev/null @@ -1,76 +0,0 @@ - -RichCharTest1: -true -true - -RichIntTest: -10 -11 -12 -13 -0 -0 -10000 -10 -20 -10001 -ffffffff - -RichStringTest1: -s1: abc -s2: abc\txyz\n -s3: abc - xyz -s4: abc - |xyz -s5: abc - #xyz - -RichStringTest2: -s1: abc -s2: abc\txyz\n -s3: abc - xyz -s4: abc - |xyz -s5: abc - #xyz - -RichStringTest3: -s1: abc -s2: abc\txyz\n -s3: abc - xyz -s4: abc - |xyz -s5: abc - #xyz - -RichStringTest4: -s1: abc -s2: abc\txyz\n -s3: abc - xyz -s4: abc -xyz -s5: abc - #xyz - -RichStringTest5: -s1: abc - xyz -s2: abc - xyz -s3: abc - xyz -s4: abc - |xyz -s5: abc -xyz -List(a, b, c, d) -List(a, b, c, d) -List(a, b, c, d) -List(a, b, c, d) -List(a, b, c, d) -List(a, b, c, d) -List(a, b, c, d) diff --git a/test/files/run/richs.scala b/test/files/run/richs.scala deleted file mode 100644 index 560de183ff74..000000000000 --- a/test/files/run/richs.scala +++ /dev/null @@ -1,137 +0,0 @@ -trait RichTest { - val s1 = """abc""" - val s2 = """abc\txyz\n""" - val s3 = """abc - xyz""" - val s4 = """abc - |xyz""" - val s5 = """abc - #xyz""" - def getObjectName: String = { - val cn = this.getClass().getName() - cn.substring(0, cn.length-1) - } - def length[A](it: Iterator[A]) = it.toList.length - def length[A](it: Iterable[A]) = it.toList.length - def run(): Unit -} -object RichCharTest1 extends RichTest { - def run(): Unit = { - println("\n" + getObjectName + ":") - println('1'.asDigit == 1) - println('A'.asDigit == 10) - } -} -// object RichCharTest2 extends RichTest { -// case class C(s: String) { -// private val it = s.iterator -// private var c: Char = _ -// def ch(): Char = c -// def nextch(): Unit = { c = if (it.hasNext) it.next() else ';' } -// def err(msg: String) = println(msg) -// nextch() -// } -// def run { -// println("\n" + getObjectName + ":") -// val c1 = C("x4A;") -// val s1 = xml.Utility.parseCharRef(c1.ch, c1.nextch, c1.err) -// val c2 = C("74;") -// val s2 = xml.Utility.parseCharRef(c2.ch, c2.nextch, c2.err) -// println(s1 == s2) -// } -// } -object RichIntTest extends RichTest { - private val n = 10 - private val m = -2 - def run(): Unit = { - println("\n" + getObjectName + ":") - println(length(0 until n)) - println(length(0 to n)) - println(length(m until n)) - println(length(m to n)) - println(length(n until m)) - println(length(n to m)) - - println(16.toBinaryString) // should be "10000" - println(16.toHexString) // should be "10" - println(16.toOctalString) // should be "20" - - println(65537.toHexString) // should be "10001" - println((-1).toHexString) // should be "ffffffff" - } -} -object RichStringTest1 extends RichTest { - def run(): Unit = { - println("\n" + getObjectName + ":") - println("s1: " + s1) - println("s2: " + s2) - println("s3: " + s3) - println("s4: " + s4) - println("s5: " + s5) - } -} -object RichStringTest2 extends RichTest { - def run(): Unit = { - println("\n" + getObjectName + ":") - Console.print("s1: "); s1.linesIterator foreach println - Console.print("s2: "); s2.linesIterator foreach println - Console.print("s3: "); s3.linesIterator foreach println - Console.print("s4: "); s4.linesIterator foreach println - Console.print("s5: "); s5.linesIterator foreach println - } -} -object RichStringTest3 extends RichTest { - def run(): Unit = { - println("\n" + getObjectName + ":") - println("s1: " + s1.stripLineEnd) - println("s2: " + s2.stripLineEnd) - println("s3: " + s3.stripLineEnd) - println("s4: " + s4.stripLineEnd) - println("s5: " + s5.stripLineEnd) - } -} -object RichStringTest4 extends RichTest { - def run(): Unit = { - println("\n" + getObjectName + ":") - println("s1: " + s1.stripMargin) - println("s2: " + s2.stripMargin) - println("s3: " + s3.stripMargin) - println("s4: " + s4.stripMargin) - println("s5: " + s5.stripMargin) - } -} -object RichStringTest5 extends RichTest { - def run(): Unit = { - println("\n" + getObjectName + ":") - println("s1: " + s3.stripMargin('#')) - println("s2: " + s3.stripMargin('#')) - println("s3: " + s3.stripMargin('#')) - println("s4: " + s4.stripMargin('#')) - println("s5: " + s5.stripMargin('#')) - } -} -object RichStringTest6 extends RichTest { - def run(): Unit = { - println("a:b:c:d".split(':').toList) - println("a.b.c.d".split('.').toList) - println("a$b$c$d".split('$').toList) - println("a^b^c^d".split('^').toList) - println("a\\b\\c\\d".split('\\').toList) - println("a:b:c.d".split(Array(':', '.')).toList) - println("a:b.c$d".split(Array(':', '.', '$')).toList) - } -} -/** xxx */ -object Test { - def main(args: Array[String]): Unit = { - RichCharTest1.run() - //RichCharTest2.run - RichIntTest.run() - RichStringTest1.run() - RichStringTest2.run() - RichStringTest3.run() - RichStringTest4.run() - RichStringTest5.run() - RichStringTest6.run() - } -} diff --git a/test/files/run/sammy_java8.scala b/test/files/run/sammy_java8.scala deleted file mode 100644 index 39118486eddd..000000000000 --- a/test/files/run/sammy_java8.scala +++ /dev/null @@ -1,32 +0,0 @@ -import scala.tools.partest._ - -// java8 version of sammy_poly.scala -object Test extends CompilerTest { - import global._ - - override lazy val units: List[CompilationUnit] = { - // This test itself does not depend on JDK8. - javaCompilationUnits(global)(samSource) ++ - compilationUnits(global)(useSamSource) - } - - private def samSource = """ -// trait F[T, U] { def apply(x: T): U } -public interface F { - U apply(T t); - default void yadayada() { - throw new UnsupportedOperationException("yadayada"); - } -} - """ - - private def useSamSource = """ -class T { - def app[T, U](x: T)(f: F[T, U]): U = f(x) - app(1)(x => List(x)) -} - """ - - // We're only checking we can compile it. - def check(source: String, unit: global.CompilationUnit): Unit = () -} diff --git a/test/files/run/sd760a.scala b/test/files/run/sd760a.scala new file mode 100644 index 000000000000..5db397752df3 --- /dev/null +++ b/test/files/run/sd760a.scala @@ -0,0 +1,12 @@ +import scala.reflect.ClassTag + +object Test { + def main(args: Array[String]): Unit = { + assert(apply[String]("") == classOf[Array[String]]) + assert(apply[Double](1d) == classOf[Array[Double]]) + } + + def apply[@specialized(Double) C: ClassTag](c: C): Class[_] = { + Array(c).getClass + } +} diff --git a/test/files/run/sd760b.scala b/test/files/run/sd760b.scala new file mode 100644 index 000000000000..fae0e9cf8a6d --- /dev/null +++ b/test/files/run/sd760b.scala @@ -0,0 +1,11 @@ +import scala.reflect.ClassTag + +object Test { + def main(args: Array[String]): Unit = { + assert(apply[Double](1d) == classOf[Array[Double]]) + } + + def apply[D <: Double: ClassTag](d: D): Class[_] = { + Array.apply[D](d).getClass + } +} diff --git a/test/files/run/shutdownhooks.javaopts b/test/files/run/shutdownhooks.javaopts deleted file mode 100644 index 9740f07b079b..000000000000 --- a/test/files/run/shutdownhooks.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm \ No newline at end of file diff --git a/test/files/run/shutdownhooks.scala b/test/files/run/shutdownhooks.scala index 518243598f97..1d22ea78380d 100644 --- a/test/files/run/shutdownhooks.scala +++ b/test/files/run/shutdownhooks.scala @@ -1,3 +1,5 @@ +// java: -Dneeds.forked.jvm + object Test { scala.sys.addShutdownHook { // sleep is added here so main#shutdown happens before this hook. diff --git a/test/files/run/splain-tree.check b/test/files/run/splain-tree.check new file mode 100644 index 000000000000..08f373071066 --- /dev/null +++ b/test/files/run/splain-tree.check @@ -0,0 +1,47 @@ +newSource1.scala:28: error: implicit error; +!I e: I1 +i1a invalid because +!I p: I2 +――i2 invalid because + !I p: I3 +――――i3a invalid because + !I p: I4 +――――――i4 invalid because + !I p: I5 +――――――――i5 invalid because + !I p: I6 +――――――――――i6a invalid because + !I p: I7 +――――――――――――i7 invalid because + !I p: I8 +――――――――――――――i8 invalid because + !I p: I9 + +――――――――――i6b invalid because + !I p: I8 +――――――――――――i8 invalid because + !I p: I9 + +――――i3b invalid because + !I p: I4 +――――――i4 invalid because + !I p: I5 +――――――――i5 invalid because + !I p: I6 +――――――――――i6a invalid because + !I p: I7 +――――――――――――i7 invalid because + !I p: I8 +――――――――――――――i8 invalid because + !I p: I9 + +i1b invalid because +!I p: I6 +――i6a invalid because + !I p: I7 +――――i7 invalid because + !I p: I8 +――――――i8 invalid because + !I p: I9 + implicitly[I1] + ^ diff --git a/test/files/run/splain-tree.scala b/test/files/run/splain-tree.scala new file mode 100644 index 000000000000..d660ee85d3f2 --- /dev/null +++ b/test/files/run/splain-tree.scala @@ -0,0 +1,48 @@ +import scala.tools.partest._ + +object Test extends DirectTest { + override def extraSettings: String = "-usejavacp -Vimplicits -Vimplicits-verbose-tree" + + def code: String = "" + + def verboseTree: String = """ +object tpes +{ + trait I1 + trait I2 + trait I3 + trait I4 + trait I5 + trait I6 + trait I7 + trait I8 + trait I9 +} +import tpes._ + +object Tree +{ + implicit def i8(implicit p: I9): I8 = ??? + implicit def i7(implicit p: I8): I7 = ??? + implicit def i6a(implicit p: I7): I6 = ??? + implicit def i6b(implicit p: I8): I6 = ??? + implicit def i5(implicit p: I6): I5 = ??? + implicit def i4(implicit p: I5): I4 = ??? + implicit def i3a(implicit p: I4): I3 = ??? + implicit def i3b(implicit p: I4): I3 = ??? + implicit def i2(implicit p: I3): I2 = ??? + implicit def i1a(implicit p: I2): I1 = ??? + implicit def i1b(implicit p: I6): I1 = ??? + implicitly[I1] +} + """ + + def show(): Unit = { + val global = newCompiler() + + def run(code: String): Unit = + compileString(global)(code.trim) + + run(verboseTree) + } +} diff --git a/test/files/run/splain-truncrefined.check b/test/files/run/splain-truncrefined.check new file mode 100644 index 000000000000..b940efbf3678 --- /dev/null +++ b/test/files/run/splain-truncrefined.check @@ -0,0 +1,4 @@ +newSource1.scala:7: error: type mismatch; + D|C {...} + f(new D { type X = C; type Y = D }) + ^ diff --git a/test/files/run/splain-truncrefined.scala b/test/files/run/splain-truncrefined.scala new file mode 100644 index 000000000000..2be99a6350bb --- /dev/null +++ b/test/files/run/splain-truncrefined.scala @@ -0,0 +1,28 @@ +import scala.tools.partest._ + +object Test extends DirectTest { + override def extraSettings: String = "-usejavacp -Vimplicits -Vtype-diffs -Vimplicits-max-refined 5" + + def code: String = "" + + def truncrefined: String = """ +object TruncRefined +{ + class C + trait D + type CAux[A] = C { type X = C; type Y = D } + def f(arg1: CAux[D]) = ??? + f(new D { type X = C; type Y = D }) +} + + """ + + def show(): Unit = { + val global = newCompiler() + + def run(code: String): Unit = + compileString(global)(code.trim) + + run(truncrefined) + } +} diff --git a/test/files/run/splain.check b/test/files/run/splain.check new file mode 100644 index 000000000000..60b373684230 --- /dev/null +++ b/test/files/run/splain.check @@ -0,0 +1,114 @@ +newSource1.scala:13: error: implicit error; +!I e: II +ImplicitChain.g invalid because +!I impPar3: I1 +⋮ +――ImplicitChain.i1 invalid because + !I impPar7: I3 + implicitly[II] + ^ +newSource1.scala:6: error: type mismatch; + L|R + f(new L) + ^ +newSource1.scala:7: error: implicit error; +!I e: F[Arg] + implicitly[F[Arg]] + ^ +newSource1.scala:4: error: implicit error; +!I ec: ExecutionContext + Cannot find an implicit ExecutionContext. You might add + an (implicit ec: ExecutionContext) parameter to your method. + + The ExecutionContext is used to configure how and on which + thread pools asynchronous tasks (such as Futures) will run, + so the specific ExecutionContext that is selected is important. + + If your application does not define an ExecutionContext elsewhere, + consider using Scala's global ExecutionContext by defining + the following: + + implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global + + long + ^ +newSource1.scala:10: error: implicit error; +!I e: String +f invalid because +!I impPar4: + List[ + ( + VeryLongTypeName :::: + VeryLongTypeName :::: + VeryLongTypeName :::: + VeryLongTypeName + ) + :::: + (Short :::: Short) :::: + ( + VeryLongTypeName :::: + VeryLongTypeName :::: + VeryLongTypeName :::: + VeryLongTypeName + ) + :::: + VeryLongTypeName :::: + VeryLongTypeName :::: + VeryLongTypeName :::: + VeryLongTypeName + ] + (No implicit view available from Int => InfixBreak.T2.) + + implicitly[String] + ^ +newSource1.scala:11: error: implicit error; +!I e: C1[T3[T1[List[String], ?], T2[Id, C4, ?], ?]] + implicitly[C1[T3]] + ^ +newSource1.scala:9: error: implicit error; +!I e: F.Aux[C, D] +Aux.f invalid because +!I impPar10: C + implicitly[F.Aux[C, D]] + ^ +newSource1.scala:11: error: type mismatch; + A with B with E|C with F| {type X = Int|String; type Y = String; type Z = |String} + f(x) + ^ +newSource1.scala:25: error: type mismatch; + C.X.Y.T|B.X.Y.T + f(x: C.X.Y.T) + ^ +newSource1.scala:6: error: type mismatch; + Int|(=> A) => B + f(1: Int) + ^ +newSource1.scala:3: error: type mismatch; + String|Tuple1[String] + val a: Tuple1[String] = "Tuple1": String + ^ +newSource1.scala:7: error: implicit error; +!I e: a.type *** b.type + implicitly[a.type *** b.type] + ^ +newSource1.scala:8: error: implicit error; +!I e: a.type *** b.type + implicitly[a.type *** b.type] + ^ +newSource1.scala:6: error: implicit error; +!I e: a.type *** b.type + implicitly[a.type *** b.type] + ^ +newSource1.scala:5: error: implicit error; +!I ev: Ordering[Object] + No implicit Ordering[Object] found to build a SortedSet[Object]. You may want to upcast to a Set[Int] first by calling `unsorted`. + +Ordering.ordered invalid because +!I asComparable: Object => Comparable[_$2] + No implicit view available from Object => Comparable[_ >: Object]. + +⋮ +Ordering.comparatorToOrdering invalid because +!I cmp: Comparator[Object] + ms.map(_ => o) + ^ diff --git a/test/files/run/splain.scala b/test/files/run/splain.scala new file mode 100644 index 000000000000..5c851b76ba9a --- /dev/null +++ b/test/files/run/splain.scala @@ -0,0 +1,225 @@ +import scala.tools.partest._ + +object Test +extends DirectTest +{ + override def extraSettings: String = "-usejavacp -Vimplicits -Vtype-diffs" + + def code: String = "" + + def chain: String = """ +object ImplicitChain +{ + trait I1 + trait I2 + trait I3 + trait I4 + trait II + implicit def i1(implicit impPar7: I3): I1 = ??? + implicit def i2a(implicit impPar8: I3): I2 = ??? + implicit def i2b(implicit impPar8: I3): I2 = ??? + implicit def i4(implicit impPar9: I2): I4 = ??? + implicit def g(implicit impPar3: I1, impPar1: I4): II = ??? + implicitly[II] +} + """ + + def foundReq: String = """ +object FoundReq +{ + class L + type R + def f(r: R): Int = ??? + f(new L) +} + """ + + def bounds: String = """ +object Bounds +{ + trait Base + trait Arg + trait F[A] + implicit def g[A <: Base, B]: F[A] = ??? + implicitly[F[Arg]] +} + """ + + def longAnnotationMessage: String = """ +object Long +{ + def long(implicit ec: concurrent.ExecutionContext): Unit = ??? + long +} + """ + + def longInfix: String = """ +object InfixBreak +{ + type ::::[A, B] + trait VeryLongTypeName + trait Short + type T1 = VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName :::: + VeryLongTypeName + type T2 = T1 :::: (Short :::: Short) :::: T1 :::: T1 + implicit def f(implicit impPar4: List[T2]): String = ??? + implicitly[String] +} + """ + + def deeplyNestedHole: String = """ +object DeepHole +{ + trait C1[F[_]] + trait C2[F[_], G[_], A] + trait C3[A, B] + trait C4[A] + type Id[A] = A + type T1[X] = C3[List[String], X] + type T2[Y] = C2[Id, C4, Y] + type T3[Z] = C2[T1, T2, Z] + implicitly[C1[T3]] +} + """ + + def auxType: String = """ +object Aux +{ + trait C + trait D + trait F + object F { type Aux[A, B] = F { type X = A; type Y = B } } + implicit def f[A, B](implicit impPar10: C): F { type X = A; type Y = B } = + ??? + implicitly[F.Aux[C, D]] +} + """ + + def refined: String = """ +object Refined +{ + trait A + trait B + trait C + trait D + trait E + trait F + def f(a: A with B with C { type Y = String; type X = String; type Z = String }): Unit = ??? + val x: B with E with A with F { type X = Int; type Y = String } = ??? + f(x) +} + """ + + def disambiguateQualified: String = """ +object A +{ + object B + { + object X + { + object Y + { + type T + } + } + } + object C + { + object X + { + object Y + { + type T + } + } + } + def f(a: B.X.Y.T): Unit = () + val x: C.X.Y.T = ??? + f(x: C.X.Y.T) +} + """ + + def bynameParam: String = """ +object Foo +{ + type A + type B + def f(g: (=> A) => B): Unit = () + f(1: Int) +} + """ + + def tuple1: String = """ +object Tup1 +{ + val a: Tuple1[String] = "Tuple1": String +} + """ + + def singleType: String = """ +object SingleImp +{ + class ***[A, B] + val a = 1 + val b = 2 + + implicitly[a.type *** b.type] +} + """ + + def singleTypeInFunction: String = """ +object SingleImp +{ + class ***[A, B] + def fn(): Unit = { + val a = 1 + val b = 2 + + implicitly[a.type *** b.type] + } +} + """ + + def singleTypeWithFreeSymbol: String = """ +object SingleImp +{ + class ***[A, B] + def fn[A, B](a: A, b: B) = { + + implicitly[a.type *** b.type] + } +} + """ + + def parameterAnnotation: String = """ + import collection.{mutable => m, immutable => i} + object Test { + val o = new Object + val ms = m.SortedSet(1,2,3) + ms.map(_ => o) + } + """ + + def show(): Unit = { + val global = newCompiler() + + def run(code: String): Unit = + compileString(global)(code.trim) + + run(chain) + run(foundReq) + run(bounds) + run(longAnnotationMessage) + run(longInfix) + run(deeplyNestedHole) + run(auxType) + run(refined) + run(disambiguateQualified) + run(bynameParam) + run(tuple1) + run(singleType) + run(singleTypeInFunction) + run(singleTypeWithFreeSymbol) + run(parameterAnnotation) + } +} diff --git a/test/files/run/stream-gc.javaopts b/test/files/run/stream-gc.javaopts deleted file mode 100644 index 58ba19b41eff..000000000000 --- a/test/files/run/stream-gc.javaopts +++ /dev/null @@ -1 +0,0 @@ --Xmx5M -Xms5M diff --git a/test/files/run/stream-gc.scala b/test/files/run/stream-gc.scala index 699ab621de0b..18d8b972c00c 100644 --- a/test/files/run/stream-gc.scala +++ b/test/files/run/stream-gc.scala @@ -1,3 +1,5 @@ +// java: -Xmx5M -Xms5M + import scala.collection.immutable._ object Test extends App { diff --git a/test/files/run/string-switch-pos.check b/test/files/run/string-switch-pos.check index 6b292d0a3808..805f5a3143bd 100644 --- a/test/files/run/string-switch-pos.check +++ b/test/files/run/string-switch-pos.check @@ -1,11 +1,11 @@ [[syntax trees at end of patmat]] // newSource1.scala -[0:187]package [0:0] { - [0:187]class Switch extends [13:187][187]scala.AnyRef { - [187]def (): [13]Switch = [187]{ - [187][187][187]Switch.super.(); +[0:216]package [0:0] { + [0:216]class Switch extends [13:216][216]scala.AnyRef { + [216]def (): [13]Switch = [216]{ + [216][216][216]Switch.super.(); [13]() }; - [17:185]def switch([28:37]s: [31:37], [39:52]cond: [45:52]): [21]Int = [56:57]{ + [17:214]def switch([28:37]s: [31:37], [39:52]cond: [45:52]): [21]Int = [56:57]{ [56:57]case val x1: [56]String = [56:57]s; [56:57][56:57]x1 match { [56:57]case [75:81]"AaAa" => [93:94]1 @@ -14,6 +14,7 @@ [151:152]3 else [180:181]4 + [56:57]case [56:57]([191:197]"CcCc"| [200:205]"Cc2") => [209:210]5 [56:57]case [56:57]_ => [56:57]throw [56:57][56:57][56:57]new [56:57]MatchError([56:57]x1) } } @@ -21,42 +22,44 @@ } [[syntax trees at end of cleanup]] // newSource1.scala -[0:187]package [0:0] { - [0:187]class Switch extends [13:187][13:187]Object { - [17:185]def switch([28:37]s: [31:37], [39:52]cond: [45:52]): [21]Int = [56:57]{ +[0:216]package [0:0] { + [0:216]class Switch extends [13:216][13:216]Object { + [17:214]def switch([28:37]s: [31:37], [39:52]cond: [45:52]): [21]Int = [56:57]{ [56:57]case val x1: [56]String = [56:57]s; [56:57]{ [56:139][56:57]if ([56][56]x1.eq([56]null)) [56]0 else [56][56]x1.hashCode() match { + [56:57]case [56]67506 => [56:57]if ([56][56][56]"Cc2".equals([56]x1)) + [56][56]case4() + else + [56][56]defaultCase1() [75:81]case [56]2031744 => [75:81]if ([75][75][75]"AaAa".equals([75]x1)) - [75][75]case1() + [93:94][75]matchEnd1([93:94]1) else - [56][56]matchEnd2() + [56][56]defaultCase1() [133:139]case [56]2062528 => [133:139]if ([133][133][133]"BbBb".equals([133]x1)) - [133][133]case3() + [143:181][133]matchEnd1([143:181]if ([143:147]cond) + [151:152]3 + else + [180:181]4) + else + [56][56]defaultCase1() + [56:57]case [56]2093312 => [56:57]if ([56][56][56]"CcCc".equals([56]x1)) + [56][56]case4() else - [56][56]matchEnd2() + [56][56]defaultCase1() [104:110]case [56]3003444 => [104:110]if ([104][104][104]"asdf".equals([104]x1)) - [104][104]case2() + [122:123][104]matchEnd1([122:123]2) else - [56][56]matchEnd2() - [56]case [56]_ => [56][56]matchEnd2() - }; - [56]case1(){ - [56][56]matchEnd1([93:94]1) + [56][56]defaultCase1() + [56]case [56]_ => [56][56]defaultCase1() }; - [56]case2(){ - [56][56]matchEnd1([122:123]2) - }; - [56]case3(){ - [56][56]matchEnd1([143:181]if ([143:147]cond) - [151:152]3 - else - [180:181]4) + [56]case4(){ + [56][56]matchEnd1([209:210]5) }; - [56]matchEnd2(){ + [56]defaultCase1(){ [56][56]matchEnd1([56:57]throw [56:57][56:57][56:57]new [56:57]MatchError([56:57]x1)) }; [56]matchEnd1(x$1: [NoPosition]Int){ @@ -64,8 +67,8 @@ } } }; - [187]def (): [13]Switch = [187]{ - [187][187][187]Switch.super.(); + [216]def (): [13]Switch = [216]{ + [216][216][216]Switch.super.(); [13]() } } diff --git a/test/files/run/string-switch-pos.scala b/test/files/run/string-switch-pos.scala index a75208046391..db093bc93a55 100644 --- a/test/files/run/string-switch-pos.scala +++ b/test/files/run/string-switch-pos.scala @@ -10,9 +10,10 @@ object Test extends DirectTest { | case "asdf" => 2 | case "BbBb" if cond => 3 | case "BbBb" => 4 + | case "CcCc" | "Cc2" => 5 | } |} """.stripMargin.trim override def show(): Unit = Console.withErr(Console.out) { super.compile() } -} \ No newline at end of file +} diff --git a/test/files/run/t11402.check b/test/files/run/t11402.check index 238c777f5d97..2ccfa8be1b9e 100644 --- a/test/files/run/t11402.check +++ b/test/files/run/t11402.check @@ -1,12 +1,12 @@ scala> def f = { val x = 'abc - val x = 'abc - ^ -On line 2: warning: symbol literal is deprecated; use Symbol("abc") instead val y = x.toString y } + val x = 'abc + ^ +On line 2: warning: symbol literal is deprecated; use Symbol("abc") instead def f: String scala> :quit diff --git a/test/files/run/t11534b.scala b/test/files/run/t11534b.scala new file mode 100644 index 000000000000..75e835bed9a3 --- /dev/null +++ b/test/files/run/t11534b.scala @@ -0,0 +1,24 @@ +object Test { + case class O(i: Int) { + class A + class B extends A { + def bOuter = O.this + } + trait C { + def cOuter = O.this + } + class D extends o2.B with C + } + val o1 = new O(1); + val o2 = new O(2); + def pat1(a: Test.o1.C) = a match { + case b: Test.o1.B => + assert(b.bOuter eq Test.o1, + s"expected ${o1} as outer of value conforming to pattern `b: Test.o1.B`, but got ${b.bOuter}") + case _ => + + } + def main(args: Array[String]): Unit = { + pat1(new o1.D) + } +} diff --git a/test/files/run/t11534c.scala b/test/files/run/t11534c.scala new file mode 100644 index 000000000000..a1fbaf0d72e6 --- /dev/null +++ b/test/files/run/t11534c.scala @@ -0,0 +1,135 @@ +// scalac: -unchecked +import scala.util.Try + +object Test { + class O(val i: Int) { + class A { + val aOuter = i + } + + class B1 extends A { + val b1Outer = i + } + } + class M(i: Int) extends O(i) { + class B2 extends m2.A { + val b2Outer = i + } + + def pat1(a: M.this.A) = a match { + case b: M.this.B1 => // can elide outer check, (a : m1.A) && (a : O#B1) implies (a : m1.B1) + assertOuter(m1.i, b.b1Outer) + true + case _ => + false + } + def pat2(a: m2.A) = a match { + case b: M.this.B2 => // needs runtime outer check + assertOuter(m1.i, b.b2Outer) + true + case _ => + false + } + def pat3(a: M.this.B1) = a match { + case b: M.this.A => // can elide outer check, (a : m1.B1) && (a : O#A) implies (a : m1.B1) + assertOuter(m1.i, b.aOuter) + true + case _ => + false + } + def pat4(a: M.this.B2) = a match { + case b: m2.A => // can elide outer check, (a : m1.B2) implies (a : m2.A) + assertOuter(m2.i, b.aOuter) + true + case _ => + false + } + } + + val m1 = new M(1); + val m2 = new M(2); + + def pat1(a: m1.A) = a match { + case b: m1.B1 => // can elide outer check, (a : m1.A) && (a : O#B1) implies (a : m1.B1) + assertOuter(m1.i, b.b1Outer) + true + case _ => + false + } + def pat2(a: m2.A) = a match { + case b: m1.B2 => // needs runtime outer check + assertOuter(m1.i, b.b2Outer) + true + case _ => + false + } + def pat3(a: m1.B1) = a match { + case b: m1.A => // can elide outer check, (a : m1.B1) && (a : O#A) implies (a : m1.B1) + assertOuter(m1.i, b.aOuter) + true + case _ => + false + } + def pat4(a: m1.B2) = a match { + case b: m2.A => // can elide outer check, (a : m1.B2) implies (a : m2.A) + assertOuter(m2.i, b.aOuter) + true + case _ => + false + } + + def pat5(a: M#B2) = a match { + case b: m2.A => // can elide outer check, (a : A#B2) implies (a : m2.A) + assertOuter(m2.i, b.aOuter) + true + case _ => + false + } + + trait ScalaProvider { def loader: Int } + type ScalaProvider2 = { def loaderLibraryOnly: Int } + import scala.language.reflectiveCalls + + def cb1400(provider: ScalaProvider) = try { + provider match { + case p: ScalaProvider2 @unchecked => p.loaderLibraryOnly + } + } catch { + case _: NoSuchMethodException => provider.loader + } + + def assertOuter(expected: Int, actual: Int): Unit = { + if (expected != actual) throw WrongOuter(expected, actual) + } + case class WrongOuter(expected: Int, actual: Int) extends RuntimeException(s"expected: $expected, actual: $actual") + + def main(args: Array[String]): Unit = { + assert(pat1(new m1.B1)) + assert(m1.pat1(new m1.B1)) + assert(Try(pat1((new m2.B1).asInstanceOf[m1.B1])).failed.get == WrongOuter(m1.i, m2.i)) + assert(Try(m1.pat1((new m2.B1).asInstanceOf[m1.B1])).failed.get == WrongOuter(m1.i, m2.i)) + + assert(!pat2(new m2.B2)) + assert(!m1.pat2(new m2.B2)) + assert(pat2(new m1.B2)) + assert(m1.pat2(new m1.B2)) + + assert(pat3(new m1.B1)) + assert(m1.pat3(new m1.B1)) + assert(Try(pat3((new m2.B1).asInstanceOf[m1.B1])).failed.get == WrongOuter(m1.i, m2.i)) + assert(Try(m1.pat3((new m2.B1).asInstanceOf[m1.B1])).failed.get == WrongOuter(m1.i, m2.i)) + + assert(pat4(new m1.B2)) + assert(m1.pat4(new m1.B2)) + assert(pat4((new m2.B2).asInstanceOf[m1.B2])) + assert(m1.pat4((new m2.B2).asInstanceOf[m1.B2])) + + assert(pat5(new m1.B2)) + assert(pat5(new m2.B2)) + + class SP1 extends ScalaProvider { def loader = 1 } + class SP2 extends ScalaProvider { def loader = 1; def loaderLibraryOnly = 2 } + assert(cb1400(new SP1()) == 1) + assert(cb1400(new SP2()) == 2) + } +} diff --git a/test/files/run/t11882-class-cast.scala b/test/files/run/t11882-class-cast.scala new file mode 100644 index 000000000000..59221be93fe6 --- /dev/null +++ b/test/files/run/t11882-class-cast.scala @@ -0,0 +1,8 @@ +object Test { + def test[T <: AnyRef: reflect.ClassTag](t: T) = Array(t) + def main(args: Array[String]): Unit = { + // was: java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; + val x: Array[String] = test[String]("x") + assert(x(0) == "x") + } +} diff --git a/test/files/run/t11924.check b/test/files/run/t11924.check new file mode 100644 index 000000000000..edee5862ce53 --- /dev/null +++ b/test/files/run/t11924.check @@ -0,0 +1,12 @@ +B1-a +B1-b +B2 +A +B3-a +B3-b +A +B4-a +B4-b +B5-a +B5-b +A diff --git a/test/files/run/t11924.scala b/test/files/run/t11924.scala new file mode 100644 index 000000000000..56211a1bffb2 --- /dev/null +++ b/test/files/run/t11924.scala @@ -0,0 +1,93 @@ +package pkg { + class A { + protected def f(): Unit = println("A") + } +} + +import pkg.A + +trait B1 { self: A => + private[this] var go = true + override def f(): Unit = if (go) { + go = false + println("B1-a") + self.f() + } else + println("B1-b") +} + +trait B2 extends A { + override def f(): Unit = { + println("B2") + super.f() + } +} + +trait B3 extends A { self: A => + private[this] var go = true + override def f(): Unit = if (go) { + go = false + println("B3-a") + self.f() + } else { + println("B3-b") + super.f() + } +} + +class C1 extends A with B1 +class C2 extends A with B2 +class C3 extends A with B3 + +// test case from pull request comment + +package l1 { + class I { + class A { + protected def f(): Unit = println("A") + } + } + object O extends I +} + +package l2 { + class I { + trait B4 { self: l1.O.A => + private[this] var go = true + override def f(): Unit = if (go) { + go = false + println("B4-a") + self.f() + } else { + println("B4-b") + } + } + + trait B5 extends l1.O.A { self: l1.O.A => + private[this] var go = true + override def f(): Unit = if (go) { + go = false + println("B5-a") + self.f() + } else { + println("B5-b") + super.f() + } + } + } + object O extends I +} + +class C4 extends l1.O.A with l2.O.B4 +class C5 extends l1.O.A with l2.O.B5 + + +object Test { + def main(args: Array[String]): Unit = { + new C1().f() + new C2().f() + new C3().f() + new C4().f() + new C5().f() + } +} diff --git a/test/files/run/t12038a.check b/test/files/run/t12038a.check new file mode 100644 index 000000000000..13d7a9d9b34d --- /dev/null +++ b/test/files/run/t12038a.check @@ -0,0 +1,3 @@ +[BuilderType <: p1.J_1.AbstractMessageLite.Builder[BuilderType]]Object { + def (): p1.J_1.AbstractMessageLite[BuilderType] +} diff --git a/test/files/run/t12038a/J_1.java b/test/files/run/t12038a/J_1.java new file mode 100644 index 000000000000..37f545e5a6fe --- /dev/null +++ b/test/files/run/t12038a/J_1.java @@ -0,0 +1,11 @@ +package p1; + +public class J_1 { + public abstract static class AbstractMessageLite< + BuilderType extends AbstractMessageLite.Builder> { + + public abstract static class Builder>{ + + } + } +} diff --git a/test/files/run/t12038a/Test.scala b/test/files/run/t12038a/Test.scala new file mode 100644 index 000000000000..5a62349415bc --- /dev/null +++ b/test/files/run/t12038a/Test.scala @@ -0,0 +1,7 @@ +object Test { + def main(args: Array[String]): Unit = { + import reflect.runtime.universe._ + val m = scala.reflect.runtime.currentMirror + println(m.staticClass("p1.J_1.AbstractMessageLite").info.toString) // was cyclic error + } +} diff --git a/test/files/run/t12038b.check b/test/files/run/t12038b.check new file mode 100644 index 000000000000..c44e84a62530 --- /dev/null +++ b/test/files/run/t12038b.check @@ -0,0 +1 @@ +(x$1: p1.J_1.O[Object]#$I[String]): Unit diff --git a/test/files/run/t12038b/J_1.java b/test/files/run/t12038b/J_1.java new file mode 100644 index 000000000000..32c240ba98bd --- /dev/null +++ b/test/files/run/t12038b/J_1.java @@ -0,0 +1,21 @@ +package p1; + +public class J_1 { + public static abstract class O { + // non static, runtime reflection should use the generic owner type + // in the type signature below. + // + // also doing for static inner classes runs into cyclic errors (see t12038a.scala) + // in the current implementation of runtime reflection. + // + // This is fine as Java rejects the type selections in `notValidJava` below with: + // + // "cannot select a static class from a parameterized type" + public abstract class I {} + + public abstract static class J {} + } + static void test(O.I oi) {} + + // static void notValidJava(O.J oj) {} +} diff --git a/test/files/run/t12038b/Test.scala b/test/files/run/t12038b/Test.scala new file mode 100644 index 000000000000..a2b2220ad4cb --- /dev/null +++ b/test/files/run/t12038b/Test.scala @@ -0,0 +1,7 @@ +object Test { + def main(args: Array[String]): Unit = { + import reflect.runtime.universe._ + val m = scala.reflect.runtime.currentMirror + println(m.staticClass("p1.J_1").companion.info.decl(TermName("test")).asMethod.info.toString) + } +} diff --git a/test/files/run/t12071.scala b/test/files/run/t12071.scala new file mode 100644 index 000000000000..5950647a1526 --- /dev/null +++ b/test/files/run/t12071.scala @@ -0,0 +1,28 @@ +// scalac: -Werror -Xlint -Xsource:3 + +class C { + def `c c`(n: Int): Int = n + 1 +} + +// backticked operator is candidate for multiline infix, +// but backticked value is an innocent bystander. +// +class t12071 { + def c: C = new C + def i: Int = 42 + def `n n`: Int = 27 + def f = c + `c c` i + def g = i + + `n n` + def basic = + 1 + + 2 +} + +object Test extends App { + val t = new t12071 + assert(t.f == 43) + assert(t.g == 69) + assert(t.basic == 3) +} diff --git a/test/files/run/t12276.check b/test/files/run/t12276.check index 302c6ff9eb63..5de7a7314602 100644 --- a/test/files/run/t12276.check +++ b/test/files/run/t12276.check @@ -82,5 +82,15 @@ 00000000 1b 5b 33 35 6d |.[35m| +00000000 73 63 61 6c 61 3e 20 1b 5b 30 6d 22 5c 75 43 41 |scala> .[0m"\uCA| +00000010 46 45 20 63 61 66 66 c3 a8 22 |FE caff.."| + +00000000 76 61 6c 20 1b 5b 31 6d 1b 5b 33 34 6d 72 65 73 |val .[1m.[34mres| +00000010 36 1b 5b 30 6d 3a 20 1b 5b 31 6d 1b 5b 33 32 6d |6.[0m: .[1m.[32m| +00000020 53 74 72 69 6e 67 1b 5b 30 6d 20 3d 20 ec ab be |String.[0m = ...| +00000030 20 63 61 66 66 c3 a8 | caff..| + +00000000 1b 5b 33 35 6d |.[35m| + 00000000 73 63 61 6c 61 3e 20 1b 5b 30 6d 3a 71 75 69 74 |scala> .[0m:quit| diff --git a/test/files/run/t12276.scala b/test/files/run/t12276.scala index 94425242fb69..50ef6b0edc5e 100644 --- a/test/files/run/t12276.scala +++ b/test/files/run/t12276.scala @@ -3,7 +3,7 @@ import scala.tools.nsc.interpreter.shell.{ILoop, ShellConfig} import scala.tools.partest.{hexdump, ReplTest} object Test extends ReplTest { - def code = """ + def code = s""" |java.nio.CharBuffer.allocate(5) |java.nio.CharBuffer.allocate(6) |class C @@ -12,6 +12,7 @@ object Test extends ReplTest { |classOf[C].toString + esc + "[3z" |classOf[C].toString + esc + "[3!" |classOf[C].toString + scala.io.AnsiColor.YELLOW + |"${"\\"}uCAFE caffè" |""".stripMargin override protected def shellConfig(testSettings: Settings) = diff --git a/test/files/run/t12290.check b/test/files/run/t12290.check new file mode 100644 index 000000000000..00d93b3657dd --- /dev/null +++ b/test/files/run/t12290.check @@ -0,0 +1,61 @@ +==== +A text + +==== + + +

Hello, world

+ + + +==== +SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB" +WHERE "CITY" = 'INDIANAPOLIS' +ORDER BY "EMP_ID", "LAST_NAME"; + +==== + + +

Hello, world

+ + + +==== + + +

Hello, world

+ + + +==== + + +

Hello, world

+ + + + +==== + + +

Hello , world

+ + + +==== + this line has 4 tabs before it + this line has 5 spaces before it and space after it + this line has 2 tabs and 3 spaces before it +  this line has 6 spaces before it + +==== +String text = """ + A text block inside a text block +"""; + +==== +foo bar +baz +==== + +==== diff --git a/test/files/run/t12290/Test.scala b/test/files/run/t12290/Test.scala new file mode 100644 index 000000000000..13b01b51478c --- /dev/null +++ b/test/files/run/t12290/Test.scala @@ -0,0 +1,30 @@ +// javaVersion: 15+ +/* Using `valueOf` is a way to check that the Java string literals were properly + * parsed, since the parsed value is what the Scala compiler will use when + * resolving the singleton types + */ +object Test extends App { + println("====") + println(valueOf[TextBlocks.aText.type]) + println("====") + println(valueOf[TextBlocks.html1.type]) + println("====") + println(valueOf[TextBlocks.query.type]) + println("====") + println(valueOf[TextBlocks.html2.type]) + println("====") + println(valueOf[TextBlocks.html3.type]) + println("====") + println(valueOf[TextBlocks.html4.type]) + println("====") + println(valueOf[TextBlocks.html5.type]) + println("====") + println(valueOf[TextBlocks.mixedIndents.type]) + println("====") + println(valueOf[TextBlocks.code.type]) + println("====") + println(valueOf[TextBlocks.simpleString.type]) + println("====") + println(valueOf[TextBlocks.emptyString.type]) + println("====") +} diff --git a/test/files/run/t12290/TextBlocks.java b/test/files/run/t12290/TextBlocks.java new file mode 100644 index 000000000000..e1928e74c971 --- /dev/null +++ b/test/files/run/t12290/TextBlocks.java @@ -0,0 +1,78 @@ +// javaVersion: 15+ +class TextBlocks { + + final static String aText = """ + A text + """; + + final static String html1 = """ + + +

Hello, world

+ + + """; + + // quote characters are unescaped + final static String query = """ + SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB" + WHERE "CITY" = 'INDIANAPOLIS' + ORDER BY "EMP_ID", "LAST_NAME"; + """; + + // incidental trailing spaces + final static String html2 = """ + + +

Hello, world

+ + + """; + + // trailing delimiter influences + final static String html3 = """ + + +

Hello, world

+ + + """; + + // blank line does not affect + final static String html4 = """ + + +

Hello, world

+ + + + """; + + // escape sequences + final static String html5 = """ + \n + \ +

Hello\s,\tworld

+ + + """; + + // mixed indentation + final static String mixedIndents = """ + \s this line has 4 tabs before it + this line has 5 spaces before it and space after it \u0020 \u000C\u0020 \u001E + this line has 2 tabs and 3 spaces before it +\u0020 \u000C\u0020 \u001E this line has 6 spaces before it + """; + + final static String code = + """ + String text = \""" + A text block inside a text block + \"""; + """; + + final static String simpleString = "foo\tbar\nbaz"; + + final static String emptyString = ""; +} diff --git a/test/files/run/t12292.check b/test/files/run/t12292.check new file mode 100644 index 000000000000..0d3abd20aa61 --- /dev/null +++ b/test/files/run/t12292.check @@ -0,0 +1,30 @@ + +scala> import scala.annotation.nowarn +import scala.annotation.nowarn + +scala> scala.#::.unapply(Stream(1)) + ^ + warning: method unapply in object #:: is deprecated (since 2.13.0): Prefer LazyList instead + ^ + warning: value Stream in package scala is deprecated (since 2.13.0): Use LazyList instead of Stream +val res0: Option[(Int, Stream[Int])] = Some((1,Stream())) + +scala> scala.#::.unapply(Stream(1)): @nowarn +val res1: Option[(Int, Stream[Int])] @scala.annotation.nowarn = Some((1,Stream())) + +scala> (scala.#::.unapply(Stream(1)): @nowarn) +val res2: Option[(Int, Stream[Int])] @scala.annotation.nowarn = Some((1,Stream())) + +scala> scala.#::.unapply(Stream(1)): @inline + ^ + warning: method unapply in object #:: is deprecated (since 2.13.0): Prefer LazyList instead + ^ + warning: value Stream in package scala is deprecated (since 2.13.0): Use LazyList instead of Stream + ^ + warning: type Stream in package scala is deprecated (since 2.13.0): Use LazyList instead of Stream +val res3: Option[(Int, Stream[Int])] @inline = Some((1,Stream())) + +scala> (scala.#::.unapply(Stream(1)): @nowarn).isEmpty +val res4: Boolean = false + +scala> :quit diff --git a/test/files/run/t12292.scala b/test/files/run/t12292.scala new file mode 100644 index 000000000000..83aa11272931 --- /dev/null +++ b/test/files/run/t12292.scala @@ -0,0 +1,14 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + override def extraSettings = "-deprecation" + + def code = """ +import scala.annotation.nowarn +scala.#::.unapply(Stream(1)) +scala.#::.unapply(Stream(1)): @nowarn +(scala.#::.unapply(Stream(1)): @nowarn) +scala.#::.unapply(Stream(1)): @inline +(scala.#::.unapply(Stream(1)): @nowarn).isEmpty +""" +} diff --git a/test/files/run/t12312.scala b/test/files/run/t12312.scala new file mode 100644 index 000000000000..3ad1c4542b75 --- /dev/null +++ b/test/files/run/t12312.scala @@ -0,0 +1,25 @@ +class A { object X } + +class C { + val a, b = new A; import a.X + class D { + def isInstanceOf_aX(z: AnyRef) = z.isInstanceOf[X.type] + class E { + def isInstanceOf_aX(z: AnyRef) = z.isInstanceOf[X.type] + } + } +} + +object Test extends C { + def main(args: Array[String]): Unit = { + val d = new D() + assert(d.isInstanceOf_aX(a.X)) + assert(!d.isInstanceOf_aX(b.X)) + assert(!d.isInstanceOf_aX(new Object)) + + val e = new d.E() + assert(e.isInstanceOf_aX(a.X)) + assert(!e.isInstanceOf_aX(b.X)) + assert(!e.isInstanceOf_aX(new Object)) + } +} diff --git a/test/files/run/t12348.scala b/test/files/run/t12348.scala new file mode 100644 index 000000000000..fdbb4d9465df --- /dev/null +++ b/test/files/run/t12348.scala @@ -0,0 +1,9 @@ +// javaVersion: 11+ + +object Test { + def main(args: Array[String]): Unit = { + val a = new Array[Object](1) + val h = java.lang.invoke.MethodHandles.arrayElementVarHandle(a.getClass) + val r = h.setVolatile(a, 0, "foo") // important: no expected type + } +} diff --git a/test/files/run/t12380/A.java b/test/files/run/t12380/A.java new file mode 100644 index 000000000000..1cdbd7e83bbf --- /dev/null +++ b/test/files/run/t12380/A.java @@ -0,0 +1,28 @@ +// filter: unchecked + +package p; + +public class A { + public static interface I { + public I w(); + } + + public static interface J> extends I { + @Override public R w(); + } + + public static interface K extends I { + @Override public K w(); + + public default String mK() { return "K"; } + } + + /* package-private */ static class B> implements J { + @Override public R w() { return (R) this; } + } + + public static class C> extends B implements J { } + + // OK in Java, also OK in Scala + public static class Test extends C implements K { } +} diff --git a/test/files/run/t12380/Test.scala b/test/files/run/t12380/Test.scala new file mode 100644 index 000000000000..abab74cde7da --- /dev/null +++ b/test/files/run/t12380/Test.scala @@ -0,0 +1,7 @@ +class Test extends p.A.C[Test] with p.A.K +object Test { + def main(args: Array[String]): Unit = { + assert((new Test).w.mK == "K") + assert((new p.A.Test).w.mK == "K") + } +} diff --git a/test/files/run/t12403.scala b/test/files/run/t12403.scala new file mode 100644 index 000000000000..76342193e786 --- /dev/null +++ b/test/files/run/t12403.scala @@ -0,0 +1,9 @@ + +object Test extends App { + val xs = + Array.empty[Double] + val ys = + Array(0.0) + assert(xs.intersect(ys).getClass.getComponentType == classOf[Double]) + assert(Array.empty[Double].intersect(Array(0.0)).getClass.getComponentType == classOf[Double]) +} diff --git a/test/files/run/t12405.check b/test/files/run/t12405.check new file mode 100644 index 000000000000..a7a8f9bd39f6 --- /dev/null +++ b/test/files/run/t12405.check @@ -0,0 +1,96 @@ +[[syntax trees at end of patmat]] // newSource1.scala +package { + final class C[A] extends scala.AnyVal { + private[this] val x: A = _; + def x: A = C.this.x; + def (x: A): C[A] = { + C.super.(); + () + }; + def isEmpty: Boolean = C.isEmpty$extension[A](C.this); + def get: A = C.get$extension[A](C.this); + override def hashCode(): Int = C.hashCode$extension[A](C.this)(); + override def equals(x$1: Any): Boolean = C.equals$extension[A](C.this)(x$1) + }; + object C extends scala.AnyRef { + def (): C.type = { + C.super.(); + () + }; + def unapply[T](c: C[T]): C[T] = c; + final def isEmpty$extension[A]($this: C[A]): Boolean = scala.Predef.???; + final def get$extension[A]($this: C[A]): A = scala.Predef.???; + final def hashCode$extension[A]($this: C[A])(): Int = $this.x.hashCode(); + final def equals$extension[A]($this: C[A])(x$1: Any): Boolean = { + case val x1: Any = x$1; + case5(){ + if (x1.isInstanceOf[C[A]]) + matchEnd4(true) + else + case6() + }; + case6(){ + matchEnd4(false) + }; + matchEnd4(x: Boolean){ + x + } +}.&&({ + val C$1: C[A] = x$1.asInstanceOf[C[A]]; + $this.x.==(C$1.x) + }) + }; + class Test extends scala.AnyRef { + def (): Test = { + Test.super.(); + () + }; + def m1(a: Any): Any = { + case val x1: Any = a; + case6(){ + if (x1.isInstanceOf[C[T]]) + { + val x2: C[T] = (x1.asInstanceOf[C[T]]: C[T]); + { + val o8: C[T] = C.unapply[T](x2); + if (o8.isEmpty.unary_!) + { + val x: T = o8.get; + matchEnd5(x) + } + else + case7() + } + } + else + case7() + }; + case7(){ + matchEnd5(null) + }; + matchEnd5(x: Any){ + x + } + }; + def m2(c: C[String]): String = { + case val x1: C[String] = c; + case5(){ + val o7: C[String] = C.unapply[String](x1); + if (o7.isEmpty.unary_!) + { + val x: String = o7.get; + matchEnd4(x) + } + else + case6() + }; + case6(){ + matchEnd4("") + }; + matchEnd4(x: String){ + x + } + } + } +} + diff --git a/test/files/run/t12405.scala b/test/files/run/t12405.scala new file mode 100644 index 000000000000..f44e19fd99ea --- /dev/null +++ b/test/files/run/t12405.scala @@ -0,0 +1,30 @@ +import scala.tools.partest._ + +object Test extends DirectTest { + override def extraSettings: String = "-usejavacp -Vprint:patmat -Ystop-after:patmat" + + override val code = + """final class C[A](val x: A) extends AnyVal { + | def isEmpty: Boolean = ??? + | def get: A = ??? + |} + |object C { + | def unapply[T](c: C[T]): C[T] = c + |} + |class Test { + | def m1(a: Any) = a match { + | case C(x) => x + | case _ => null + | } + | + | def m2(c: C[String]) = c match { + | case C(x) => x + | case _ => "" + | } + |} + |""".stripMargin + + override def show(): Unit = Console.withErr(System.out) { + compile() + } +} diff --git a/test/files/run/t2318.javaopts b/test/files/run/t2318.javaopts deleted file mode 100644 index 8bf493ce91e6..000000000000 --- a/test/files/run/t2318.javaopts +++ /dev/null @@ -1 +0,0 @@ --Ddummy=fresh_jvm_needed_to_test_security_manager \ No newline at end of file diff --git a/test/files/run/t2318.scala b/test/files/run/t2318.scala index bce56f6be33f..f00297b5c9e1 100644 --- a/test/files/run/t2318.scala +++ b/test/files/run/t2318.scala @@ -1,3 +1,4 @@ +// java: -Ddummy=fresh_jvm_needed_to_test_security_manager // filter: WARNING.* // for now, ignore warnings due to reflective invocation import java.security._ diff --git a/test/files/run/t3613.scala b/test/files/run/t3613.scala index 1293f62c0fd4..d8a6a862c925 100644 --- a/test/files/run/t3613.scala +++ b/test/files/run/t3613.scala @@ -1,15 +1,14 @@ class Boopy { - private val s = new Schnuck - def observer : PartialFunction[ Any, Unit ] = s.observer + private val s = new Schnuck + def observer : PartialFunction[ Any, Unit ] = s.observer - private class Schnuck extends javax.swing.AbstractListModel { - model => - val observer : PartialFunction[ Any, Unit ] = { - case "Boopy" => fireIntervalAdded( model, 0, 1 ) - } - def getSize = 0 - def getElementAt( idx: Int ) = ??? + private class Schnuck extends javax.swing.AbstractListModel[AnyRef] { model => + val observer : PartialFunction[ Any, Unit ] = { + case "Boopy" => fireIntervalAdded( model, 0, 1 ) } + def getSize = 0 + def getElementAt(idx: Int): AnyRef = null + } } diff --git a/test/files/run/t3835.scala b/test/files/run/t3835.scala index 0ee60da845cf..c84c5bb67140 100644 --- a/test/files/run/t3835.scala +++ b/test/files/run/t3835.scala @@ -1,7 +1,4 @@ object Test extends App { - // work around optimizer bug scala/bug#5672 -- generates wrong bytecode for switches in arguments - // virtpatmat happily emits a switch for a one-case switch - // this is not the focus of this test, hence the temporary workaround def a = (1, 2, 3) match { case (r, θ, φ) => r + θ + φ } println(a) def b = (1 match { case é => é }) diff --git a/test/files/run/t4415.scala b/test/files/run/t4415.scala index 5892b0c16def..8a196b516dfd 100644 --- a/test/files/run/t4415.scala +++ b/test/files/run/t4415.scala @@ -3,7 +3,7 @@ * * Exception in thread "main" java.lang.VerifyError: (class: ExtractorIssue$$, method: convert signature: (LTopProperty;)LMyProp;) Accessing value from uninitialized register 5 * at ExtractorIssue.main(ExtractorIssue.scala) - * at com.intellij.rt.execution.application.AppMain.main(AppMain.java:115)] + * at com.intellij.rt.execution.application.AppMain.main(AppMain.java)] * * If lines 15/16 are present, the compiler crashes: * diff --git a/test/files/run/t5225_2.check b/test/files/run/t5225_2.check index 477ea4eb6d41..1333b31b2347 100644 --- a/test/files/run/t5225_2.check +++ b/test/files/run/t5225_2.check @@ -1,4 +1,4 @@ { - def foo(@new elidable(0) x: Int) = ""; + def foo(@new elidable(level = 0) x: Int) = ""; () } diff --git a/test/files/run/t5887.scala b/test/files/run/t5887.scala new file mode 100644 index 000000000000..34bf5fa8487a --- /dev/null +++ b/test/files/run/t5887.scala @@ -0,0 +1,19 @@ + +import scala.tools.testkit.AssertUtil.assertThrows +import scala.annotation.nowarn + +@nowarn("msg=This catches all Throwables.") +object Test extends App { + def npe: Int = throw null + def err: Int = throw new Error() + + val pf: PartialFunction[Throwable, Int] = { case _: NullPointerException => 42 } + val f: Throwable => Int = pf + + assertThrows[NullPointerException](npe) + + assert(42 == (try npe catch pf)) + assert(42 == (try npe catch f)) + assertThrows[Error](try err catch pf) + assertThrows[MatchError](try err catch f) +} diff --git a/test/files/run/t6344.check b/test/files/run/t6344.check index d994d81c7dc5..03f2468145d1 100644 --- a/test/files/run/t6344.check +++ b/test/files/run/t6344.check @@ -4,9 +4,9 @@ public int C0.v1(int) public int C0.v3() public int C0.v3() public int C0.v4(int,scala.collection.immutable.List) -#partest !java15 +#partest !java15+ public int C0.v4(int,scala.collection.immutable.List>) -#partest java15 +#partest java15+ public int C0.v4(int,scala.collection.immutable.List>) #partest public scala.collection.immutable.List C0.v2() @@ -18,9 +18,9 @@ public java.lang.Object C1.v1(java.lang.Object) public java.lang.Object C1.v3() public java.lang.Object C1.v3() public java.lang.Object C1.v4(java.lang.Object,scala.collection.immutable.List) -#partest !java15 +#partest !java15+ public java.lang.Object C1.v4(java.lang.Object,scala.collection.immutable.List>) -#partest java15 +#partest java15+ public java.lang.Object C1.v4(java.lang.Object,scala.collection.immutable.List>) #partest public scala.collection.immutable.List C1.v2() @@ -32,9 +32,9 @@ public java.lang.String C2.v1(java.lang.String) public java.lang.String C2.v3() public java.lang.String C2.v3() public java.lang.String C2.v4(java.lang.String,scala.collection.immutable.List) -#partest !java15 +#partest !java15+ public java.lang.String C2.v4(java.lang.String,scala.collection.immutable.List>) -#partest java15 +#partest java15+ public java.lang.String C2.v4(java.lang.String,scala.collection.immutable.List>) #partest public scala.collection.immutable.List C2.v2() diff --git a/test/files/run/t6411a.javaopts b/test/files/run/t6411a.javaopts deleted file mode 100644 index 2e862e5f806e..000000000000 --- a/test/files/run/t6411a.javaopts +++ /dev/null @@ -1 +0,0 @@ --XX:CompileCommand=exclude,scala/runtime/BoxesRunTime.unboxToInt diff --git a/test/files/run/t6411a.scala b/test/files/run/t6411a.scala index f40c42d05965..bd2fdd37be52 100644 --- a/test/files/run/t6411a.scala +++ b/test/files/run/t6411a.scala @@ -1,3 +1,4 @@ +// java: -XX:CompileCommand=exclude,scala/runtime/BoxesRunTime.unboxToInt // filter: scala.runtime.BoxesRunTime.{1,2}unboxToInt // // noise from -XX:CompileCommand=exclude,scala/runtime/BoxesRunTime.unboxToInt diff --git a/test/files/run/t6476.check b/test/files/run/t6476.check new file mode 100644 index 000000000000..b7be3ae88a91 --- /dev/null +++ b/test/files/run/t6476.check @@ -0,0 +1,13 @@ +"Hello", Alice +"Hello", Alice +"Hello", Alice +"Hello", Alice +\"Hello\", Alice +\"Hello\", Alice +\TILT\ +\TILT\ +\\TILT\\ +\TILT\ +\TILT\ +\\TILT\\ +\TILT\ diff --git a/test/files/run/t6476.scala b/test/files/run/t6476.scala new file mode 100644 index 000000000000..a04645065a2a --- /dev/null +++ b/test/files/run/t6476.scala @@ -0,0 +1,23 @@ +object Test { + def main(args: Array[String]): Unit = { + val person = "Alice" + println(s"\"Hello\", $person") + println(s"""\"Hello\", $person""") + + println(f"\"Hello\", $person") + println(f"""\"Hello\", $person""") + + println(raw"\"Hello\", $person") + println(raw"""\"Hello\", $person""") + + println(s"\\TILT\\") + println(f"\\TILT\\") + println(raw"\\TILT\\") + + println(s"""\\TILT\\""") + println(f"""\\TILT\\""") + println(raw"""\\TILT\\""") + + println(raw"""\TILT\""") + } +} diff --git a/test/files/run/t6488.javaopts b/test/files/run/t6488.javaopts deleted file mode 100644 index 0c252573c8f0..000000000000 --- a/test/files/run/t6488.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dforked.test=yes.please diff --git a/test/files/run/t6488.scala b/test/files/run/t6488.scala index 1d99bd85d4cf..90d29b264964 100644 --- a/test/files/run/t6488.scala +++ b/test/files/run/t6488.scala @@ -1,3 +1,5 @@ +// java: -Dforked.test=yes.please + import scala.sys.process._ import scala.util.Try import scala.util.Properties.{javaHome, javaClassPath, userDir} diff --git a/test/files/run/t7398.scala b/test/files/run/t7398.scala deleted file mode 100644 index 4b4685076810..000000000000 --- a/test/files/run/t7398.scala +++ /dev/null @@ -1,26 +0,0 @@ -import scala.tools.partest._ - -object Test extends CompilerTest { - import global._ - - override lazy val units: List[CompilationUnit] = { - // This test itself does not depend on JDK8. - javaCompilationUnits(global)(defaultMethodSource) - } - - private def defaultMethodSource = """ -public interface Iterator { - boolean hasNext(); - E next(); - default void remove() { - throw new UnsupportedOperationException("remove"); - } - default void forEachRemaining(Consumer action) { - throw new UnsupportedOperationException("forEachRemaining"); - } -} - """ - - // We're only checking we can compile it. - def check(source: String, unit: global.CompilationUnit): Unit = () -} diff --git a/test/files/run/t7448.scala b/test/files/run/t7448.scala new file mode 100644 index 000000000000..5bf74ee85a77 --- /dev/null +++ b/test/files/run/t7448.scala @@ -0,0 +1,18 @@ +// scalac: -nowarn +import util.chaining._ + +object Test { + def main(args: Array[String]) = 42.tap(res => assert(res == 42)) +} + +// test that partest is using scala runner to execute this test. +// With warnings enabled: +/* +t7448.scala:7: warning: not a valid main method for Test, + because main methods must have the exact signature `(Array[String]): Unit`, though Scala runners will forgive a non-Unit result. + To define an entry point, please define the main method as: + def main(args: Array[String]): Unit + + def main(args: Array[String]) = 42.tap(res => assert(res == 42)) + ^ + */ diff --git a/test/files/run/t7634.javaopts b/test/files/run/t7634.javaopts deleted file mode 100644 index b0c90bb1f73a..000000000000 --- a/test/files/run/t7634.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm.for.windows diff --git a/test/files/run/t7634.scala b/test/files/run/t7634.scala index 345138eb933f..5997b3d48fa5 100644 --- a/test/files/run/t7634.scala +++ b/test/files/run/t7634.scala @@ -1,3 +1,5 @@ +// java: -Dneeds.forked.jvm.for.windows + import java.io.File import scala.tools.partest.ReplTest import scala.util.Properties.propOrElse diff --git a/test/files/run/t7741a.check b/test/files/run/t7741a.check deleted file mode 100644 index e835f0ce738c..000000000000 --- a/test/files/run/t7741a.check +++ /dev/null @@ -1,3 +0,0 @@ -#partest !java8 -Note: t7741a/GroovyInterface$1Dump.java uses or overrides a deprecated API. -Note: Recompile with -Xlint:deprecation for details. diff --git a/test/files/run/t7741a/GroovyInterface$1Dump.java b/test/files/run/t7741a/GroovyInterface$1Dump.java index 0c0eab3f1b6d..cc187f353ed4 100644 --- a/test/files/run/t7741a/GroovyInterface$1Dump.java +++ b/test/files/run/t7741a/GroovyInterface$1Dump.java @@ -175,7 +175,7 @@ public static byte[] dump () throws Exception { { mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$createCallSiteArray", "()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray;", null, null); mv.visitCode(); - mv.visitLdcInsn(new Integer(0)); + mv.visitLdcInsn(Integer.valueOf(0)); mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); mv.visitVarInsn(ASTORE, 0); mv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/callsite/CallSiteArray"); diff --git a/test/files/run/t7747-repl.check b/test/files/run/t7747-repl.check index ba77d518fc7d..9691e4db43d4 100644 --- a/test/files/run/t7747-repl.check +++ b/test/files/run/t7747-repl.check @@ -153,7 +153,7 @@ scala> x1 + x2 + x3 val res23: Int = 6 scala> :reset -Resetting interpreter state. +Resetting REPL state. Forgetting this session history: var x = 10 diff --git a/test/files/run/t7805-repl-i.javaopts b/test/files/run/t7805-repl-i.javaopts deleted file mode 100644 index 9740f07b079b..000000000000 --- a/test/files/run/t7805-repl-i.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm \ No newline at end of file diff --git a/test/files/run/t7805-repl-i.scala b/test/files/run/t7805-repl-i.scala index 2a80ad8bda2d..816926b7c38a 100644 --- a/test/files/run/t7805-repl-i.scala +++ b/test/files/run/t7805-repl-i.scala @@ -1,3 +1,5 @@ +// java: -Dneeds.forked.jvm + import scala.tools.partest.ReplTest import scala.tools.nsc.{ GenericRunnerSettings, Settings } import scala.tools.nsc.settings.MutableSettings diff --git a/test/files/run/t7825.scala b/test/files/run/t7825.scala deleted file mode 100644 index 65ca06fdfc09..000000000000 --- a/test/files/run/t7825.scala +++ /dev/null @@ -1,34 +0,0 @@ -import scala.tools.partest._ - -object Test extends CompilerTest { - import global._ - - override lazy val units: List[CompilationUnit] = { - // We can test this on JDK6. - javaCompilationUnits(global)(defaultMethodSource) ++ compilationUnits(global)(scalaExtendsDefault) - } - - private def defaultMethodSource = """ -public interface Iterator { - boolean hasNext(); - E next(); - default void remove() { - throw new UnsupportedOperationException("remove"); - } -} - """ - - private def scalaExtendsDefault = """ -object Test { - object X extends Iterator[String] { - def hasNext = true - def next = "!" - } -} - """ - - // We're only checking we that the Scala compilation unit passes refchecks - // No further checks are needed here. - def check(source: String, unit: global.CompilationUnit): Unit = { - } -} diff --git a/test/files/run/t8266-octal-interp.javaopts b/test/files/run/t8266-octal-interp.javaopts deleted file mode 100644 index 9740f07b079b..000000000000 --- a/test/files/run/t8266-octal-interp.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm \ No newline at end of file diff --git a/test/files/run/t8852a.scala b/test/files/run/t8852a.scala deleted file mode 100644 index cbff8ab75b91..000000000000 --- a/test/files/run/t8852a.scala +++ /dev/null @@ -1,34 +0,0 @@ -import scala.tools.partest._ - -// Test that static methods in Java interfaces (new in Java 8) -// are callable from jointly compiler Scala code. -object Test extends CompilerTest { - import global._ - - override lazy val units: List[CompilationUnit] = { - // This test itself does not depend on JDK8. - javaCompilationUnits(global)(staticMethodInInterface) ++ - compilationUnits(global)(scalaClient) - } - - private def staticMethodInInterface = """ -public interface Interface { - public static int staticMethod() { - return 42; - } -} - - """ - - private def scalaClient = """ -object Test { - val x: Int = Interface.staticMethod() -} - -class C extends Interface // expect no errors about unimplemented members. - - """ - - // We're only checking we can compile it. - def check(source: String, unit: global.CompilationUnit): Unit = () -} diff --git a/test/files/run/t8928.javaopts b/test/files/run/t8928.javaopts deleted file mode 100644 index a8e6bbca18ae..000000000000 --- a/test/files/run/t8928.javaopts +++ /dev/null @@ -1 +0,0 @@ --Dneeds.forked.jvm diff --git a/test/files/run/t8928/Test_1.scala b/test/files/run/t8928/Test_1.scala index 1cef564ff1be..bcf94ce41e52 100644 --- a/test/files/run/t8928/Test_1.scala +++ b/test/files/run/t8928/Test_1.scala @@ -1,3 +1,4 @@ +// java: -Dneeds.forked.jvm import test._ object Test extends App { diff --git a/test/files/run/t9529.check b/test/files/run/t9529.check index f1c433ddaced..38ad198f56ba 100644 --- a/test/files/run/t9529.check +++ b/test/files/run/t9529.check @@ -32,7 +32,7 @@ u: List(@anns.Ann_0$Container(value={@anns.Ann_0(name="u", value="you"), @anns.A List(@anns.Ann_0$Container(value={@anns.Ann_0(name="", value="constructor"), @anns.Ann_0(name="", value="initializer")})) -#partest java15 +#partest java15+ A: List() B: List(@java.lang.Deprecated(forRemoval=false, since="")) C: List(@anns.Ann_0(name="C", value="see")) diff --git a/test/files/run/type-tag-leak.javaopts b/test/files/run/type-tag-leak.javaopts deleted file mode 100644 index 408a4e4cb595..000000000000 --- a/test/files/run/type-tag-leak.javaopts +++ /dev/null @@ -1 +0,0 @@ --Xmx192M -XX:+ExitOnOutOfMemoryError \ No newline at end of file diff --git a/test/files/run/type-tag-leak.scala b/test/files/run/type-tag-leak.scala index 245288802a84..277799f765eb 100644 --- a/test/files/run/type-tag-leak.scala +++ b/test/files/run/type-tag-leak.scala @@ -1,3 +1,5 @@ +// java: -Xmx192M -XX:+ExitOnOutOfMemoryError + import scala.reflect.runtime.universe import scala.reflect.runtime.universe._ import scala.tools.nsc.interpreter._ diff --git a/test/files/run/virtpatmat_alts.check b/test/files/run/virtpatmat_alts.check index f39e292fef10..91b7c068c307 100644 --- a/test/files/run/virtpatmat_alts.check +++ b/test/files/run/virtpatmat_alts.check @@ -1,7 +1,9 @@ -virtpatmat_alts.scala:5: warning: match may not be exhaustive. +virtpatmat_alts.scala:2: warning: match may not be exhaustive. +It would fail on the following inputs: (false, true), (true, false) (true, true) match { ^ -virtpatmat_alts.scala:9: warning: match may not be exhaustive. +virtpatmat_alts.scala:6: warning: match may not be exhaustive. +It would fail on the following inputs: List((x: Int forSome x not in (1, 2, 4, 5, 6, 7))), List((x: Int forSome x not in (1, 2, 4, 5, 6, 7)), _), List(1, _), List(2, _), List(4, _), List(5, _), List(6, _), List(7, _), List(_, _) List(5) match { ^ OK 5 diff --git a/test/files/run/virtpatmat_alts.scala b/test/files/run/virtpatmat_alts.scala index d1dfa8a4a1ab..0ae6f36241d5 100644 --- a/test/files/run/virtpatmat_alts.scala +++ b/test/files/run/virtpatmat_alts.scala @@ -1,6 +1,3 @@ -/* - * filter: It would fail on the following input - */ object Test extends App { (true, true) match { case (true, true) | (false, false) => 1 diff --git a/test/files/run/virtpatmat_nested_lists.check b/test/files/run/virtpatmat_nested_lists.check index 9d1d5a90a8f8..0ae86d1bf5f7 100644 --- a/test/files/run/virtpatmat_nested_lists.check +++ b/test/files/run/virtpatmat_nested_lists.check @@ -1,4 +1,5 @@ -virtpatmat_nested_lists.scala:7: warning: match may not be exhaustive. +virtpatmat_nested_lists.scala:3: warning: match may not be exhaustive. +It would fail on the following inputs: List(_), List(_, List(_), _), List(_, List(_, _)), List(_, List(_, _), _), List(_, Nil), List(_, Nil, _), Nil List(List(1), List(2)) match { case x :: (y :: Nil) :: Nil => println(y) } ^ 2 diff --git a/test/files/run/virtpatmat_nested_lists.scala b/test/files/run/virtpatmat_nested_lists.scala index 6b7233c0454e..58f36b642303 100644 --- a/test/files/run/virtpatmat_nested_lists.scala +++ b/test/files/run/virtpatmat_nested_lists.scala @@ -1,8 +1,4 @@ // scalac: -Ypatmat-exhaust-depth off -// -/* - * filter: It would fail on the following input - */ object Test extends App { List(List(1), List(2)) match { case x :: (y :: Nil) :: Nil => println(y) } } diff --git a/test/files/run/virtpatmat_opt_sharing.check b/test/files/run/virtpatmat_opt_sharing.check index 95e962134f98..a2189687a521 100644 --- a/test/files/run/virtpatmat_opt_sharing.check +++ b/test/files/run/virtpatmat_opt_sharing.check @@ -1,4 +1,5 @@ -virtpatmat_opt_sharing.scala:9: warning: match may not be exhaustive. +virtpatmat_opt_sharing.scala:5: warning: match may not be exhaustive. +It would fail on the following inputs: List((x: Int forSome x not in 1)), List((x: Int forSome x not in 1), (x: Int forSome x not in 3)), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), (x: Int forSome x not in 4)), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), (x: Int forSome x not in 4), (x: Int forSome x not in (5, 6, 7))), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), (x: Int forSome x not in 4), 5), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), (x: Int forSome x not in 4), 6), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), (x: Int forSome x not in 4), 7), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), (x: Int forSome x not in 4), ??), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), 4), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), 4, (x: Int forSome x not in (5, 6, 7))), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), 4, 5), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), 4, 6), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), 4, 7), List((x: Int forSome x not in 1), (x: Int forSome x not in 3), 4, ??), List((x: Int forSome x not in 1), 3), List((x: Int forSome x not in 1), 3, (x: Int forSome x not in 4)), List((x: Int forSome x not in 1), 3, (x: Int forSome x not in 4), (x: Int forSome x not in (5, 6, 7))), List((x: Int forSome x not in 1), 3, (x: Int forSome x not in 4), 5), List((x: Int forSome x not in 1), 3, (x: Int forSome x not in 4), 6), List((x: Int forSome x not in 1), 3, (x: Int forSome x not in 4), 7), List((x: Int forSome x not in 1), 3, (x: Int forSome x not in 4), ??), List((x: Int forSome x not in 1), 3, 4), List((x: Int forSome x not in 1), 3, 4, (x: Int forSome x not in (5, 6, 7))), List((x: Int forSome x not in 1), 3, 4, 5), List((x: Int forSome x not in 1), 3, 4, 6), List((x: Int forSome x not in 1), 3, 4, 7), List((x: Int forSome x not in 1), 3, 4, ??), List(1), List(1, (x: Int forSome x not in 3)), List(1, (x: Int forSome x not in 3), (x: Int forSome x not in 4)), List(1, (x: Int forSome x not in 3), (x: Int forSome x not in 4), (x: Int forSome x not in (5, 6, 7))), List(1, (x: Int forSome x not in 3), (x: Int forSome x not in 4), 5), List(1, (x: Int forSome x not in 3), (x: Int forSome x not in 4), 6), List(1, (x: Int forSome x not in 3), (x: Int forSome x not in 4), 7), List(1, (x: Int forSome x not in 3), (x: Int forSome x not in 4), ??), List(1, (x: Int forSome x not in 3), 4), List(1, (x: Int forSome x not in 3), 4, (x: Int forSome x not in (5, 6, 7))), List(1, (x: Int forSome x not in 3), 4, 5), List(1, (x: Int forSome x not in 3), 4, 6), List(1, (x: Int forSome x not in 3), 4, 7), List(1, (x: Int forSome x not in 3), 4, ??), List(1, 3), List(1, 3, (x: Int forSome x not in 4)), List(1, 3, (x: Int forSome x not in 4), (x: Int forSome x not in (5, 6, 7))), List(1, 3, (x: Int forSome x not in 4), 5), List(1, 3, (x: Int forSome x not in 4), 6), List(1, 3, (x: Int forSome x not in 4), 7), List(1, 3, (x: Int forSome x not in 4), ??), List(1, 3, 4), List(1, 3, 4, (x: Int forSome x not in (5, 6, 7))), Nil List(1, 3, 4, 7) match { ^ 1 diff --git a/test/files/run/virtpatmat_opt_sharing.scala b/test/files/run/virtpatmat_opt_sharing.scala index 2f1b68d05780..988f963c8ec7 100644 --- a/test/files/run/virtpatmat_opt_sharing.scala +++ b/test/files/run/virtpatmat_opt_sharing.scala @@ -1,8 +1,4 @@ // scalac: -Ypatmat-exhaust-depth off -// -/* - * filter: It would fail on the following input - */ object Test extends App { virtMatch() def virtMatch() = { diff --git a/test/instrumented/srt.patch b/test/instrumented/srt.patch index ff7a2357b2b9..c819cff3320b 100644 --- a/test/instrumented/srt.patch +++ b/test/instrumented/srt.patch @@ -3,16 +3,16 @@ @@ -10,6 +10,8 @@ * additional information regarding copyright ownership. */ - + +/* INSTRUMENTED VERSION */ + package scala package runtime - + @@ -52,8 +54,11 @@ def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] = classTag[T].runtimeClass.asInstanceOf[jClass[T]] - + + var arrayApplyCount = 0 + /** Retrieve generic array element */ @@ -21,10 +21,10 @@ (xs: @unchecked) match { case x: Array[AnyRef] => x(idx).asInstanceOf[Any] case x: Array[Int] => x(idx).asInstanceOf[Any] -@@ -69,8 +74,11 @@ +@@ -68,8 +73,11 @@ } } - + + var arrayUpdateCount = 0 + /** update generic array element */ diff --git a/test/jcstress/project/build.properties b/test/jcstress/project/build.properties index 0b2e09c5ac99..67d27a1dfe00 100644 --- a/test/jcstress/project/build.properties +++ b/test/jcstress/project/build.properties @@ -1 +1 @@ -sbt.version=1.4.7 +sbt.version=1.5.3 diff --git a/test/junit/scala/collection/ArrayOpsTest.scala b/test/junit/scala/collection/ArrayOpsTest.scala index 06a1cc2713f3..3283caa252fd 100644 --- a/test/junit/scala/collection/ArrayOpsTest.scala +++ b/test/junit/scala/collection/ArrayOpsTest.scala @@ -122,4 +122,24 @@ class ArrayOpsTest { val a: Array[Byte] = new Array[Byte](1000).sortWith { _ < _ } assertEquals(0, a(0)) } + + @Test + def `empty intersection has correct component type for array`(): Unit = { + val something = Array(3.14) + val nothing = Array[Double]() + val empty = Array.empty[Double] + + assertEquals(classOf[Double], nothing.intersect(something).getClass.getComponentType) + assertTrue(nothing.intersect(something).isEmpty) + + assertEquals(classOf[Double], empty.intersect(something).getClass.getComponentType) + assertTrue(empty.intersect(something).isEmpty) + assertEquals(classOf[Double], empty.intersect(nothing).getClass.getComponentType) + assertTrue(empty.intersect(nothing).isEmpty) + + assertEquals(classOf[Double], something.intersect(nothing).getClass.getComponentType) + assertTrue(something.intersect(nothing).isEmpty) + assertEquals(classOf[Double], something.intersect(empty).getClass.getComponentType) + assertTrue(something.intersect(empty).isEmpty) + } } diff --git a/test/junit/scala/collection/MapTest.scala b/test/junit/scala/collection/MapTest.scala index 6bfa66955e54..90900619f472 100644 --- a/test/junit/scala/collection/MapTest.scala +++ b/test/junit/scala/collection/MapTest.scala @@ -123,4 +123,9 @@ class MapTest { check(mutable.CollisionProofHashMap(1 -> 1)) } + @Test + def t12228(): Unit = { + assertFalse(Set("") == immutable.BitSet(1)) + assertFalse(Map("" -> 2) == scala.collection.immutable.LongMap(1L -> 2)) + } } diff --git a/test/junit/scala/collection/SortedSetMapEqualsTest.scala b/test/junit/scala/collection/SortedSetMapEqualsTest.scala index 44653696c74f..804a6989f8e7 100644 --- a/test/junit/scala/collection/SortedSetMapEqualsTest.scala +++ b/test/junit/scala/collection/SortedSetMapEqualsTest.scala @@ -1,6 +1,7 @@ package scala.collection -import org.junit.{Assert, Test}, Assert.assertEquals +import org.junit.{Assert, Test} +import Assert.{assertEquals, assertNotEquals} class SortedSetMapEqualsTest { @Test @@ -68,4 +69,60 @@ class SortedSetMapEqualsTest { } assertEquals(m1, m2) } + + @Test + def compareSortedMapKeysByOrdering(): Unit = { + val ord: Ordering[String] = _ compareToIgnoreCase _ + + val itm1 = scala.collection.immutable.TreeMap("A" -> "2")(ord) + val itm2 = scala.collection.immutable.TreeMap("a" -> "2")(ord) + val mtm1 = scala.collection.mutable.TreeMap("A" -> "2")(ord) + val mtm2 = scala.collection.mutable.TreeMap("a" -> "2")(ord) + + assertEquals(itm1, itm2) + assertEquals(mtm1, mtm2) + + assertEquals(itm1, mtm2) + assertEquals(mtm1, itm2) + + val m1 = Map("A" -> "2") + val m2 = Map("a" -> "2") + + for (m <- List(m1, m2); tm <- List[Map[String, String]](itm1, itm2, mtm1, mtm2)) + assertEquals(m, tm) // uses keys in `m` to look up values in `tm`, which always succeeds + + assertEquals(itm1, m1) + assertEquals(mtm1, m1) + + assertNotEquals(itm2, m1) // uses key in `itm2` ("a") to look up in `m1`, which fails + assertNotEquals(mtm2, m1) + } + + @Test + def compareSortedSetsByOrdering(): Unit = { + val ord: Ordering[String] = _ compareToIgnoreCase _ + + val its1 = scala.collection.immutable.TreeSet("A")(ord) + val its2 = scala.collection.immutable.TreeSet("a")(ord) + val mts1 = scala.collection.mutable.TreeSet("A")(ord) + val mts2 = scala.collection.mutable.TreeSet("a")(ord) + + assertEquals(its1, its2) + assertEquals(mts1, mts2) + + assertEquals(its1, mts2) + assertEquals(mts1, its2) + + val s1 = Set("A") + val s2 = Set("a") + + for (m <- List(s1, s2); tm <- List[Set[String]](its1, its2, mts1, mts2)) + assertEquals(m, tm) // uses keys in `m` to look up values in `tm`, which always succeeds + + assertEquals(its1, s1) + assertEquals(mts1, s1) + + assertNotEquals(its2, s1) // uses key in `its2` ("a") to look up in `s1`, which fails + assertNotEquals(mts2, s1) + } } diff --git a/test/junit/scala/collection/immutable/HashMapTest.scala b/test/junit/scala/collection/immutable/HashMapTest.scala index d9f1bf53d27a..a73c02d000b3 100644 --- a/test/junit/scala/collection/immutable/HashMapTest.scala +++ b/test/junit/scala/collection/immutable/HashMapTest.scala @@ -8,6 +8,7 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import scala.tools.testkit.AllocationTest +import scala.tools.testkit.AssertUtil.assertThrows @RunWith(classOf[JUnit4]) class HashMapTest extends AllocationTest{ @@ -339,4 +340,10 @@ class HashMapTest extends AllocationTest{ check(cs => TreeMap(cs: _*)) // exercise special case for HashMap/HasForEachEntry check(cs => HashMap(cs: _*).withDefault(_ => ???)) // default cases } + + @Test + def noSuchElement(): Unit = { + assertThrows[NoSuchElementException](HashMap(1->1)(2), _ == "key not found: 2") + assertThrows[NoSuchElementException](HashMap.empty(3), _ == "key not found: 3") + } } diff --git a/test/junit/scala/collection/immutable/TreeMapTest.scala b/test/junit/scala/collection/immutable/TreeMapTest.scala index 95c3f6d8fc16..31bfebb1cc5c 100644 --- a/test/junit/scala/collection/immutable/TreeMapTest.scala +++ b/test/junit/scala/collection/immutable/TreeMapTest.scala @@ -254,4 +254,12 @@ class TreeMapTest extends AllocationTest { def withoutOrdering(m: Map[K, V]): Map[K, V] = collection.immutable.Map.apply(m.iterator.toSeq: _*) assertEquals(withoutOrdering(expected), withoutOrdering(map)) } + + @Test def removeNonContent(): Unit = { + val src: Map[Int, String] = TreeMap(Range(0, 100, 2).map((_, "")) :_*) + for (i <- Range(-1, 101, 2)) { + src - i + assertSame(i.toString, src, nonAllocating(src - i, text = i.toString)) + } + } } diff --git a/test/junit/scala/collection/immutable/TreeSetTest.scala b/test/junit/scala/collection/immutable/TreeSetTest.scala index 871c02c2ebf7..05e436791d82 100644 --- a/test/junit/scala/collection/immutable/TreeSetTest.scala +++ b/test/junit/scala/collection/immutable/TreeSetTest.scala @@ -332,4 +332,10 @@ class TreeSetTest extends AllocationTest { assertEquals(expected, src filter set) } } + @Test def removeNonContent(): Unit = { + val src = TreeSet(Range(0, 100, 2) :_*) + for (i <- Range(-1, 101, 2)) { + assertSame(src, nonAllocating(src - i)) + } + } } diff --git a/test/junit/scala/lang/RicherTest.scala b/test/junit/scala/lang/RicherTest.scala new file mode 100644 index 000000000000..d6557e4fde44 --- /dev/null +++ b/test/junit/scala/lang/RicherTest.scala @@ -0,0 +1,144 @@ + +package scala + +import org.junit.{Assert, Test} +import scala.util.chaining._ + +class RicherTest { + import RicherTest._ + + private def assertEqualTo(expected: String)(actual: String) = Assert.assertEquals(expected, actual) + private def assertEqualTo(expected: Int)(actual: Int) = Assert.assertEquals(expected, actual) + private def assertEqualTo[A](expected: List[A])(actual: List[A]) = Assert.assertEquals(expected, actual) + + @Test def `Byte expansions should be byte-sized`(): Unit = { + val sixteen = 16.toByte + assertEqualTo(x"1_0000")(sixteen.toBinaryString) + assertEqualTo("10")(sixteen.toHexString) + assertEqualTo("20")(sixteen.toOctalString) + val max = 0x7F.toByte + assertEqualTo(x"111_1111")(max.toBinaryString) + assertEqualTo("7f")(max.toHexString) + assertEqualTo("177")(max.toOctalString) + val extended = 0x80.toByte + assertEqualTo("1" * 24 + x"1000_0000")(extended.toBinaryString) + assertEqualTo(x"ffff_ff80")(extended.toHexString) + assertEqualTo("37777777600")(extended.toOctalString) + val neg = -1.toByte + assertEqualTo("1" * 32)(neg.toBinaryString) + assertEqualTo("f" * 8)(neg.toHexString) + assertEqualTo("3" + "7" * 10)(neg.toOctalString) + } + @Test def `Short expansions should be short-sized`(): Unit = { + val sixteen = 16.toShort + assertEqualTo(x"1_0000")(sixteen.toBinaryString) + assertEqualTo("10")(sixteen.toHexString) + assertEqualTo("20")(sixteen.toOctalString) + val max = 0x7FFF.toShort + assertEqualTo(x"111_1111_1111_1111")(max.toBinaryString) + assertEqualTo("7fff")(max.toHexString) + assertEqualTo("77777")(max.toOctalString) + val extended = 0x8000.toShort + assertEqualTo(x"1111_1111_1111_1111_1000_0000_0000_0000")(extended.toBinaryString) + assertEqualTo(x"ffff_8000")(extended.toHexString) + assertEqualTo(x"37777700000")(extended.toOctalString) + val neg = -1.toShort + assertEqualTo("1" * 32)(neg.toBinaryString) + assertEqualTo(x"ffff_ffff")(neg.toHexString) + assertEqualTo(x"37777777777")(neg.toOctalString) + } + // same as short, but uses int conversion because unsigned + @Test def `Char expansions should be char-sized`(): Unit = { + val sixteen = 16.toChar + assertEqualTo(x"1_0000")(sixteen.toBinaryString) + assertEqualTo("10")(sixteen.toHexString) + assertEqualTo("20")(sixteen.toOctalString) + val max = 0x7FFF.toChar + assertEqualTo(x"111_1111_1111_1111")(max.toBinaryString) + assertEqualTo("7fff")(max.toHexString) + assertEqualTo("77777")(max.toOctalString) + val extended = 0x8000.toChar + assertEqualTo(x"1000_0000_0000_0000")(extended.toBinaryString) + assertEqualTo("8000")(extended.toHexString) + assertEqualTo(x"10_0000")(extended.toOctalString) + val neg = -1.toChar + assertEqualTo("1" * 16)(neg.toBinaryString) + assertEqualTo("ffff")(neg.toHexString) + assertEqualTo(x"17_7777")(neg.toOctalString) + } + @Test def `Chars are digits`(): Unit = { + assertEqualTo(1)('1'.asDigit) + assertEqualTo(10)('A'.asDigit) + } + @Test def `Ints are ranged`(): Unit = { + assertEqualTo(10)((0 until 10).length) + assertEqualTo(11)((0 to 10).length) + assertEqualTo(12)((-2 until 10).length) + assertEqualTo(13)((-2 to 10).length) + assertEqualTo(0)((10 until -2).length) + assertEqualTo(0)((10 to -2).length) + } + @Test def `Int strings`(): Unit = { + assertEqualTo(x"1_0000")(16.toBinaryString) + assertEqualTo("10")(16.toHexString) + assertEqualTo("20")(16.toOctalString) + assertEqualTo("10001")(65537.toHexString) + assertEqualTo("f" * 8)(-1.toHexString) + } + + // see also StringLikeTest + val s1 = """abc""" + val s2 = """abc\txyz\n""" + val s3 = """abc + xyz""" + val s4 = """abc + |xyz""" + val s5 = """abc + #xyz""" + @Test def `linesIterator iterates lines`(): Unit = { + assertEqualTo(1)(s1.linesIterator.length) + assertEqualTo(s1)(s1.linesIterator.next()) + assertEqualTo(1)(s2.linesIterator.length) + assertEqualTo(s2)(s2.linesIterator.next()) + assertEqualTo(2)(s3.linesIterator.length) + assertEqualTo("abc")(s3.linesIterator.next()) + assertEqualTo(" xyz")(s3.linesIterator.pipe { it => it.next(); it.next() }) + } + @Test def `stripLineEnd strips lines ends`(): Unit = { + assertEqualTo(s1)(s1.stripLineEnd) + assertEqualTo(s2)(s2.stripLineEnd) + assertEqualTo(s3)(s3.stripLineEnd) + assertEqualTo(s4)(s4.stripLineEnd) + assertEqualTo(s5)(s5.stripLineEnd) + assertEqualTo("abc")("abc\n".stripLineEnd) + } + @Test def `stripMargin strips lines margins`(): Unit = { + assertEqualTo(s1)(s1.stripMargin) + assertEqualTo(s2)(s2.stripMargin) + assertEqualTo(s3)(s3.stripMargin) + assertEqualTo("abc\nxyz")(s4.stripMargin) + assertEqualTo(s5)(s5.stripMargin) + } + @Test def `stripMargin strips custom margins`(): Unit = { + assertEqualTo(s1)(s1.stripMargin('#')) + assertEqualTo(s2)(s2.stripMargin('#')) + assertEqualTo(s3)(s3.stripMargin('#')) + assertEqualTo(s4)(s4.stripMargin('#')) + assertEqualTo("abc\nxyz")(s5.stripMargin('#')) + } + @Test def `split splits strings`(): Unit = { + assertEqualTo(List("a","b","c","d"))("a:b:c:d".split(':').toList) + assertEqualTo(List("a","b","c","d"))("a.b.c.d".split('.').toList) + assertEqualTo(List("a","b","c","d"))("a$b$c$d".split('$').toList) + assertEqualTo(List("a","b","c","d"))("a^b^c^d".split('^').toList) + assertEqualTo(List("a","b","c","d"))("a\\b\\c\\d".split('\\').toList) + assertEqualTo(List("a","b","c","d"))("a:b:c.d".split(Array(':','.')).toList) + assertEqualTo(List("a","b","c","d"))("a:b.c$d".split(Array(':','.','$')).toList) + } +} + +object RicherTest { + implicit class stripper(private val sc: StringContext) extends AnyVal { + def x(args: Any*) = StringContext.standardInterpolator(_.replace("_", ""), args, sc.parts) + } +} diff --git a/test/junit/scala/reflect/internal/SubstMapTest.scala b/test/junit/scala/reflect/internal/SubstMapTest.scala new file mode 100644 index 000000000000..7719e3a9a968 --- /dev/null +++ b/test/junit/scala/reflect/internal/SubstMapTest.scala @@ -0,0 +1,13 @@ +package scala.reflect.internal + +import scala.tools.nsc.symtab.SymbolTableForUnitTesting + +class SubstMapTest { + object symbolTable extends SymbolTableForUnitTesting + import symbolTable._ + + // compile-test for https://github.com/scala/community-build/pull/1413 + new SubstMap[String](Nil, Nil) { + protected def toType(fromtp: Type, tp: String) = fromtp + } +} diff --git a/test/junit/scala/reflect/io/ZipArchiveTest.scala b/test/junit/scala/reflect/io/ZipArchiveTest.scala index 40bf4b540839..ec7ede4348b6 100644 --- a/test/junit/scala/reflect/io/ZipArchiveTest.scala +++ b/test/junit/scala/reflect/io/ZipArchiveTest.scala @@ -28,6 +28,21 @@ class ZipArchiveTest { } } + @Test + def weirdFileAtRoot(): Unit = { + val f = Files.createTempFile("test", ".jar").tap {f => + Using.resource(new JarOutputStream(Files.newOutputStream(f))) { jout => + jout.putNextEntry(new JarEntry("/.hey.txt")) + val bytes = "hello, world".getBytes + jout.write(bytes, 0, bytes.length) + () + } + } + Using.resources(ForDeletion(f), new FileZipArchive(f.toFile)){ (_, fza) => + assertEquals(Seq(".hey.txt"), fza.iterator.toSeq.map(_.name)) + } + } + @Test def missingFile(): Unit = { val f = Paths.get("xxx.does.not.exist") diff --git a/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala index b67ee23b13e3..4bc7e2035e2e 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala @@ -10,6 +10,7 @@ import scala.tools.testkit.ASMConverters._ import scala.tools.testkit.BytecodeTesting import scala.tools.testkit.BytecodeTesting._ import scala.tools.asm.Opcodes +import scala.tools.asm.tree.MethodNode class BytecodeTest extends BytecodeTesting { import compiler._ @@ -343,4 +344,33 @@ class BytecodeTest extends BytecodeTesting { val a = A.fields.asScala.find(_.name == "a").get assertEquals(0, a.access & Opcodes.ACC_FINAL) } + + @Test + def t12362(): Unit = { + val code = + """object Test { + | def foo(value: String) = { + | println(value) + | } + | + | def abcde(value1: String, value2: Long, value3: Double, value4: Int, value5: Double): Double = { + | println(value1) + | value5 + | } + |}""".stripMargin + + val List(mirror, _) = compileClasses(code) + assertEquals(mirror.name, "Test") + + val foo = getAsmMethod(mirror, "foo") + val abcde = getAsmMethod(mirror, "abcde") + + def t(m: MethodNode, r: List[(String, String, Int)]) = { + assertTrue((m.access & Opcodes.ACC_STATIC) != 0) + assertEquals(r, m.localVariables.asScala.toList.map(l => (l.desc, l.name, l.index))) + } + + t(foo, List(("Ljava/lang/String;", "value", 0))) + t(abcde, List(("Ljava/lang/String;", "value1", 0), ("J", "value2", 1), ("D", "value3", 3), ("I", "value4", 5), ("D", "value5", 6))) + } } diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxAndInlineTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxAndInlineTest.scala new file mode 100644 index 000000000000..7277f3a91797 --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxAndInlineTest.scala @@ -0,0 +1,106 @@ +package scala.tools.nsc.backend.jvm.opt + +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.asm.Opcodes +import scala.tools.asm.Opcodes._ +import scala.tools.asm.util.CheckMethodAdapter +import scala.tools.nsc.backend.jvm.MethodNode1 +import scala.tools.testkit.ASMConverters._ +import scala.tools.testkit.BytecodeTesting +import scala.tools.testkit.BytecodeTesting._ + +/** + * Tests for boxing/unboxing optimizations. + */ +@RunWith(classOf[JUnit4]) +class BoxUnboxAndInlineTest extends BytecodeTesting { + override def compilerArgs = "-opt:l:inline -opt-inline-from:**/*" + import compiler._ + + // Was crashing in 2.13.x once https://github.com/scala/scala/pull/9433 was merged in. + // Failure was: scala.tools.asm.tree.analysis.AnalyzerException: Error at instruction 16: Cannot pop operand off an empty stack. + // Discussion: https://github.com/scala/scala/pull/9495#issuecomment-779600132 + @Test + def unboxAsmCrash(): Unit = { + val code = + """package p1; class C { + |def f(b: java.lang.Byte) = { + | var box = 0 + | + | @inline def writeBox: Unit = { + | box = 1 + | } + | + | writeBox + |} + |}""".stripMargin + val c = compileClass(code) + assertSameSummary(getMethod(c, "f"), List(RETURN)) + + } + + // This bytecode pattern results from `unboxAsmCrash` in 2.13.x and exposes a bug in + // https://github.com/scala/scala/pull/9392, which landed originally in 2.12.x. + // + // The bytecode shape after the inliner differs in 2.12.x to masks the bug, probably due to + // https://github.com/scala/scala/pull/7133, which is 2.13.x only. + // + // This test constructs the problematic bytecode shape directly. Before the patch, it + // has an extra POP instruction which would be reported as invalid bytecode by CheckMethodAdapter. + // "Error at instruction 5: Cannot pop operand off an empty stack. m()V" + // + @Test + def unboxAsmCrashDirect(): Unit = { + val code: List[Instruction] = List( + Label(1), + Op(ACONST_NULL), + Invoke(INVOKESTATIC, "scala/runtime/ObjectRef", "create", "(Ljava/lang/Object;)Lscala/runtime/ObjectRef;", false), + VarOp(ASTORE, 1), + Op(ACONST_NULL), + VarOp(ALOAD, 1), + Op(POP), + VarOp(ASTORE, 2), + VarOp(ALOAD, 1), + VarOp(ALOAD, 2), + Field(PUTFIELD, "scala/runtime/ObjectRef", "elem", "Ljava/lang/Object;"), + Op(ACONST_NULL), + VarOp(ASTORE, 2), + Op(RETURN) + ) + val method = genMethod(localVars = List( + LocalVariable("this", "Lcom/foo/Bar;", None, Label(1), Label(1), 1), + LocalVariable("x", "Lscala/runtime/ObjectRef;", None, Label(1), Label(1), 1), + LocalVariable("y", "Lscala/runtime/ObjectRef;", None, Label(1), Label(1), 1), + // introduced by the box/unbox transform, we create the slot ahead of time. CheckMethodAdapter + // relies on it to verify the bytecode. + LocalVariable("z", "Lscala/runtime/ObjectRef;", None, Label(1), Label(1), 1) + ))(code: _*) + + val r = new compiler.global.Run() + compiler.global.enteringPhase(r.jvmPhase) { + compiler.global.genBCode.postProcessor.initialize() + val changed = compiler.global.genBCode.postProcessor.localOpt.boxUnbox.boxUnboxElimination(method, "p1.Owner") + assert(changed) + method.visitMaxs(2, 4) + val labelInsnIndices = new java.util.HashMap[scala.tools.asm.Label, java.lang.Integer]() + method.instructions.resetLabels() + + val checker = new CheckMethodAdapter(Opcodes.V1_8, "m", "()V", new MethodNode1(), labelInsnIndices) + method.accept(checker) + + assertSameCode(convertMethod(method), List( + Op(ACONST_NULL), + VarOp(ASTORE, 3), + Op(ACONST_NULL), + VarOp(ASTORE, 2), + VarOp(ALOAD, 2), + VarOp(ASTORE, 3), + Op(ACONST_NULL), + VarOp(ASTORE, 2), + Op(RETURN))) + } + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala index f9e228a19181..5cfd694aa7b1 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala @@ -145,6 +145,13 @@ class BoxUnboxTest extends BytecodeTesting { | bi + li | } | + | def t17(x: Int) = { // this one's pretty contrived but tests that primitives can be unboxed through a branch + | val wat: Any = if (x > 0) x else -x + | wat match { + | case i: Int => String valueOf i + | case _ => "?" + | } + | } |} """.stripMargin @@ -198,10 +205,11 @@ class BoxUnboxTest extends BytecodeTesting { assertDoesNotInvoke(getInstructions(c, "t16"), "boxToLong") assertDoesNotInvoke(getInstructions(c, "t16"), "unboxToInt") assertDoesNotInvoke(getInstructions(c, "t16"), "unboxToLong") + assertDoesNotInvoke(getMethod(c, "t17"), "boxToInteger") } @Test - def refEliminiation(): Unit = { + def refElimination(): Unit = { val code = """class C { | import runtime._ @@ -244,6 +252,12 @@ class BoxUnboxTest extends BytecodeTesting { | val res: IntRef = if (b) r1 else r2 | res.elem // boxes remain: can't rewrite this read, don't know which local | } + | + | // this case is contemplated by BoxUnbox despite my inability to provide a motivating example + | def t7(b: Boolean) = { + | val r1 = if (b) IntRef.zero() else IntRef.create(1) + | r1.elem + | } |} """.stripMargin val c = compileClass(code) @@ -255,6 +269,7 @@ class BoxUnboxTest extends BytecodeTesting { List("scala/runtime/IntRef.elem")) assertEquals(getInstructions(c, "t6") collect { case Field(op, owner, name, _) => s"$op $owner.$name" }, List(s"$PUTFIELD scala/runtime/IntRef.elem", s"$GETFIELD scala/runtime/IntRef.elem")) + assertNoInvoke(getMethod(c, "t7")) } @Test @@ -308,6 +323,21 @@ class BoxUnboxTest extends BytecodeTesting { | case (x, y) if x == y => 0 | case (x, y) => x + y | } + | + | def t10(a: Int, b: Int) = { // tuple is optimized away + | val (greater, lesser) = if (a > b) (a, b) else (b, a) + | greater - lesser + | } + | + | def t11(n: Int)(j: Int) = { // tuple is optimized away + | val (a, b, c, _) = n match { + | case 0 => (j, 0, 1, 1) + | case 1 => (0, j, 0, 1) + | case 2 => (1, 0, j, 0) + | case 3 => (1, 1, 0, j) + | } + | a + b + c + | } |} """.stripMargin val c = compileClass(code) @@ -327,6 +357,11 @@ class BoxUnboxTest extends BytecodeTesting { ILOAD, ILOAD, IADD, ILOAD, IADD, IRETURN)) assertNoInvoke(getMethod(c, "t8")) assertNoInvoke(getMethod(c, "t9")) + assertNoInvoke(getMethod(c, "t10")) + assertInvokedMethods(getMethod(c, "t11"), List( + "scala/runtime/BoxesRunTime.boxToInteger", // only once, for the MatchError + "scala/MatchError.", + )) } @Test @@ -352,4 +387,26 @@ class BoxUnboxTest extends BytecodeTesting { VarOp(ALOAD, 0), TypeOp(CHECKCAST, "java/lang/Integer"), Op(POP), Op(ICONST_0), Op(IRETURN))) } + + @Test + def unboxAsmCrash(): Unit = { + val code = + """ + |package p1 + | + |class AssertUtil { + | + | def waitForIt(terminated: => Boolean, progress: Int = 0, label: => String = "test"): Unit = { + | val limit = 5 + | var n = 1 + | var (dormancy, factor) = progress match { + | case 0 => (10000L, 5) + | case _ => (250L, 4) + | } + | () + | } + |}""".stripMargin + val m = getMethod(compileClass(code), "waitForIt") + assertSameCode(m, List(VarOp(ILOAD, 2), TableSwitch(TABLESWITCH, 0, 0, Label(4), List(Label(4))), Label(4), Op(RETURN))) + } } diff --git a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala index b1938a55a553..c32eed2182ca 100644 --- a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala +++ b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala @@ -1,10 +1,9 @@ package scala.tools.nsc.interpreter -import java.io.{PrintWriter, StringWriter} - import org.junit.Assert.{assertEquals, assertTrue} import org.junit.Test +import java.io.{PrintWriter, StringWriter} import scala.reflect.internal.util.{BatchSourceFile, SourceFile} import scala.tools.nsc.Settings import scala.tools.nsc.interpreter.shell._ @@ -36,6 +35,28 @@ class CompletionTest { (completer, intp, acc) } + private def commandInterpretLines(): (Completion, Repl, Accumulator) = { + val intp = newIMain() + class CommandMock extends LoopCommands { + override protected def echo(msg: String): Unit = ??? + override protected def out: PrintWriter = ??? + override def commands: List[LoopCommand] = { + val default = (string: String) => Result.default + List( + LoopCommand.cmd("paste", "[-raw] [path]", "enter paste mode or paste a file", default), + LoopCommand.cmd("paste", "[-raw] [path]", "enter paste mode or paste a file", default)// Other commands + ) + } + } + val acc = new Accumulator + val shellCompletion = new Completion { + override def complete(buffer: String, cursor: Int, filter: Boolean) = + if (buffer.startsWith(":")) new CommandMock().colonCompletion(buffer, cursor).complete(buffer, cursor) + else NoCompletions + } + (shellCompletion, intp, acc) + } + implicit class BeforeAfterCompletion(completion: Completion) { def complete(before: String, after: String = ""): CompletionResult = completion.complete(before + after, before.length) @@ -85,7 +106,7 @@ class CompletionTest { checkExact(completer, "asInstanceO", "", includeUniversal = true)("asInstanceOf") // Output is sorted - assertEquals(List("prefix_aaa", "prefix_nnn", "prefix_zzz"), completer.complete( """class C { def prefix_nnn = 0; def prefix_zzz = 0; def prefix_aaa = 0; prefix_""").candidates.filter(!_.isUniversal).map(_.defString)) + assertEquals(List("prefix_aaa", "prefix_nnn", "prefix_zzz"), completer.complete( """class C { def prefix_nnn = 0; def prefix_zzz = 0; def prefix_aaa = 0; prefix_""").candidates.filter(!_.isUniversal).map(_.name)) // Enable implicits to check completion enrichment checkExact(completer, """'c'.toU""")("toUpper") @@ -151,11 +172,8 @@ class CompletionTest { def defStringConstructor(): Unit = { val intp = newIMain() val completer = new ReplCompletion(intp) - checkExact(completer, "class Shazam(i: Int); new Shaza")("Shazam") - checkExact(completer, "class Shazam(i: Int); new Shazam")(EmptyString, "def (i: Int): Shazam") - - checkExact(completer, "class Shazam(i: Int) { def this(x: String) = this(0) }; new Shaza")("Shazam") - checkExact(completer, "class Shazam(i: Int) { def this(x: String) = this(0) }; new Shazam")(EmptyString, "def (i: Int): Shazam", "def (x: String): Shazam") + checkExact(completer, "class Shazam(i: Int); new Shazam", result = _.declString())("def (i: Int): Shazam") + checkExact(completer, "class Shazam(i: Int) { def this(x: String) = this(0) }; new Shazam", result = _.declString())("def (i: Int): Shazam", "def (x: String): Shazam") } @Test @@ -191,7 +209,7 @@ class CompletionTest { | .map(_ + 1) /* then we do reverse */ | .rev""".stripMargin assertTrue( - completer.complete(withMultilineCommit).candidates.map(_.defString).contains("reverseMap") + completer.complete(withMultilineCommit).candidates.map(_.name).contains("reverseMap") ) val withInlineCommit = @@ -199,7 +217,7 @@ class CompletionTest { | .map(_ + 1) // then we do reverse | .rev""".stripMargin assertTrue( - completer.complete(withInlineCommit).candidates.map(_.defString).contains("reverseMap") + completer.complete(withInlineCommit).candidates.map(_.name).contains("reverseMap") ) } @@ -217,6 +235,30 @@ class CompletionTest { assertTrue(candidates2.forall(_.isDeprecated)) } + @Test + def isDeprecatedOverrideMethod(): Unit = { + val (completer, _, _) = interpretLines( + """object Stale { def oldie(i: Int) = ???; @deprecated("","") def oldie = ??? }""" + ) + val candidates1 = completer.complete("Stale.ol").candidates + assertEquals(2, candidates1.size) + // Our JLine Reader is now responsible for only displaying @deprecated if all candidates with the name are + // deprecated. That isn't covered by this test. + assertEquals(candidates1.head.isDeprecated, true) + assertEquals(candidates1.last.isDeprecated, false) + } + + @Test + def isDeprecatedOverrideMethodDefString(): Unit = { + val (completer, _, _) = interpretLines( + """object Stale { def oldie(i: Int) = ???; @deprecated("","") def oldie = ??? }""" + ) + val candidates1 = completer.complete("Stale.oldie").candidates + assertEquals(2, candidates1.size) + assertEquals(candidates1.filter(_.isDeprecated).map(_.declString().contains("deprecated")).head, true) + assertEquals(candidates1.last.isDeprecated, false) + } + @Test def isDeprecatedInMethodDesc(): Unit = { val (completer, _, _) = interpretLines( @@ -224,11 +266,22 @@ class CompletionTest { """object Stuff { @deprecated("","") def `this` = ??? ; @deprecated("","") def `that` = ??? }""" ) val candidates1 = completer.complete("Stale.oldie").candidates - assertEquals(2, candidates1.size) // When exactly matched, there is an empty character - assertTrue(candidates1.last.defString.contains("deprecated")) + assertEquals(1, candidates1.size) // When exactly matched, there is an empty character + assertTrue(candidates1.filter(_.declString().contains("oldie")).head.declString().contains("deprecated")) val candidates2 = completer.complete("Stuff.that").candidates - assertEquals(2, candidates2.size) - assertTrue(candidates2.last.defString.contains("deprecated")) + assertEquals(1, candidates2.size) + assertTrue(candidates2.filter(_.declString().contains("that")).head.declString().contains("deprecated")) + } + + @Test + def jline3Matcher(): Unit = { + val (completer, _, _) = commandInterpretLines() + val candidates1 = completer.complete(":p").candidates + assertEquals(2, candidates1.size) + + // Save the line to the CompletionResult of the matcher, and select the command to match successfully. + val completionResult = completer.complete(":p") + assertEquals(completionResult.line, ":p") } @Test @@ -247,9 +300,9 @@ class CompletionTest { """object A { class Type; object Term }""" ) val candidates1 = completer.complete("A.T").candidates - assertEquals("Term", candidates1.map(_.defString).mkString(" ")) + assertEquals("Term", candidates1.map(_.name).mkString(" ")) val candidates2 = completer.complete("import A.T").candidates - assertEquals("Term Type", candidates2.map(_.defString).sorted.mkString(" ")) + assertEquals("Term Type", candidates2.map(_.name).sorted.mkString(" ")) } @Test @@ -294,11 +347,12 @@ object Test2 { checkExact(completer, "test.Test.withoutParens.charA")("charAt") } - def checkExact(completer: Completion, before: String, after: String = "", includeUniversal: Boolean = false)(expected: String*): Unit = { - val actual = - completer.complete(before, after).candidates - .filter(c => includeUniversal || !c.isUniversal) - .map(_.defString) + def checkExact(completer: Completion, before: String, after: String = "", includeUniversal: Boolean = false, + result: CompletionCandidate => String = _.name)(expected: String*): Unit = { + val candidates = completer.complete(before, after).candidates + .filter(c => includeUniversal || !c.isUniversal) + val actual = candidates.map(result) assertEquals(expected.sorted.mkString(" "), actual.toSeq.distinct.sorted.mkString(" ")) } + } diff --git a/test/junit/scala/tools/nsc/parser/ParserTest.scala b/test/junit/scala/tools/nsc/parser/ParserTest.scala index f5522b562574..dd5f562fe67e 100644 --- a/test/junit/scala/tools/nsc/parser/ParserTest.scala +++ b/test/junit/scala/tools/nsc/parser/ParserTest.scala @@ -1,9 +1,9 @@ package scala.tools.nsc.parser -import org.junit.Assert._ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.junit.Assert._ import scala.tools.testkit.BytecodeTesting @@ -30,4 +30,19 @@ class ParserTest extends BytecodeTesting{ assertFalse(reporter.hasErrors) run.compileSources(newSourceFile(crlfCode) :: Nil) } + + @Test + def rangePosOfDefaultInitializer_t12213(): Unit = { + val code = + """object Other { var x: Int = _; var y: Int = 42 }""" + import compiler._, global._ + val run = new Run + run.compileSources(newSourceFile(code) :: Nil) + assertFalse(reporter.hasErrors) + val unit = run.units.toList.head + def codeOf(pos: Position) = new String(pos.source.content.slice(pos.start, pos.end)) + val List(x, y) = unit.body.collect { case vd : ValDef => vd }.takeRight(2) + assertEquals("var y: Int = 42", codeOf(y.pos)) + assertEquals("var x: Int = _", codeOf(x.pos)) + } } diff --git a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala index a499e2aa1a1c..0c0a6b96f286 100644 --- a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala +++ b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala @@ -1,6 +1,6 @@ package scala.tools.nsc.typechecker -import org.junit.Assert.assertEquals +import org.junit.Assert.{assertEquals, assertNotEquals} import org.junit.Test import scala.tools.testkit.BytecodeTesting @@ -24,4 +24,42 @@ class TypedTreeTest extends BytecodeTesting { val List(t) = tree.filter(_.attachments.all.nonEmpty).toList assertEquals("42:Set(OriginalTreeAttachment(O.x))", s"$t:${t.attachments.all}") } + + + // Ensure SingletonTypeTree#ref is typed and it has symbol after typing. + // see: https://github.com/scala/bug/issues/12296 + @Test + def singletonTypeTreeRefTyped(): Unit = { + val code = + """|object root { + | object impl + | val f: impl.type => Unit = { + | case _: impl.type => () + | } + |} + """.stripMargin + val run = compiler.newRun() + run.compileSources(List(BytecodeTesting.makeSourceFile(code, "UnitTestSingletonTypeTreeSource.scala"))) + val tree = run.units.next().body + + import compiler.global._ + + val singletonTypeTrees = collection.mutable.Buffer[SingletonTypeTree]() + object traverser extends Traverser { + override def traverse(t: Tree): Unit = { + t match { + case tt: TypeTree if tt.original != null => traverse(tt.original) + case st: SingletonTypeTree => + singletonTypeTrees += st + case _ => super.traverse(t) + } + } + } + traverser.traverse(tree) + + singletonTypeTrees.foreach { t => + assertEquals(t.ref.symbol.fullName, "root.impl") + assertNotEquals(NoPosition, t.pos) + } + } } diff --git a/test/scalacheck/CheckEither.scala b/test/scalacheck/CheckEither.scala index cf6b2e2f8558..c650cee4ade3 100644 --- a/test/scalacheck/CheckEither.scala +++ b/test/scalacheck/CheckEither.scala @@ -4,8 +4,16 @@ import org.scalacheck.Gen.oneOf import org.scalacheck.Prop._ import org.scalacheck.Test.check import Function.tupled +import scala.util.Either.LeftProjection +@annotation.nowarn("cat=deprecation") object CheckEitherTest extends Properties("Either") { + implicit class Failing[A, B](val e: Either[A, B]) { + def orFail = e.getOrElse(???) + } + implicit class FailingLeft[A, B](val e: LeftProjection[A, B]) { + def orFail = e.getOrElse(???) + } implicit def arbitraryEither[X, Y](implicit xa: Arbitrary[X], ya: Arbitrary[Y]): Arbitrary[Either[X, Y]] = Arbitrary[Either[X, Y]](oneOf(arbitrary[X].map(Left(_)), arbitrary[Y].map(Right(_)))) @@ -14,14 +22,14 @@ object CheckEitherTest extends Properties("Either") { val prop_either2 = forAll((n: Int) => Right(n).fold(a => sys.error("fail"), x => x) == n) val prop_swap = forAll((e: Either[Int, Int]) => e match { - case Left(a) => e.swap.right.get == a - case Right(b) => e.swap.left.get == b + case Left(a) => e.swap.orFail == a + case Right(b) => e.swap.left.orFail == b }) val prop_isLeftRight = forAll((e: Either[Int, Int]) => e.isLeft != e.isRight) object CheckLeftProjection { - val prop_value = forAll((n: Int) => Left(n).left.get == n) + val prop_value = forAll((n: Int) => Left(n).left.orFail == n) val prop_getOrElse = forAll((e: Either[Int, Int], or: Int) => e.left.getOrElse(or) == (e match { case Left(a) => a @@ -29,10 +37,10 @@ object CheckEitherTest extends Properties("Either") { })) val prop_forall = forAll((e: Either[Int, Int]) => - e.left.forall(_ % 2 == 0) == (e.isRight || e.left.get % 2 == 0)) + e.left.forall(_ % 2 == 0) == (e.isRight || e.left.orFail % 2 == 0)) val prop_exists = forAll((e: Either[Int, Int]) => - e.left.exists(_ % 2 == 0) == (e.isLeft && e.left.get % 2 == 0)) + e.left.exists(_ % 2 == 0) == (e.isLeft && e.left.orFail % 2 == 0)) val prop_flatMapLeftIdentity = forAll((e: Either[Int, Int], n: Int, s: String) => { def f(x: Int) = if(x % 2 == 0) Left(s) else Right(s) @@ -53,7 +61,7 @@ object CheckEitherTest extends Properties("Either") { e.left.map(x => f(g(x))) == e.left.map(x => g(x)).left.map(f(_))}) val prop_filterToOption = forAll((e: Either[Int, Int], x: Int) => e.left.filterToOption(_ % 2 == 0) == - (if(e.isRight || e.left.get % 2 != 0) None else Some(e))) + (if(e.isRight || e.left.orFail % 2 != 0) None else Some(e))) val prop_seq = forAll((e: Either[Int, Int]) => e.left.toSeq == (e match { case Left(a) => Seq(a) @@ -67,46 +75,46 @@ object CheckEitherTest extends Properties("Either") { } object CheckRightProjection { - val prop_value = forAll((n: Int) => Right(n).right.get == n) + val prop_value = forAll((n: Int) => Right(n).orFail == n) - val prop_getOrElse = forAll((e: Either[Int, Int], or: Int) => e.right.getOrElse(or) == (e match { + val prop_getOrElse = forAll((e: Either[Int, Int], or: Int) => e.getOrElse(or) == (e match { case Left(_) => or case Right(b) => b })) val prop_forall = forAll((e: Either[Int, Int]) => - e.right.forall(_ % 2 == 0) == (e.isLeft || e.right.get % 2 == 0)) + e.forall(_ % 2 == 0) == (e.isLeft || e.orFail % 2 == 0)) val prop_exists = forAll((e: Either[Int, Int]) => - e.right.exists(_ % 2 == 0) == (e.isRight && e.right.get % 2 == 0)) + e.exists(_ % 2 == 0) == (e.isRight && e.orFail % 2 == 0)) val prop_flatMapLeftIdentity = forAll((e: Either[Int, Int], n: Int, s: String) => { def f(x: Int) = if(x % 2 == 0) Left(s) else Right(s) - Right(n).right.flatMap(f(_)) == f(n)}) + Right(n).flatMap(f(_)) == f(n)}) - val prop_flatMapRightIdentity = forAll((e: Either[Int, Int]) => e.right.flatMap(Right(_)) == e) + val prop_flatMapRightIdentity = forAll((e: Either[Int, Int]) => e.flatMap(Right(_)) == e) val prop_flatMapComposition = forAll((e: Either[Int, Int]) => { def f(x: Int) = if(x % 2 == 0) Left(x) else Right(x) def g(x: Int) = if(x % 7 == 0) Right(x) else Left(x) - e.right.flatMap(f(_)).right.flatMap(g(_)) == e.right.flatMap(f(_).right.flatMap(g(_)))}) + e.flatMap(f(_)).flatMap(g(_)) == e.flatMap(f(_).flatMap(g(_)))}) - val prop_mapIdentity = forAll((e: Either[Int, Int]) => e.right.map(x => x) == e) + val prop_mapIdentity = forAll((e: Either[Int, Int]) => e.map(x => x) == e) val prop_mapComposition = forAll((e: Either[Int, String]) => { def f(s: String) = s.toLowerCase def g(s: String) = s.reverse - e.right.map(x => f(g(x))) == e.right.map(x => g(x)).right.map(f(_))}) + e.map(x => f(g(x))) == e.map(x => g(x)).map(f(_))}) val prop_filterToOption = forAll((e: Either[Int, Int], x: Int) => e.right.filterToOption(_ % 2 == 0) == - (if(e.isLeft || e.right.get % 2 != 0) None else Some(e))) + (if(e.isLeft || e.orFail % 2 != 0) None else Some(e))) - val prop_seq = forAll((e: Either[Int, Int]) => e.right.toSeq == (e match { + val prop_seq = forAll((e: Either[Int, Int]) => e.toSeq == (e match { case Left(_) => Seq.empty case Right(b) => Seq(b) })) - val prop_option = forAll((e: Either[Int, Int]) => e.right.toOption == (e match { + val prop_option = forAll((e: Either[Int, Int]) => e.toOption == (e match { case Left(_) => None case Right(b) => Some(b) })) @@ -114,7 +122,7 @@ object CheckEitherTest extends Properties("Either") { val prop_Either_left = forAll((n: Int) => Left(n).left.get == n) - val prop_Either_right = forAll((n: Int) => Right(n).right.get == n) + val prop_Either_right = forAll((n: Int) => Right(n).orFail == n) val prop_Either_joinLeft = forAll((e: Either[Either[Int, Int], Int]) => e match { case Left(ee) => e.joinLeft == ee diff --git a/test/scalacheck/Ctrie.scala b/test/scalacheck/Ctrie.scala index 6101105f06fc..9c120c552566 100644 --- a/test/scalacheck/Ctrie.scala +++ b/test/scalacheck/Ctrie.scala @@ -3,8 +3,7 @@ import Prop._ import org.scalacheck.Gen._ import collection._ import collection.concurrent.TrieMap - - +import scala.language.reflectiveCalls case class Wrap(i: Int) { override def hashCode = i // * 0x9e3775cd @@ -192,8 +191,8 @@ object CtrieTest extends Properties("concurrent.TrieMap") { idx => (0 until sz) foreach { i => - val v = ct.getOrElseUpdate(Wrap(i), idx + ":" + i) - if (v == idx + ":" + i) totalInserts.incrementAndGet() + val v = ct.getOrElseUpdate(Wrap(i), s"$idx:$i") + if (v == s"$idx:$i") totalInserts.incrementAndGet() } } diff --git a/test/scalacheck/concurrent-map.scala b/test/scalacheck/concurrent-map.scala index 75082e8bd09c..f3c529922269 100644 --- a/test/scalacheck/concurrent-map.scala +++ b/test/scalacheck/concurrent-map.scala @@ -1,6 +1,6 @@ import java.util.concurrent._ import scala.collection._ -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters._ import org.scalacheck._ import org.scalacheck.Prop._ import org.scalacheck.Gen._ @@ -26,6 +26,7 @@ object ConcurrentMapTest extends Properties("concurrent.TrieMap") { /* helpers */ def inParallel[T](totalThreads: Int)(body: Int => T): Seq[T] = { + import scala.language.reflectiveCalls val threads = for (idx <- 0 until totalThreads) yield new Thread { setName("ParThread-" + idx) private var res: T = _ diff --git a/test/scalacheck/range.scala b/test/scalacheck/range.scala index 3344d3be6315..f06606b59fbc 100644 --- a/test/scalacheck/range.scala +++ b/test/scalacheck/range.scala @@ -43,9 +43,9 @@ abstract class RangeTest(kind: String) extends Properties("Range "+kind) { size <- choose(1, 100) step <- choose(1, 101) } yield { - val signum = if (boundary == 0) 1 else boundary.signum - if (isStart) Range(boundary, boundary - size * boundary.signum, - step * signum) - else Range(boundary - size * boundary.signum, boundary, step * signum) + val signum = if (boundary == 0) 1 else boundary.sign + if (isStart) Range(boundary, boundary - size * boundary.sign, - step * signum) + else Range(boundary - size * boundary.sign, boundary, step * signum) } diff --git a/test/scalacheck/redblacktree.scala b/test/scalacheck/redblacktree.scala index ea5cab8c1dcd..02c7597548b3 100644 --- a/test/scalacheck/redblacktree.scala +++ b/test/scalacheck/redblacktree.scala @@ -24,7 +24,7 @@ abstract class RedBlackTreeTest(tname: String) extends Properties(tname) with Re import RB._ def nodeAt[A](tree: Tree[String, A], n: Int): Option[(String, A)] = if (n < iterator(tree).size && n >= 0) - Some(iterator(tree).drop(n).next) + Some(iterator(tree).drop(n).next()) else None @@ -400,7 +400,7 @@ object TestPartitionLeft extends RedBlackTreeTest("RedBlackTree.partitionKeysLef override type ModifyParm = Int override def genParm(tree: Tree[String, Int]): Gen[ModifyParm] = choose(0, 0) override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = - partitionKeys[String, Int](tree, k => k.hashCode % 2 == 0)._1 + partitionEntries[String, Int](tree, (k, v) => k.hashCode % 2 == 0)._1 property("partition") = forAll(genInput) { case (tree, parm, newTree) => iterator(tree).filter(t => t._1.hashCode % 2 == 0).toList == iterator(newTree).toList @@ -413,7 +413,7 @@ object TestPartitionRight extends RedBlackTreeTest("RedBlackTree.partitionKeysRi override type ModifyParm = Int override def genParm(tree: Tree[String, Int]): Gen[ModifyParm] = choose(0, 0) override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = - partitionKeys[String, Int](tree, k => k.hashCode % 2 == 0)._2 + partitionEntries[String, Int](tree, (k, v) => k.hashCode % 2 == 0)._2 property("partition") = forAll(genInput) { case (tree, parm, newTree) => iterator(tree).filter(t => t._1.hashCode % 2 != 0).toList == iterator(newTree).toList diff --git a/test/scalacheck/scala/ArrayTest.scala b/test/scalacheck/scala/ArrayTest.scala index e08c77e3e8af..a51562d5d88e 100644 --- a/test/scalacheck/scala/ArrayTest.scala +++ b/test/scalacheck/scala/ArrayTest.scala @@ -25,9 +25,9 @@ object ArrayTest extends Properties("Array") { property("fill") = forAll( Gen.choose(-10, 100), ) { len => - val xs = Vector.fill(len)(Random.nextInt) + val xs = Vector.fill(len)(Random.nextInt()) val i = xs.iterator - Array.fill(len)(i.next).toVector == xs + Array.fill(len)(i.next()).toVector == xs } property("tabulate") = forAll( diff --git a/test/scalacheck/scala/collection/FloatFormatTest.scala b/test/scalacheck/scala/collection/FloatFormatTest.scala index 7dd3989fab9f..6a70352fde82 100644 --- a/test/scalacheck/scala/collection/FloatFormatTest.scala +++ b/test/scalacheck/scala/collection/FloatFormatTest.scala @@ -81,8 +81,8 @@ object FloatFormatTest extends Properties("FloatFormat") { 10 -> right )) - // type annotation shouldn't be necessary? see typelevel/scalacheck#721 - Gen.sequence[List[String], String](bogoparts).map(_.mkString) + import scala.jdk.CollectionConverters._ + Gen.sequence(bogoparts).map(_.asScala.mkString) } //compare NaN equal diff --git a/test/scalacheck/scala/collection/IndexOfSliceTest.scala b/test/scalacheck/scala/collection/IndexOfSliceTest.scala index 3853139d340f..50c11d5cea25 100644 --- a/test/scalacheck/scala/collection/IndexOfSliceTest.scala +++ b/test/scalacheck/scala/collection/IndexOfSliceTest.scala @@ -8,6 +8,7 @@ object IndexOfSliceTest extends Properties("indexOfSlice") { // The default arbitrary[Seq[Int]] picks only one Seq implementation. // Here we explicitly list all the implementations we want to test + @annotation.nowarn("msg=type WrappedArray") val genDifferentSeqs = Gen.oneOf[Seq[Int]]( Arbitrary.arbitrary[collection.immutable.List[Int]], diff --git a/test/scalacheck/scala/collection/IntegralParseTest.scala b/test/scalacheck/scala/collection/IntegralParseTest.scala index 6fd4e229551c..b49466e9bb15 100644 --- a/test/scalacheck/scala/collection/IntegralParseTest.scala +++ b/test/scalacheck/scala/collection/IntegralParseTest.scala @@ -120,8 +120,11 @@ object NumericStringGenerators { if (n >= 0) Gen.oneOf(digitsByValue(n)) else Gen.const(ch) }) - // type annotation shouldn't be necessary? see typelevel/scalacheck#721 - Gen.sequence[List[Char], Char](listOfGens).map(_.mkString) + + import scala.jdk.CollectionConverters._ + + val sequenced = Gen.sequence(listOfGens) + sequenced.map(_.asScala.mkString) } } diff --git a/test/scalacheck/scala/collection/IteratorProperties.scala b/test/scalacheck/scala/collection/IteratorProperties.scala index 820cbaa11748..d20e24c33b7d 100644 --- a/test/scalacheck/scala/collection/IteratorProperties.scala +++ b/test/scalacheck/scala/collection/IteratorProperties.scala @@ -34,12 +34,12 @@ object IteratorProperties extends Properties("Iterator") { val indexed = s.toIndexedSeq // IndexedSeqs and their Iterators have a knownSize val simple = new SimpleIterable(s) // SimpleIterable and its Iterator don't val stream = LazyList.from(s) // Lazy - val indexed1 = f(indexed, n).toSeq - val indexed2 = f(indexed.iterator, n).toSeq - val simple1 = f(simple, n).toSeq - val simple2 = f(simple.iterator, n).toSeq - val stream1 = f(stream, n).toSeq - val stream2 = f(stream.iterator, n).toSeq + val indexed1 = f(indexed, n).iterator.to(Seq) + val indexed2 = f(indexed.iterator, n).iterator.to(Seq) + val simple1 = f(simple, n).iterator.to(Seq) + val simple2 = f(simple.iterator, n).iterator.to(Seq) + val stream1 = f(stream, n).iterator.to(Seq) + val stream2 = f(stream.iterator, n).iterator.to(Seq) (indexed1 == indexed2) :| s"indexed: $indexed1 != $indexed2" && (simple1 == simple2) :| s"simple: $simple1 != $simple2" && (stream1 == stream2) :| s"stream: $stream1 != $stream2" && diff --git a/test/scalacheck/scala/collection/StringOpsProps.scala b/test/scalacheck/scala/collection/StringOpsProps.scala index b902512e39e7..bdade1547a72 100644 --- a/test/scalacheck/scala/collection/StringOpsProps.scala +++ b/test/scalacheck/scala/collection/StringOpsProps.scala @@ -6,7 +6,7 @@ import java.io.{BufferedReader, StringReader} import org.scalacheck.{Gen, Properties}, Gen.{oneOf, listOf} import org.scalacheck.Prop._ -import JavaConverters._ +import scala.jdk.CollectionConverters._ object StringOpsTest extends Properties("StringOps") { diff --git a/test/scalacheck/scala/collection/immutable/ImmutableChampHashMapProperties.scala b/test/scalacheck/scala/collection/immutable/ImmutableChampHashMapProperties.scala index fa41faa4b724..2a61d5fe0382 100644 --- a/test/scalacheck/scala/collection/immutable/ImmutableChampHashMapProperties.scala +++ b/test/scalacheck/scala/collection/immutable/ImmutableChampHashMapProperties.scala @@ -33,7 +33,7 @@ object ImmutableChampHashMapProperties extends Properties("HashMap") { val builder = HashMap.newBuilder[K, V] inputMap.foreach(builder.addOne) - val duplicateMap = builder.result + val duplicateMap = builder.result() inputMap == duplicateMap } @@ -72,7 +72,7 @@ object ImmutableChampHashMapProperties extends Properties("HashMap") { property("adding elems twice to builder is the same as adding them once") = forAll { seq: Seq[(K, V)] => val b = HashMap.newBuilder[K, V].addAll(seq) - b.result == b.addAll(seq).result() + b.result() == b.addAll(seq).result() } property("(xs ++ ys).toMap == xs.toMap ++ ys.toMap") = forAll { (xs: Seq[(K, V)],ys: Seq[(K, V)]) => diff --git a/test/scalacheck/scala/collection/immutable/ImmutableChampHashSetProperties.scala b/test/scalacheck/scala/collection/immutable/ImmutableChampHashSetProperties.scala index 62ea4d75257b..7331f78c64b3 100644 --- a/test/scalacheck/scala/collection/immutable/ImmutableChampHashSetProperties.scala +++ b/test/scalacheck/scala/collection/immutable/ImmutableChampHashSetProperties.scala @@ -38,7 +38,7 @@ object ImmutableChampHashSetProperties extends Properties("immutable.HashSet") { val builder = HashSet.newBuilder[K] inputSet.foreach(builder.addOne) - val duplicateSet = builder.result + val duplicateSet = builder.result() inputSet == duplicateSet } @@ -64,7 +64,7 @@ object ImmutableChampHashSetProperties extends Properties("immutable.HashSet") { val builder = HashSet.newBuilder[K] inputShared.foreach(builder.addOne) - val duplicateSet = builder.result + val duplicateSet = builder.result() inputShared == inputShared.intersect(duplicateSet) } @@ -121,7 +121,7 @@ object ImmutableChampHashSetProperties extends Properties("immutable.HashSet") { val builder = HashSet.newBuilder[K] inputShared.foreach(builder.addOne) - val duplicateSet = builder.result + val duplicateSet = builder.result() inputShared == inputShared.union(duplicateSet) } @@ -166,7 +166,7 @@ object ImmutableChampHashSetProperties extends Properties("immutable.HashSet") { val builder = HashSet.newBuilder[K] inputShared.foreach(builder.addOne) - val duplicateSet = builder.result + val duplicateSet = builder.result() HashSet.empty[K] == inputShared.diff(duplicateSet) } @@ -240,7 +240,7 @@ object ImmutableChampHashSetProperties extends Properties("immutable.HashSet") { } property("adding elems twice to builder is the same as adding them once") = forAll { seq: Seq[K] => val b = HashSet.newBuilder[K].addAll(seq) - b.result == b.addAll(seq).result() + b.result() == b.addAll(seq).result() } property("(xs ++ ys).toSet == xs.toSet ++ ys.toSet") = forAll { (xs: Seq[K],ys: Seq[K]) => (xs ++ ys).toSet =? xs.toSet ++ ys.toSet diff --git a/test/scalacheck/scala/collection/immutable/SeqProperties.scala b/test/scalacheck/scala/collection/immutable/SeqProperties.scala index 1086506da5ee..0cd7ecbcbb4f 100644 --- a/test/scalacheck/scala/collection/immutable/SeqProperties.scala +++ b/test/scalacheck/scala/collection/immutable/SeqProperties.scala @@ -11,6 +11,7 @@ import scala.util.{Success, Try} import org.scalacheck.Properties +@annotation.nowarn("cat=deprecation&msg=Stream") object SeqProperties extends Properties("immutable.Seq builder implementations"){ type A = Int diff --git a/test/scalacheck/scala/collection/immutable/SetProperties.scala b/test/scalacheck/scala/collection/immutable/SetProperties.scala index f100b7292f48..f34a303cc164 100644 --- a/test/scalacheck/scala/collection/immutable/SetProperties.scala +++ b/test/scalacheck/scala/collection/immutable/SetProperties.scala @@ -7,8 +7,8 @@ import org.scalacheck.commands.Commands import scala.collection.mutable import scala.util.{Success, Try} - -object SetProperties extends Properties("immutable.Set builder implementations"){ +@annotation.nowarn("cat=deprecation&msg=Stream") +object SetProperties extends Properties("immutable.Set builder implementations") { type A = Int @@ -60,6 +60,7 @@ class SetBuilderStateProperties[A, To <: Set[A]](newBuilder: => mutable.Builder[ override def genCommand(state: State): Gen[Command] = _genCommand + @annotation.nowarn("cat=deprecation&msg=Stream") override def shrinkState = Shrink.apply[State]( set => set.to(Stream).map(set - _) ) case object Clear extends UnitCommand { diff --git a/test/scalacheck/scala/collection/mutable/MapProperties.scala b/test/scalacheck/scala/collection/mutable/MapProperties.scala index a77365d5a244..22394a1931c5 100644 --- a/test/scalacheck/scala/collection/mutable/MapProperties.scala +++ b/test/scalacheck/scala/collection/mutable/MapProperties.scala @@ -33,6 +33,7 @@ object MapProperties extends Properties("mutable.Map") { override def addOne(elem: (K, V)): this.type = { _elems += elem; this } } + @annotation.nowarn("cat=deprecation&msg=ListMap") implicit val arbMap: Arbitrary[Map[K, V]] = Arbitrary { for { @@ -52,4 +53,4 @@ object MapProperties extends Properties("mutable.Map") { map.filterInPlace(p) (map: collection.Map[K, V]) ?= expected } -} \ No newline at end of file +} diff --git a/test/scalacheck/scala/collection/mutable/RedBlackTree.scala b/test/scalacheck/scala/collection/mutable/RedBlackTree.scala index a6613309bc7a..c643a3d4c104 100644 --- a/test/scalacheck/scala/collection/mutable/RedBlackTree.scala +++ b/test/scalacheck/scala/collection/mutable/RedBlackTree.scala @@ -24,7 +24,7 @@ abstract class RedBlackTreeTest(tname: String) extends Properties(tname) with Re import RB._ def nodeAt[A](tree: Tree[String, A], n: Int): Option[(String, A)] = if (n < iterator(tree).size && n >= 0) - Some(iterator(tree).drop(n).next) + Some(iterator(tree).drop(n).next()) else None diff --git a/test/scalacheck/scala/math/BigIntProperties.scala b/test/scalacheck/scala/math/BigIntProperties.scala index c4c0295dc50a..d036719b368f 100644 --- a/test/scalacheck/scala/math/BigIntProperties.scala +++ b/test/scalacheck/scala/math/BigIntProperties.scala @@ -61,6 +61,7 @@ object BigIntProperties extends Properties("BigInt") { property("longValue") = forAll { (l: Long) => BigInt(l).longValue ?= l } property("toLong") = forAll { (l: Long) => BigInt(l).toLong ?= l } + property("new BigInt(bigInteger = BigInteger.ZERO)") = (new BigInt(bigInteger = BigInteger.ZERO)) == 0 property("BigInt.apply(i: Int)") = forAll { (i: Int) => BigInt(i) ?= BigInt(BigInteger.valueOf(i)) } property("BigInt.apply(l: Long)") = forAll { (l: Long) => BigInt(l) ?= BigInt(BigInteger.valueOf(l)) } property("BigInt.apply(x: Array[Byte])") = forAll(bigInteger) { bi => BigInt(bi) ?= BigInt(bi.toByteArray) } diff --git a/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala b/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala index 0541405f1c7d..19032a2d0fb7 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/ArbitraryTreesAndNames.scala @@ -1,6 +1,7 @@ package scala.reflect.quasiquotes import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.language.implicitConversions import scala.reflect.runtime.universe._, internal._, Flag._ trait ArbitraryTreesAndNames { diff --git a/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala index 6d4526bfcb47..2356f272038c 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala @@ -1,6 +1,7 @@ package scala.reflect.quasiquotes import org.scalacheck._, Prop._, Gen._, Arbitrary._ +import scala.language.reflectiveCalls import scala.reflect.runtime.universe._, Flag._, internal.reificationSupport.ScalaDot object DefinitionConstructionProps @@ -12,8 +13,10 @@ object DefinitionConstructionProps with PatDefConstruction with DefConstruction with PackageConstruction - with ImportConstruction { + with ImportConstruction + with QuasiquoteSliceTypeTests +trait QuasiquoteSliceTypeTests { self: QuasiquoteProperties => val x: Tree = q"val x: Int" property("scala/bug#6842 a1") = test { assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0") } property("scala/bug#6842 a2") = test { assertEqAst(q"class C($x)", "class C(val x: Int)") } @@ -32,6 +35,7 @@ trait ClassConstruction { self: QuasiquoteProperties => val emptyConstructor = DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(pendingSuperCall), Literal(Constant(())))) + @annotation.nowarn("cat=deprecation&msg=emptyValDef") def classWith(name: TypeName, parents: List[Tree] = List(anyRef), body: List[DefDef] = Nil) = ClassDef( Modifiers(), name, List(), @@ -229,7 +233,7 @@ trait ValDefConstruction { self: QuasiquoteProperties => q"var $name: $tpt = $rhs" ≈ ValDef(Modifiers(MUTABLE), name, tpt, rhs) } - // left tree is not a pattern due to Si-8211 + // left tree is not a pattern due to scala/bug#8211 property("scala/bug#8202") = test { assertEqAst(q"val (x: Int) = 1", "val x: Int = 1") } diff --git a/test/scalacheck/scala/reflect/quasiquotes/DeprecationProps.scala b/test/scalacheck/scala/reflect/quasiquotes/DeprecationProps.scala index 9439a5a2c69f..cccb06144ce5 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/DeprecationProps.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/DeprecationProps.scala @@ -3,6 +3,7 @@ package scala.reflect.quasiquotes import org.scalacheck._, Prop._, Gen._, Arbitrary._ import scala.reflect.runtime.universe._ +@annotation.nowarn("cat=deprecation") object DeprecationProps extends QuasiquoteProperties("deprecation") { val tname = TypeName("Foo") val tpt = tq"Foo" diff --git a/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala b/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala index f0d900363be7..6dd0cd5c0644 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala @@ -74,7 +74,13 @@ trait Helpers { assert(false, "exception wasn't thrown") } - def assertEqAst(tree: Tree, code: String) = assert(eqAst(tree, code)) + def assertEqAst(tree: Tree, code: String) = + assert(eqAst(tree, code), + s"""quasiquote tree != parse(code) tree + |quasiquote: $tree + |parse tree: ${parse(code)} + |code (str): $code""".stripMargin) + def eqAst(tree: Tree, code: String) = tree ≈ parse(code) val toolbox = currentMirror.mkToolBox() diff --git a/test/scalacheck/scala/reflect/quasiquotes/UnliftableProps.scala b/test/scalacheck/scala/reflect/quasiquotes/UnliftableProps.scala index 77e5b2de3d88..ae2d9aaf0b7f 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/UnliftableProps.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/UnliftableProps.scala @@ -1,8 +1,10 @@ package scala.reflect.quasiquotes +import org.junit.Assert.{assertEquals, assertTrue} import org.scalacheck._, Prop._, Gen._, Arbitrary._ import scala.reflect.runtime.universe._, Flag._ +@annotation.nowarn("msg=deprecated adaptation") object UnliftableProps extends QuasiquoteProperties("unliftable") { property("unlift name") = test { val termname0 = TermName("foo") @@ -74,7 +76,9 @@ object UnliftableProps extends QuasiquoteProperties("unliftable") { property("unlift scala.symbol") = test { val q"${s: scala.Symbol}" = q"'foo" - assert(s.isInstanceOf[scala.Symbol] && s == 'foo) + //assert(s.isInstanceOf[scala.Symbol] && s == Symbol("foo")) + assertTrue(s.isInstanceOf[scala.Symbol]) + assertEquals(Symbol("foo"), s) } implicit def unliftList[T: Unliftable]: Unliftable[List[T]] = Unliftable { diff --git a/test/scalacheck/t2460.scala b/test/scalacheck/t2460.scala index 42ff3ecfe6ab..81941a33261f 100644 --- a/test/scalacheck/t2460.scala +++ b/test/scalacheck/t2460.scala @@ -12,11 +12,11 @@ object SI2460Test extends Properties("Regex : Ticket 2460") { } val numberOfGroup = forAll(vowel) { - (s: String) => "\\s*([a-z])\\s*([a-z])\\s*".r("data").findAllMatchIn((1 to 20).map(_ => s).mkString).next.groupCount == 2 + (s: String) => "\\s*([a-z])\\s*([a-z])\\s*".r("data").findAllMatchIn((1 to 20).map(_ => s).mkString).next().groupCount == 2 } val nameOfGroup = forAll(vowel) { - (s: String) => "([a-z])".r("data").findAllMatchIn(s).next.group("data") == s + (s: String) => "([a-z])".r("data").findAllMatchIn(s).next().group("data") == s } val tests = List( diff --git a/test/scalacheck/treemap.scala b/test/scalacheck/treemap.scala index f21dacaef7fa..83fb586b5192 100644 --- a/test/scalacheck/treemap.scala +++ b/test/scalacheck/treemap.scala @@ -71,21 +71,21 @@ object TreeMapTest extends Properties("TreeMap") { property("minAfter") = forAll { (elements: List[Int]) => elements.nonEmpty ==> { val half = elements.take(elements.size / 2) val subject = TreeMap((half zip half): _*) - elements.forall{e => { - val temp = subject.from(e) + elements.forall { e => + val temp = subject.rangeFrom(e) if (temp.isEmpty) subject.minAfter(e).isEmpty else subject.minAfter(e).get == temp.min - }} + } }} property("maxBefore") = forAll { (elements: List[Int]) => elements.nonEmpty ==> { val half = elements.take(elements.size / 2) val subject = TreeMap((half zip half): _*) - elements.forall{e => { - val temp = subject.until(e) + elements.forall { e => + val temp = subject.rangeUntil(e) if (temp.isEmpty) subject.maxBefore(e).isEmpty else subject.maxBefore(e).get == temp.max - }} + } }} property("head/tail identity") = forAll { (subject: TreeMap[Int, String]) => subject.nonEmpty ==> { @@ -146,7 +146,7 @@ object TreeMapTest extends Properties("TreeMap") { property("from is inclusive") = forAll { (subject: TreeMap[Int, String]) => subject.nonEmpty ==> { val n = choose(0, subject.size - 1).sample.get val from = subject.drop(n).firstKey - subject.from(from).firstKey == from && subject.from(from).forall(_._1 >= from) + subject.rangeFrom(from).firstKey == from && subject.rangeFrom(from).forall(_._1 >= from) }} property("to is inclusive") = forAll { (subject: TreeMap[Int, String]) => subject.nonEmpty ==> { @@ -158,7 +158,7 @@ object TreeMapTest extends Properties("TreeMap") { property("until is exclusive") = forAll { (subject: TreeMap[Int, String]) => subject.size > 1 ==> { val n = choose(1, subject.size - 1).sample.get val until = subject.drop(n).firstKey - subject.until(until).lastKey == subject.take(n).lastKey && subject.until(until).forall(_._1 <= until) + subject.rangeUntil(until).lastKey == subject.take(n).lastKey && subject.rangeUntil(until).forall(_._1 <= until) }} property("remove single") = forAll { (subject: TreeMap[Int, String]) => subject.nonEmpty ==> { diff --git a/test/scalacheck/treeset.scala b/test/scalacheck/treeset.scala index 286fb1bc6919..e4ba91f54727 100644 --- a/test/scalacheck/treeset.scala +++ b/test/scalacheck/treeset.scala @@ -70,7 +70,7 @@ object TreeSetTest extends Properties("TreeSet") { val half = elements.take(elements.size / 2) val subject = TreeSet(half: _*) elements.forall{e => { - val temp = subject.from(e) + val temp = subject.rangeFrom(e) if (temp.isEmpty) subject.minAfter(e).isEmpty else subject.minAfter(e).get == temp.min }} @@ -80,7 +80,7 @@ object TreeSetTest extends Properties("TreeSet") { val half = elements.take(elements.size / 2) val subject = TreeSet(half: _*) elements.forall{e => { - val temp = subject.from(e) + val temp = subject.rangeFrom(e) if (temp.isEmpty) subject.minAfter(e).isEmpty else subject.minAfter(e).get == temp.min }} @@ -144,7 +144,7 @@ object TreeSetTest extends Properties("TreeSet") { property("from is inclusive") = forAll { (subject: TreeSet[Int]) => subject.nonEmpty ==> { val n = choose(0, subject.size - 1).sample.get val from = subject.drop(n).firstKey - subject.from(from).firstKey == from && subject.from(from).forall(_ >= from) + subject.rangeFrom(from).firstKey == from && subject.rangeFrom(from).forall(_ >= from) }} property("to is inclusive") = forAll { (subject: TreeSet[Int]) => subject.nonEmpty ==> { @@ -156,7 +156,7 @@ object TreeSetTest extends Properties("TreeSet") { property("until is exclusive") = forAll { (subject: TreeSet[Int]) => subject.size > 1 ==> { val n = choose(1, subject.size - 1).sample.get val until = subject.drop(n).firstKey - subject.until(until).lastKey == subject.take(n).lastKey && subject.until(until).forall(_ <= until) + subject.rangeUntil(until).lastKey == subject.take(n).lastKey && subject.rangeUntil(until).forall(_ <= until) }} property("remove single") = forAll { (subject: TreeSet[Int]) => subject.nonEmpty ==> { diff --git a/test/tasty/neg-move-macros/src-2/TestMacroCompat.check b/test/tasty/neg-move-macros/src-2/TestMacroCompat.check index f69ad0abe7b5..07deaf926e69 100644 --- a/test/tasty/neg-move-macros/src-2/TestMacroCompat.check +++ b/test/tasty/neg-move-macros/src-2/TestMacroCompat.check @@ -1,4 +1,4 @@ -TestMacroCompat_fail.scala:7: error: can't find term required by object tastytest.MacroCompat: tastytest.`package`.Macros.posImpl; perhaps it is missing from the classpath. +TestMacroCompat_fail.scala:7: error: can't find term required by object tastytest.MacroCompat: tastytest.package.Macros.posImpl; perhaps it is missing from the classpath. val result = MacroCompat.testCase("foo")(pos) ^ 1 error diff --git a/test/tasty/neg/src-2/TestCompiletimeQuoteType.check b/test/tasty/neg/src-2/TestCompiletimeQuoteType.check index c041a36c4d04..5c2f3c01b8df 100644 --- a/test/tasty/neg/src-2/TestCompiletimeQuoteType.check +++ b/test/tasty/neg/src-2/TestCompiletimeQuoteType.check @@ -1,4 +1,4 @@ -TestCompiletimeQuoteType_fail.scala:4: error: Unsupported Scala 3 context function type in result: scala.quoted.Quotes ?=> scala.quoted.Type[T]; found in method of in object scala.quoted.Type. +TestCompiletimeQuoteType_fail.scala:4: error: could not find implicit value for evidence parameter of type scala.quoted.Type[Int] def test = CompiletimeQuoteType.f[Int] ^ 1 error diff --git a/test/tasty/neg/src-2/TestDelayedPrivate.check b/test/tasty/neg/src-2/TestDelayedPrivate.check new file mode 100644 index 000000000000..dbf046b62d43 --- /dev/null +++ b/test/tasty/neg/src-2/TestDelayedPrivate.check @@ -0,0 +1,4 @@ +TestDelayedPrivate_fail.scala:7: error: value Deeper is not a member of object tastytest.DelayedPrivate.Nested + DelayedPrivate.Nested.Deeper + ^ +1 error diff --git a/test/tasty/neg/src-2/TestDelayedPrivateInverse.check b/test/tasty/neg/src-2/TestDelayedPrivateInverse.check new file mode 100644 index 000000000000..9742e9453372 --- /dev/null +++ b/test/tasty/neg/src-2/TestDelayedPrivateInverse.check @@ -0,0 +1,4 @@ +TestDelayedPrivateInverse_fail.scala:6: error: value Internal is not a member of object tastytest.DelayedPrivateInverse + val _ = DelayedPrivateInverse.Internal + ^ +1 error diff --git a/test/tasty/neg/src-2/TestDelayedPrivateInverse_fail.scala b/test/tasty/neg/src-2/TestDelayedPrivateInverse_fail.scala new file mode 100644 index 000000000000..002fa21936c9 --- /dev/null +++ b/test/tasty/neg/src-2/TestDelayedPrivateInverse_fail.scala @@ -0,0 +1,8 @@ +package tastytest + +object TestDelayedPrivateInverse { + def test: DelayedPrivateInverse.Parent[Nothing] = ??? // force sealed children of parent + locally { + val _ = DelayedPrivateInverse.Internal + } +} diff --git a/test/tasty/neg/src-2/TestDelayedPrivate_fail.scala b/test/tasty/neg/src-2/TestDelayedPrivate_fail.scala new file mode 100644 index 000000000000..50c7728d8e91 --- /dev/null +++ b/test/tasty/neg/src-2/TestDelayedPrivate_fail.scala @@ -0,0 +1,9 @@ +package tastytest + +object TestDelayedPrivate { + + locally { + val _ = Nil: List[DelayedPrivate.Root] // force Root to be seen first + DelayedPrivate.Nested.Deeper + } +} diff --git a/test/tasty/neg/src-2/TestHello.check b/test/tasty/neg/src-2/TestHello.check index 947231704f7c..7bc3fcecd052 100644 --- a/test/tasty/neg/src-2/TestHello.check +++ b/test/tasty/neg/src-2/TestHello.check @@ -32,4 +32,7 @@ List's type parameters do not match type F's expected parameters: type A is covariant, but type _ is declared contravariant HelloWorld.higherBounded6[List] ^ -8 errors +TestHello_fail.scala:12: error: Unsupported Scala 3 inline value msg1; found in object helloworld.HelloWorld. + HelloWorld.msg1 + ^ +9 errors diff --git a/test/tasty/neg/src-2/TestHello_2.check b/test/tasty/neg/src-2/TestHello_2.check new file mode 100644 index 000000000000..09ba893b845c --- /dev/null +++ b/test/tasty/neg/src-2/TestHello_2.check @@ -0,0 +1,7 @@ +TestHello_2_fail.scala:4: error: Unsupported Scala 3 inline value msg1; found in object helloworld.HelloWorld. + HelloWorld.acceptsOnlyMsg1(HelloWorld.msg1) + ^ +TestHello_2_fail.scala:5: error: Unsupported Scala 3 inline method inlineMethod; found in object helloworld.HelloWorld. + HelloWorld.inlineMethod(1) + ^ +2 errors diff --git a/test/tasty/neg/src-2/TestHello_2_fail.scala b/test/tasty/neg/src-2/TestHello_2_fail.scala new file mode 100644 index 000000000000..99caab29a3dd --- /dev/null +++ b/test/tasty/neg/src-2/TestHello_2_fail.scala @@ -0,0 +1,6 @@ +package helloworld + +object TestHello_2 { + HelloWorld.acceptsOnlyMsg1(HelloWorld.msg1) + HelloWorld.inlineMethod(1) +} diff --git a/test/tasty/neg/src-2/TestHello_fail.scala b/test/tasty/neg/src-2/TestHello_fail.scala index 62e686411202..5920eeaed244 100644 --- a/test/tasty/neg/src-2/TestHello_fail.scala +++ b/test/tasty/neg/src-2/TestHello_fail.scala @@ -9,4 +9,5 @@ object TestHello { HelloWorld.higherBounded5[Show] HelloWorld.higherBounded6[List] + HelloWorld.msg1 } diff --git a/test/tasty/neg/src-2/TestInvisibleDefs.check b/test/tasty/neg/src-2/TestInvisibleDefs.check new file mode 100644 index 000000000000..9ce3bf4804cd --- /dev/null +++ b/test/tasty/neg/src-2/TestInvisibleDefs.check @@ -0,0 +1,13 @@ +TestInvisibleDefs_fail.scala:5: error: type argIsHello is not a member of package tastytest + def foo: tastytest.argIsHello = ??? // has invisible flag so should not be seen + ^ +TestInvisibleDefs_fail.scala:6: error: type argIsHello is not a member of package tastytest + def bar: tastytest.argIsHello = ??? // second try on same type + ^ +TestInvisibleDefs_fail.scala:11: error: value getStatus is not a member of tastytest.InvisibleDefs.MyBean + mybean.getStatus() // error + ^ +TestInvisibleDefs_fail.scala:12: error: value setStatus is not a member of tastytest.InvisibleDefs.MyBean + mybean.setStatus("closed") // error + ^ +4 errors diff --git a/test/tasty/neg/src-2/TestInvisibleDefs_fail.scala b/test/tasty/neg/src-2/TestInvisibleDefs_fail.scala new file mode 100644 index 000000000000..d8e681206150 --- /dev/null +++ b/test/tasty/neg/src-2/TestInvisibleDefs_fail.scala @@ -0,0 +1,15 @@ +package tastytest + +object TestInvisibleDefs { + + def foo: tastytest.argIsHello = ??? // has invisible flag so should not be seen + def bar: tastytest.argIsHello = ??? // second try on same type + + def testBean = { + val mybean = new InvisibleDefs.MyBean + mybean.status = "open" + mybean.getStatus() // error + mybean.setStatus("closed") // error + } + +} diff --git a/test/tasty/neg/src-3/DelayedPrivate.scala b/test/tasty/neg/src-3/DelayedPrivate.scala new file mode 100644 index 000000000000..76c2fc949d20 --- /dev/null +++ b/test/tasty/neg/src-3/DelayedPrivate.scala @@ -0,0 +1,15 @@ +package tastytest + +object DelayedPrivate { + + sealed trait Root + + object Nested { + + private object Deeper { + final class Leaf extends Root + } + + } + +} diff --git a/test/tasty/neg/src-3/DelayedPrivateInverse.scala b/test/tasty/neg/src-3/DelayedPrivateInverse.scala new file mode 100644 index 000000000000..3d03e90fb361 --- /dev/null +++ b/test/tasty/neg/src-3/DelayedPrivateInverse.scala @@ -0,0 +1,8 @@ +package tastytest + +object DelayedPrivateInverse { + private object Internal { + final class Impl extends DelayedPrivateInverse.Parent[Nothing] + } + sealed trait Parent[T] +} diff --git a/test/tasty/neg/src-3/ErasedTypes.scala b/test/tasty/neg/src-3/ErasedTypes.scala index bafb95891012..a535369ebbdb 100644 --- a/test/tasty/neg/src-3/ErasedTypes.scala +++ b/test/tasty/neg/src-3/ErasedTypes.scala @@ -1,5 +1,7 @@ package tastytest +import language.experimental.erasedDefinitions + object ErasedTypes { trait Foo { diff --git a/test/tasty/neg/src-3/HelloWorld.scala b/test/tasty/neg/src-3/HelloWorld.scala index 3ea81a78f0a4..3f03c01e2925 100644 --- a/test/tasty/neg/src-3/HelloWorld.scala +++ b/test/tasty/neg/src-3/HelloWorld.scala @@ -1,11 +1,12 @@ package helloworld object HelloWorld { - final val msg1 = "Hello, World!" + inline val msg1 = "Hello, World!" def acceptsOnlyMsg1(m: msg1.type): String = m + m def higherBounded2[T <: List[_ <: Int]](f: T): T = f def higherBounded3[T <: List[List[_ <: Int]]](f: T): T = f def higherBounded4[T <: Either[_ <: Int, String]](f: T): T = f def higherBounded5[F[+_]] = ??? def higherBounded6[F[-_]] = ??? + inline def inlineMethod(inline i: Int): Int = i } diff --git a/test/tasty/neg/src-3/InvisibleDefs.scala b/test/tasty/neg/src-3/InvisibleDefs.scala new file mode 100644 index 000000000000..5bd0190c28e1 --- /dev/null +++ b/test/tasty/neg/src-3/InvisibleDefs.scala @@ -0,0 +1,16 @@ +package tastytest + +import scala.beans.BeanProperty + +object InvisibleDefs { + + @main def argIsHello(arg: String): Unit = assert(arg == "Hello") + + class MyBean { + + @BeanProperty + var status = "" + + } + +} diff --git a/test/tasty/run/pre/tastytest/package.scala b/test/tasty/run/pre/tastytest/package.scala index ccfd109a5f3a..fca544cff4fb 100644 --- a/test/tasty/run/pre/tastytest/package.scala +++ b/test/tasty/run/pre/tastytest/package.scala @@ -4,6 +4,29 @@ import scala.reflect.macros.blackbox.Context package object tastytest { + def anyObj[T]: T = null.asInstanceOf[T] + + trait Aspect { + def applyTo(op: => Unit): Unit + } + + implicit class AspectOps(op: => Unit) { + def @@(aspect: Aspect): Unit = aspect.applyTo(op) + } + + object ExpectCastOrNull extends Aspect { + def applyTo(op: => Unit): Unit = { + try { + op + throw new AssertionError("expected a failure") + } + catch { + case npe: NullPointerException => // swallow + case cce: ClassCastException => // swallow + } + } + } + implicit final class SafeEq[T](private val t: T) extends AnyVal { final def ===[U](u: U)(implicit ev: T =:= U): Boolean = t == u } diff --git a/test/tasty/run/pre/tastytest/reflectshims/Context.scala b/test/tasty/run/pre/tastytest/reflectshims/Context.scala new file mode 100644 index 000000000000..55c883114a9a --- /dev/null +++ b/test/tasty/run/pre/tastytest/reflectshims/Context.scala @@ -0,0 +1,9 @@ +package tastytest.reflectshims + +trait Context { + + type TreeShim = universe.TreeShim + + val universe: Universe + +} diff --git a/test/tasty/run/pre/tastytest/reflectshims/Universe.scala b/test/tasty/run/pre/tastytest/reflectshims/Universe.scala new file mode 100644 index 000000000000..722a4b5a70e6 --- /dev/null +++ b/test/tasty/run/pre/tastytest/reflectshims/Universe.scala @@ -0,0 +1,8 @@ +package tastytest.reflectshims + +abstract class Universe { + type TreeShim >: Null <: AnyRef with TreeShimApi + trait TreeShimApi extends Product { this: TreeShim => } + + val EmptyTree: TreeShim +} diff --git a/test/tasty/run/pre/tastytest/reflectshims/impl/Context.scala b/test/tasty/run/pre/tastytest/reflectshims/impl/Context.scala new file mode 100644 index 000000000000..1ed77e3e3be0 --- /dev/null +++ b/test/tasty/run/pre/tastytest/reflectshims/impl/Context.scala @@ -0,0 +1,17 @@ +package tastytest.reflectshims.impl + +import tastytest.reflectshims + +object Context extends reflectshims.Context { + + object universe extends reflectshims.Universe { + + abstract class TreeShimImpl extends TreeShimApi with Product + + type TreeShim = TreeShimImpl + + case object EmptyTree extends TreeShimImpl + + } + +} diff --git a/test/tasty/run/pre/tastytest/scala2Erasure/api.scala b/test/tasty/run/pre/tastytest/scala2Erasure/api.scala new file mode 100644 index 000000000000..f7999bd8fbd4 --- /dev/null +++ b/test/tasty/run/pre/tastytest/scala2Erasure/api.scala @@ -0,0 +1,263 @@ +package tastytest + +// Keep synchronized with src-3/tastytest/dottyErasureApi/api.scala +package scala2Erasure + +class foo extends scala.annotation.StaticAnnotation + +trait A +trait B +trait SubB extends B +trait C +trait Cov[+T] +trait Univ extends Any + +class D + +class VC(val self: A) extends AnyVal +class VC2(val self: A) extends AnyVal + +class Outer { + class E + trait F extends E +} + +object OpaqueHolder { + type Q[T] = Cov[T] + type Y[T] = Cov[T] +} +import OpaqueHolder._ + +sealed abstract class Enumerated +object Enumerated { + final val C1: Enumerated with A = new Enumerated with A {} + final val C2: Enumerated with B = new Enumerated with B {} +} + +// The parameter type of `a_XX` should erase to A, `b_XX` to `B`, etc. +// This is enforced by dottyApp/Main.scala +class Z { self => + def a_01(a: A with B): Unit = {} + def b_02X(b: B with A): Unit = {} + def a_02(a: A with B with A): Unit = {} + def a_03(a: A with (B with A)): Unit = {} + def b_04(b: A with (B with A) @foo): Unit = {} + def b_04X(b: A with (B with C) @foo): Unit = {} + def b_05(b: A with (B with A) @foo with (C with B with A) @foo): Unit = {} + + type T1 <: A with B + def a_06(a: T1): Unit = {} + + type S <: B with T1 + def a_07(a: S): Unit = {} + + type T2 <: B with A + type U <: T2 with S + def b_08(b: U): Unit = {} + + val singB: B = new B {} + def a_09(a: A with singB.type): Unit = {} + def b_10(b: singB.type with A): Unit = {} + + type V >: SubB <: B + def b_11(b: V): Unit = {} + def b_12(b: V with SubB): Unit = {} + + def d_13(d: D with A): Unit = {} + def d_14(d: A with D): Unit = {} + + val singD: D = new D {} + def d_13x(d: singD.type with A): Unit = {} + def d_14x(d: A with singD.type): Unit = {} + + type DEq = D + def d_15(d: A with DEq): Unit = {} + def d_16(d: A with (DEq @foo)): Unit = {} + def d_17(d: DEq with A): Unit = {} + def d_18(d: (DEq @foo) with A): Unit = {} + + val singDEq: DEq @foo = new D {} + def d_15b(d: A with singDEq.type): Unit = {} + def d_16b(d: A with (singDEq.type @foo)): Unit = {} + + type DSub <: D + def a_19(a: A with DSub): Unit = {} + def d_19x(d: DSub with A): Unit = {} + def z_20(z: DSub with Z): Unit = {} + + type W1 <: A with Cov[Any] + type X1 <: Cov[Int] with W1 + def a_21(a: X1): Unit = {} + + type W2 <: A with Cov[Any] + type X2 <: Cov[Int] with W2 + def a_22(a: X2): Unit = {} + + def z_23(z: A with this.type): Unit = {} + def z_24(z: this.type with A): Unit = {} + + def b_25(b: A with (B { type T })): Unit = {} + def a_26(a: (A { type T }) with ((B with A) { type T })): Unit = {} + + def a_27(a: VC with B): Unit = {} + def a_28(a: B with VC): Unit = {} + + val o1: Outer = new Outer + val o2: Outer = new Outer + def f_29(f: o1.E with o1.F): Unit = {} + def f_30(f: o1.F with o1.E): Unit = {} + def f_31(f: o1.E with o2.F): Unit = {} + def f_32(f: o2.F with o1.E): Unit = {} + def f_33(f: Outer#E with Outer#F): Unit = {} + def f_34(f: Outer#F with Outer#E): Unit = {} + + val structural1: { type DSub <: D } = new { type DSub <: D } + def a_35(a: A with structural1.DSub): Unit = {} + def d_36(a: structural1.DSub with A): Unit = {} + def z_37(z: Z with structural1.DSub): Unit = {} + def z_38(z: structural1.DSub with Z): Unit = {} + + val structural2: { type SubCB <: C with B } = new { type SubCB <: C with B } + def c_39(c: structural2.SubCB with B): Unit = {} + def c_40(c: B with structural2.SubCB): Unit = {} + + val structural3a: { type SubB <: B; type SubCB <: C with SubB } = new { type SubB <: B; type SubCB <: C with SubB } + val structural3b: { type SubB <: B; type SubCB <: C with SubB } = new { type SubB <: B; type SubCB <: C with SubB } + def c_41(c: structural3a.SubB with structural3a.SubCB): Unit = {} + def c_42(c: structural3a.SubCB with structural3a.SubB): Unit = {} + def b_43(b: structural3a.SubB with structural3b.SubCB): Unit = {} + def c_44(c: structural3b.SubCB with structural3a.SubB): Unit = {} + + type SubStructural <: C with structural3a.SubB + def c_45(x: structural3a.SubB with SubStructural): Unit = {} + def b_46(x: structural3b.SubB with SubStructural): Unit = {} + + type Rec1 <: A with B + type Rec2 <: C with Rec1 + def c_47(a: A with B with Rec2): Unit = {} + def a_48(a: (A with B) @foo with Rec2): Unit = {} + + type F1 = A with B + type F2 = A with B + type Rec3 <: F1 + type Rec4 <: C with Rec3 + def c_49(a: F1 @foo with Rec4): Unit = {} + def c_50(a: F1 with Rec4): Unit = {} + def a_51(a: F2 @foo with Rec4): Unit = {} + def c_52(a: F2 with Rec4): Unit = {} + + type AA = A + type F3 = AA with B + type Rec5 <: F3 + type Rec6 <: C with Rec5 + def a_53(a: F3 @foo with Rec6): Unit = {} + def c_54(a: F3 with Rec6): Unit = {} + + val structural4a: { type M[X] <: A } = new { type M[X] <: A } + val structural4b: { type N <: B with structural4a.M[Int] } = new { type N <: B with structural4a.M[Int] } + def b_55(x: structural4a.M[Any] with structural4b.N): Unit = {} + + type Bla = A { type M[X] <: A } + def b_56(x: Bla#M[Any] with ({ type N <: B with Bla#M[Int] })#N): Unit = {} + type AEq = A + type Bla2 = AEq { type M[X] <: A } + def a_57(x: Bla2#M[Any] with ({ type N <: B with Bla2#M[Int] })#N): Unit = {} + + def int_58(x: Int with Singleton): Unit = {} + def int_59(x: Singleton with Int): Unit = {} + def int_60(x: Int with Any): Unit = {} + def int_61(x: Any with Int): Unit = {} + def int_62(x: Int with AnyVal): Unit = {} + def int_63(x: AnyVal with Int): Unit = {} + + def intARRAY_64(x: Array[Int with Singleton]): Unit = {} + def object_65(x: Array[_ <: Int]): Unit = {} + def object_66(x: Array[_ <: Int with Singleton]): Unit = {} + def object_67(x: Array[_ <: Singleton with Int]): Unit = {} + def object_68(x: Array[_ <: Int with Any]): Unit = {} + def object_69(x: Array[_ <: Any with Int]): Unit = {} + def object_70(x: Array[_ <: Int with AnyVal]): Unit = {} + def object_71(x: Array[_ <: AnyVal with Int]): Unit = {} + + def stringARRAY_72(x: Array[String with Singleton]): Unit = {} + def stringARRAY_73(x: Array[_ <: String]): Unit = {} + def stringARRAY_74(x: Array[_ <: String with Singleton]): Unit = {} + def stringARRAY_75(x: Array[_ <: Singleton with String]): Unit = {} + def stringARRAY_76(x: Array[_ <: String with Any]): Unit = {} + def stringARRAY_77(x: Array[_ <: Any with String]): Unit = {} + def stringARRAY_78(x: Array[_ <: String with AnyRef]): Unit = {} + def stringARRAY_79(x: Array[_ <: AnyRef with String]): Unit = {} + + def object_80(x: Array[_ <: Singleton]): Unit = {} + def object_81(x: Array[_ <: AnyVal]): Unit = {} + def objectARRAY_82(x: Array[_ <: AnyRef]): Unit = {} + def object_83(x: Array[_ <: Any]): Unit = {} + + def object_84(x: Array[_ <: Serializable]): Unit = {} + def object_85(x: Array[_ <: Univ]): Unit = {} + def aARRAY_86(x: Array[_ <: A]): Unit = {} + def aARRAY_87(x: Array[_ <: A with B]): Unit = {} + + def objectARRAY_88(x: Array[Any]): Unit = {} + def objectARRAY_89(x: Array[AnyRef]): Unit = {} + def objectARRAY_90(x: Array[AnyVal]): Unit = {} + + def stringARRAY_91(x: Array[_ <: ({ type Foo <: String with Singleton })#Foo]): Unit = {} + def stringARRAY_92(x: Array[({ type Foo <: String with Singleton })#Foo]): Unit = {} + def stringARRAY_93(x: Array[({ type Id[T] = T })#Id[String with Singleton]]): Unit = {} + + def covARRAY_94(x: Array[Q[String]]): Unit = {} // cant define opaque type in scala 2, so it is ordinary type + + def aARRAY_95(x: Array[(A with B { type L <: String }) with C]): Unit = {} + def aARRAY_96(x: Array[A { type L <: String }]): Unit = {} + def zARRAY_97(x: Array[self.type]): Unit = {} + def aARRAY_98(x: Array[(A { type L <: String }) with B]): Unit = {} + def stringARRAY_99[Arg <: String](x: Array[Arg]): Unit = {} + def aARRAY_100(x: Array[Bla2#M[Any] with ({ type N <: B with Bla2#M[Int] })#N]): Unit = {} + def zARRAY_101(x: Array[structural1.DSub with Z]): Unit = {} + def aARRAY_102(x: Array[F3 @foo with Rec6]): Unit = {} + def aARRAY_103(x: Array[A @foo]): Unit = {} + def dARRAY_104(x: Array[singD.type]): Unit = {} + def intARRAY_105(x: Array[3]): Unit = {} + def vcARRAY_106(x: Array[VC]): Unit = {} + def listARRAY_107(x: Array[List[_]]): Unit = {} + def intARRAY_108(x: Array[Int with String]): Unit = {} + def stringARRAY_109(x: Array[String with Int]): Unit = {} + + def a_110(x: VC with VC2): Unit = {} + def a_111(x: VC2 with VC): Unit = {} + def aARRAY_112(x: Array[VC2 with VC]): Unit = {} // this should not erase to Array[A]??? + def aARRAY_113(x: Array[VC with VC2]): Unit = {} // this should not erase to Array[A]??? + def a_114(x: VC with D): Unit = {} + def d_115(x: D with VC): Unit = {} + def d_116(x: D with B with VC): Unit = {} + def d_117(x: B with D with VC): Unit = {} + def a_118(x: VC with B with D): Unit = {} + def a_119(x: VC with Int): Unit = {} + def int_120(x: Int with VC): Unit = {} + + def object_121[T](x: Array[T]): Unit = {} + def object_122(x: Array[_ <: AnyVal with Singleton]): Unit = {} + def objectARRAY_123(x: Array[AnyVal with Singleton]): Unit = {} + def objectARRAY_124[T, U](x: Array[T with U]): Unit = {} + def objectARRAY_125(x: Array[({ type W <: String }) with ({ type X <: Int })]): Unit = {} + def covARRAY_126(x: Array[Q[B] with Y[SubB]]): Unit = {} + def covARRAY_127(x: Array[Q[B] with Y[SubB] { type X <: Cov[String] }]): Unit = {} + + type SubAny <: Any + type SubAnyVal <: AnyVal + + def objectARRAY_128(x: Array[SubAny with SubAnyVal]): Unit = {} + def intARRAYARRAY_129(x: Array[Array[Int]]): Unit = {} + def intARRAYARRAY_130(x: Array[_ <: Array[Int]]): Unit = {} + def objectARRAY_130(x: Array[_ <: Array[_ <: AnyVal]]): Unit = {} + def stringARRAY_131(x: Array[String] with Array[Int]): Unit = {} + + def enumerated_132(x: Enumerated.C1.type with Enumerated.C2.type): Unit = {} + def enumerated_133(x: Enumerated.C2.type with Enumerated.C1.type): Unit = {} + def enumerated_134(x: Enumerated.C1.type): Unit = {} + def enumeratedARRAY_135(x: Array[Enumerated.C1.type]): Unit = {} + def enumeratedARRAY_136(x: Array[Enumerated.C2.type with Enumerated.C1.type]): Unit = {} + def enumeratedARRAY_137(x: Array[Enumerated.C1.type with Enumerated.C2.type]): Unit = {} + +} diff --git a/test/tasty/run/src-2/tastytest/TestErasure.scala b/test/tasty/run/src-2/tastytest/TestErasure.scala new file mode 100644 index 000000000000..1ba9cc7ae3d2 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestErasure.scala @@ -0,0 +1,178 @@ +package tastytest + +import tastytest.{dottyErasure => dotc, scala2Erasure => nsc} + +object TestErasure extends Suite("TestErasure") { + + val z = new dotc.Z + + test("erasure of scala 3 from scala 2") { + z.a_01(anyObj) + z.a_02(anyObj) + z.a_02X(anyObj) + z.a_03(anyObj) + z.a_04(anyObj) + z.a_04X(anyObj) + z.a_05(anyObj) + z.a_06(anyObj) + z.a_07(anyObj) + z.a_08(anyObj) + z.a_09(anyObj) + z.a_10(anyObj) + z.b_11(anyObj) + z.subb_12(anyObj) + z.d_13(anyObj) + z.d_13x(anyObj) + z.d_14(anyObj) + z.d_14x(anyObj) + z.d_15(anyObj) + z.d_15b(anyObj) + z.d_16(anyObj) + z.d_16b(anyObj) + z.d_17(anyObj) + z.d_18(anyObj) + z.d_19(anyObj) + z.d_19x(anyObj) + z.d_20(anyObj) + z.a_21(anyObj) + z.a_22(anyObj) + z.z_23(anyObj) + z.z_24(anyObj) + z.a_25(anyObj) + z.a_26(anyObj) + z.a_27(anyObj) @@ ExpectCastOrNull + z.a_28(anyObj) @@ ExpectCastOrNull + z.e_29(anyObj) + z.e_30(anyObj) + z.e_31(anyObj) + z.e_32(anyObj) + z.e_33(anyObj) + z.e_34(anyObj) + z.d_35(anyObj) + z.d_36(anyObj) + z.d_37(anyObj) + z.d_38(anyObj) + z.b_39(anyObj) + z.b_40(anyObj) + z.b_41(anyObj) + z.b_42(anyObj) + z.b_43(anyObj) + z.b_44(anyObj) + z.b_45(anyObj) + z.b_46(anyObj) + z.a_47(anyObj) + z.a_48(anyObj) + z.a_49(anyObj) + z.a_50(anyObj) + z.a_51(anyObj) + z.a_52(anyObj) + z.a_53(anyObj) + z.a_54(anyObj) + z.a_55(anyObj) + z.a_56(anyObj) + z.a_57(anyObj) + z.int_58(1) + z.int_59(1) + z.int_60(1) + z.int_61(1) + z.int_62(1) + z.int_63(1) + z.intARRAY_64(anyObj) + z.intARRAY_65(anyObj) + z.intARRAY_66(anyObj) + z.intARRAY_67(anyObj) + z.intARRAY_68(anyObj) + z.intARRAY_69(anyObj) + z.intARRAY_70(anyObj) + z.intARRAY_71(anyObj) + // z.intARRAY_71a(anyObj) // illegal union type + // z.intARRAY_71b(anyObj) // illegal union type + z.stringARRAY_72(anyObj) + z.stringARRAY_73(anyObj) + z.stringARRAY_74(anyObj) + z.stringARRAY_75(anyObj) + z.stringARRAY_76(anyObj) + z.stringARRAY_77(anyObj) + z.stringARRAY_78(anyObj) + z.stringARRAY_79(anyObj) + // z.stringARRAY_79a(anyObj) // illegal union type + // z.stringARRAY_79b(anyObj) // illegal union type + z.object_80(anyObj) + z.object_81(anyObj) + z.objectARRAY_82(anyObj) + z.object_83(anyObj) + z.object_83a(anyObj) + // z.object_83b(anyObj) // illegal union type + // z.object_83c(anyObj) // illegal union type + // z.object_83d(anyObj) // illegal union type + // z.object_83e(anyObj) // illegal union type + z.serializableARRAY_84(anyObj) + z.univARRAY_85(anyObj) + z.aARRAY_86(anyObj) + z.aARRAY_87(anyObj) + z.objectARRAY_88(anyObj) + z.objectARRAY_89(anyObj) + z.objectARRAY_90(anyObj) + z.stringARRAY_91(anyObj) + z.stringARRAY_92(anyObj) + z.stringARRAY_93(anyObj) + z.covARRAY_94(anyObj) + z.aARRAY_95(anyObj) + z.aARRAY_96(anyObj) + z.zARRAY_97(anyObj) + z.aARRAY_98(anyObj) + z.stringARRAY_99(anyObj) + z.aARRAY_100(anyObj) + z.dARRAY_101(anyObj) + z.aARRAY_102(anyObj) + z.aARRAY_103(anyObj) + z.dARRAY_104(anyObj) + z.intARRAY_105(anyObj) + z.vcARRAY_106(anyObj) + z.listARRAY_107(anyObj) + z.intARRAY_108(anyObj) + z.intARRAY_109(anyObj) + z.a_110(anyObj) @@ ExpectCastOrNull + z.a_111(anyObj) @@ ExpectCastOrNull + z.vcARRAY_112(anyObj) + z.vcARRAY_113(anyObj) + z.a_114(anyObj) @@ ExpectCastOrNull + z.a_115(anyObj) @@ ExpectCastOrNull + z.a_116(anyObj) @@ ExpectCastOrNull + z.a_117(anyObj) @@ ExpectCastOrNull + z.a_118(anyObj) @@ ExpectCastOrNull + z.a_119(anyObj) @@ ExpectCastOrNull + z.a_120(anyObj) @@ ExpectCastOrNull + z.object_121(anyObj) + z.object_122(anyObj) + z.objectARRAY_123(anyObj) + z.object_124(anyObj) + z.objectARRAY_125(anyObj) + z.covARRAY_126(anyObj) + z.covARRAY_127(anyObj) + z.object_128(anyObj) + z.intARRAYARRAY_129(anyObj) + z.intARRAYARRAY_130(anyObj) + z.objectARRAY_130(anyObj) + z.intARRAY_131(anyObj) + z.enumerated_132(anyObj) + z.enumerated_133(anyObj) + z.enumerated_134(anyObj) + z.enumeratedARRAY_135(anyObj) + z.enumeratedARRAY_136(anyObj) + z.enumeratedARRAY_137(anyObj) + } + + test("erasure matches name") { + val methods = classOf[nsc.Z].getDeclaredMethods.toList ++ classOf[dotc.Z].getDeclaredMethods.toList + methods.foreach { m => + m.getName match { + case s"${prefix}_${suffix}" => + val paramClass = m.getParameterTypes()(0).getSimpleName.toLowerCase.replaceAll("""\[\]""", "ARRAY") + assert(prefix == paramClass, s"Method `$m` erased to `$paramClass` which does not match its prefix `$prefix`") + case _ => + } + } + } + +} diff --git a/test/tasty/run/src-2/tastytest/TestInlineCompat.scala b/test/tasty/run/src-2/tastytest/TestInlineCompat.scala new file mode 100644 index 000000000000..4c3c9612c86e --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestInlineCompat.scala @@ -0,0 +1,7 @@ +package tastytest + +import InlineCompat._ + +object TestInlineCompat extends Suite("TestInlineCompat") { + test(assert(foo("Hello, World!") == "Hello, World!")) +} diff --git a/test/tasty/run/src-2/tastytest/TestInlineCompat2.scala b/test/tasty/run/src-2/tastytest/TestInlineCompat2.scala new file mode 100644 index 000000000000..54e31e954c64 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestInlineCompat2.scala @@ -0,0 +1,7 @@ +package tastytest + +import InlineCompat2._ + +object TestInlineCompat2 extends Suite("TestInlineCompat2") { + test(assert(foo("Hello, World!") == "Hello, World!")) +} diff --git a/test/tasty/run/src-2/tastytest/TestIntersectionErasure.scala b/test/tasty/run/src-2/tastytest/TestIntersectionErasure.scala new file mode 100644 index 000000000000..3137a9500034 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestIntersectionErasure.scala @@ -0,0 +1,40 @@ +package tastytest + +import IntersectionErasure._ + +object TestIntersectionErasure extends Suite("TestIntersectionErasure") { + + def boxedId[T](t: T): T = t + + val bWithA: B with A = new B with A {} // dotc erases to A, scalac to B + + test("SAM bridges") { + val sam: IntersectionSAM = x => x + assert(sam(bWithA) === bWithA) + } + + test("VC param")( + assert(boxedId(new IntersectionVC(bWithA)).unwrapped == bWithA) + ) + + test("VC method unboxed")( + assert(boxedId(new IntersectionVC(bWithA)).matchesInternal(bWithA)) + ) + + test("VC method boxed")( + assert(boxedId(new IntersectionVC(bWithA)).matches(new IntersectionVC(bWithA))) + ) + + test("VC parametric param")( + assert(boxedId(new IntersectionVCParametric(bWithA)).unwrapped == bWithA) + ) + + test("VC parametric method unboxed")( + assert(boxedId(new IntersectionVCParametric(bWithA)).matchesInternal(bWithA)) + ) + + test("VC parametric method boxed")( + assert(boxedId(new IntersectionVCParametric(bWithA)).matches(new IntersectionVCParametric(bWithA))) + ) + +} diff --git a/test/tasty/run/src-2/tastytest/TestInvisibleDefs.scala b/test/tasty/run/src-2/tastytest/TestInvisibleDefs.scala new file mode 100644 index 000000000000..4962af12bbe4 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestInvisibleDefs.scala @@ -0,0 +1,15 @@ +package tastytest + +object TestInvisibleDefs extends Suite("TestInvisibleDefs") { + + test("invoke '@main def argIsHello'") { + InvisibleDefs.argIsHello("Hello") + } + + test("update bean.status") { + val mybean = new InvisibleDefs.MyBean + mybean.status = "open" + assert(mybean.status === "open") + } + +} diff --git a/test/tasty/run/src-2/tastytest/TestOperatorToken.scala b/test/tasty/run/src-2/tastytest/TestOperatorToken.scala new file mode 100644 index 000000000000..ed7fc22cf327 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestOperatorToken.scala @@ -0,0 +1,7 @@ +package tastytest + +object TestOperatorToken extends Suite("TestOperatorToken") { + test(assert(OperatorToken.<:< != null)) + test(assert(OperatorToken.=:= != null)) + test(assert(OperatorToken.<*> != null)) +} diff --git a/test/tasty/run/src-2/tastytest/TestReflection.scala b/test/tasty/run/src-2/tastytest/TestReflection.scala new file mode 100644 index 000000000000..d292b8b7e737 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestReflection.scala @@ -0,0 +1,18 @@ +package tastytest + +import tastytest.reflectshims.impl.Context +import Context.universe.EmptyTree +import Context.TreeShim + +object TestReflection extends Suite("TestReflection") { + + test(assert(Reflection.reflectionInvokerIdentity(Context)(EmptyTree) === (EmptyTree: TreeShim))) + test(assert(new Reflection.Invoker(Context)(EmptyTree).tree === (EmptyTree: TreeShim))) + + // bridge method not generated (AbstractMethodError) [same if Reflection.InvokerSAM is compiled by Scala 2] + // test { + // val invoker = new Reflection.InvokerSAM(Context) + // val id: invoker.TreeFn = x => x + // assert(id(EmptyTree) === (EmptyTree: TreeShim)) + // } +} diff --git a/test/tasty/run/src-2/tastytest/TestSAMErasure.scala b/test/tasty/run/src-2/tastytest/TestSAMErasure.scala new file mode 100644 index 000000000000..4aa5e88b1535 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestSAMErasure.scala @@ -0,0 +1,23 @@ +package tastytest + +import SAMErasure._ + +object TestSAMErasure extends Suite("TestSAMErasure") { + + def f = ((x: TreeShimSAM) => x): FunTreeShimSAM + + def g = ((xs: Array[TreeShimSAM]) => xs): FunTreeShimSAM2 + + case object EmptyTree extends TreeShimSAMApi + val tree = EmptyTree.asInstanceOf[TreeShimSAM] + + test { + assert(f(tree) == tree) + } + + test { + val trees = Array(tree) + assert(g(trees) == trees) + } + +} diff --git a/test/tasty/run/src-2/tastytest/TestSuperTypes.scala b/test/tasty/run/src-2/tastytest/TestSuperTypes.scala index b675a0ec87d0..ed552f561ac6 100644 --- a/test/tasty/run/src-2/tastytest/TestSuperTypes.scala +++ b/test/tasty/run/src-2/tastytest/TestSuperTypes.scala @@ -11,11 +11,10 @@ object TestSuperTypes extends Suite("TestSuperTypes") { assert(("" match { case bar.A(x) => x: "Foo.foo" }) === "Foo.foo") } - // TODO [tasty]: what is happening here - // test("SUPERtype in type, version 2") { - // val BarA = (new SuperTypes.Bar()).A - // assert(("" match { case BarA(x) => x: "Foo.foo" }) === "Foo.foo") - // } + test("SUPERtype in type, version 2") { + val bar = new SuperTypes.Bar() + assert(("" match { case bar.A(x) => x : bar.foo.type }) === "Foo.foo") + } test("SUPER qualified in type tree") { assert((new SuperTypes.Baz().baz: "Foo.foo") === "Foo.foo") diff --git a/test/tasty/run/src-3/tastytest/InlineCompat.scala b/test/tasty/run/src-3/tastytest/InlineCompat.scala new file mode 100644 index 000000000000..286a30dd0f46 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/InlineCompat.scala @@ -0,0 +1,16 @@ +package tastytest + +import scala.language.experimental.macros + +import scala.reflect.macros.blackbox.Context + +object InlineCompat { + + def foo(code: String): String = macro InlineCompatScala2Macro.foo + inline def foo(inline code: String): String = code // inline method, not macro + +} + +object InlineCompatScala2Macro { + def foo(c: Context)(code: c.Tree): c.Tree = code +} diff --git a/test/tasty/run/src-3/tastytest/InlineCompat2.scala b/test/tasty/run/src-3/tastytest/InlineCompat2.scala new file mode 100644 index 000000000000..c6fcbd6090fa --- /dev/null +++ b/test/tasty/run/src-3/tastytest/InlineCompat2.scala @@ -0,0 +1,16 @@ +package tastytest + +import scala.language.experimental.macros + +import scala.reflect.macros.blackbox.Context + +object InlineCompat2 { + + def foo(code: String): String = macro InnerScala2MacroImpl.fooImpl + inline def foo(inline code: String): String = code // inline method, not macro + + object InnerScala2MacroImpl { + def fooImpl(c: Context)(code: c.Tree): c.Tree = code + } + +} diff --git a/test/tasty/run/src-3/tastytest/IntersectionErasure.scala b/test/tasty/run/src-3/tastytest/IntersectionErasure.scala new file mode 100644 index 000000000000..8a75f53056ed --- /dev/null +++ b/test/tasty/run/src-3/tastytest/IntersectionErasure.scala @@ -0,0 +1,23 @@ +package tastytest + +object IntersectionErasure { + + trait A + trait B + + @FunctionalInterface + abstract class IntersectionSAM { + def apply(arg: B with A): B with A + } + + final class IntersectionVC(val unwrapped: B with A) extends AnyVal { + def matchesInternal(that: B with A): Boolean = that == unwrapped + def matches(that: IntersectionVC): Boolean = this == that + } + + final class IntersectionVCParametric[T <: B with A](val unwrapped: T) extends AnyVal { + def matchesInternal(that: T): Boolean = that == unwrapped + def matches(that: IntersectionVCParametric[T]): Boolean = this == that + } + +} diff --git a/test/tasty/run/src-3/tastytest/InvisibleDefs.scala b/test/tasty/run/src-3/tastytest/InvisibleDefs.scala new file mode 100644 index 000000000000..5bd0190c28e1 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/InvisibleDefs.scala @@ -0,0 +1,16 @@ +package tastytest + +import scala.beans.BeanProperty + +object InvisibleDefs { + + @main def argIsHello(arg: String): Unit = assert(arg == "Hello") + + class MyBean { + + @BeanProperty + var status = "" + + } + +} diff --git a/test/tasty/run/src-3/tastytest/OperatorToken.scala b/test/tasty/run/src-3/tastytest/OperatorToken.scala new file mode 100644 index 000000000000..8ac355db1dd7 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/OperatorToken.scala @@ -0,0 +1,7 @@ +package tastytest + +enum OperatorToken { + case <:< + case =:= + case <*> +} diff --git a/test/tasty/run/src-3/tastytest/Reflection.scala b/test/tasty/run/src-3/tastytest/Reflection.scala new file mode 100644 index 000000000000..434cc62ee39c --- /dev/null +++ b/test/tasty/run/src-3/tastytest/Reflection.scala @@ -0,0 +1,22 @@ +package tastytest + +import tastytest.reflectshims + +object Reflection { + + def reflectionInvokerIdentity(ctx: reflectshims.Context)(tree: ctx.TreeShim): ctx.TreeShim = tree + + class Invoker[C <: reflectshims.Context with Singleton](val ctx: C)(root: ctx.TreeShim) { + def tree: ctx.TreeShim = root + } + + class InvokerSAM[C <: reflectshims.Context with Singleton](val ctx: C) { + + @FunctionalInterface + trait TreeFn { + def apply(tree: ctx.TreeShim): ctx.TreeShim + } + + } + +} diff --git a/test/tasty/run/src-3/tastytest/SAMErasure.scala b/test/tasty/run/src-3/tastytest/SAMErasure.scala new file mode 100644 index 000000000000..00a471cd95e0 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/SAMErasure.scala @@ -0,0 +1,18 @@ +package tastytest + +object SAMErasure { + + trait TreeShimSAMApi extends Product + + type TreeShimSAM >: Null <: AnyRef with TreeShimSAMApi + + implicit val TreeShimSAMTag: reflect.ClassTag[TreeShimSAM] = + reflect.classTag[TreeShimSAMApi].asInstanceOf[reflect.ClassTag[TreeShimSAM]] + + @FunctionalInterface + trait FunTreeShimSAM { def apply(a: TreeShimSAM): TreeShimSAM } + + @FunctionalInterface + trait FunTreeShimSAM2 { def apply(a: Array[TreeShimSAM]): Array[TreeShimSAM] } + +} diff --git a/test/tasty/run/src-3/tastytest/SuperTypes.scala b/test/tasty/run/src-3/tastytest/SuperTypes.scala index 90c3cb331777..3e89f38b4fa9 100644 --- a/test/tasty/run/src-3/tastytest/SuperTypes.scala +++ b/test/tasty/run/src-3/tastytest/SuperTypes.scala @@ -3,7 +3,7 @@ package tastytest object SuperTypes { class Foo { - final val foo = "Foo.foo" + final val foo: "Foo.foo" = "Foo.foo" } class Bar extends Foo { diff --git a/test/tasty/run/src-3/tastytest/dottyErasure/api.scala b/test/tasty/run/src-3/tastytest/dottyErasure/api.scala new file mode 100644 index 000000000000..5d563fb8eaf8 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/dottyErasure/api.scala @@ -0,0 +1,271 @@ +package tastytest + +// Keep synchronized with pre/tastytest/scala2ErasureApi/api.scala +package dottyErasure + +class foo extends scala.annotation.StaticAnnotation + +trait A +trait B +trait SubB extends B +trait C +trait Cov[+T] +trait Univ extends Any + +class D + +class VC(val self: A) extends AnyVal +class VC2(val self: A) extends AnyVal + +class Outer { + class E + trait F extends E +} + +object OpaqueHolder { + opaque type Q[T] <: Any = Cov[T] + opaque type Y[T] <: Any = Cov[T] +} +import OpaqueHolder._ + +enum Enumerated { + case C1 extends Enumerated with A + case C2 extends Enumerated with B +} + +// The parameter type of `a_XX` should erase to A, `b_XX` to `B`, etc. +// This is enforced by dottyApp/Main.scala +class Z { self => + def a_01(a: A with B): Unit = {} + def a_02X(b: B with A): Unit = {} + def a_02(a: A with B with A): Unit = {} + def a_03(a: A with (B with A)): Unit = {} + def a_04(b: A with (B with A) @foo): Unit = {} + def a_04X(b: A with (B with C) @foo): Unit = {} + def a_05(b: A with (B with A) @foo with (C with B with A) @foo): Unit = {} + + type T1 <: A with B + def a_06(a: T1): Unit = {} + + type S <: B with T1 + def a_07(a: S): Unit = {} + + type T2 <: B with A + type U <: T2 with S + def a_08(b: U): Unit = {} + + val singB: B = new B {} + def a_09(a: A with singB.type): Unit = {} + def a_10(b: singB.type with A): Unit = {} + + type V >: SubB <: B + def b_11(b: V): Unit = {} + def subb_12(b: V with SubB): Unit = {} + + def d_13(d: D with A): Unit = {} + def d_14(d: A with D): Unit = {} + + val singD: D = new D {} + def d_13x(d: singD.type with A): Unit = {} + def d_14x(d: A with singD.type): Unit = {} + + type DEq = D + def d_15(d: A with DEq): Unit = {} + def d_16(d: A with (DEq @foo)): Unit = {} + def d_17(d: DEq with A): Unit = {} + def d_18(d: (DEq @foo) with A): Unit = {} + + val singDEq: DEq @foo = new D {} + def d_15b(d: A with singDEq.type): Unit = {} + def d_16b(d: A with (singDEq.type @foo)): Unit = {} + + type DSub <: D + def d_19(a: A with DSub): Unit = {} + def d_19x(d: DSub with A): Unit = {} + def d_20(z: DSub with Z): Unit = {} + + type W1 <: A with Cov[Any] + type X1 <: Cov[Int] with W1 + def a_21(a: X1): Unit = {} + + type W2 <: A with Cov[Any] + type X2 <: Cov[Int] with W2 + def a_22(a: X2): Unit = {} + + def z_23(z: A with this.type): Unit = {} + def z_24(z: this.type with A): Unit = {} + + def a_25(b: A with (B { type T })): Unit = {} + def a_26(a: (A { type T }) with ((B with A) { type T })): Unit = {} + + def a_27(a: VC with B): Unit = {} + def a_28(a: B with VC): Unit = {} + + val o1: Outer = new Outer + val o2: Outer = new Outer + def e_29(f: o1.E with o1.F): Unit = {} + def e_30(f: o1.F with o1.E): Unit = {} + def e_31(f: o1.E with o2.F): Unit = {} + def e_32(f: o2.F with o1.E): Unit = {} + def e_33(f: Outer#E with Outer#F): Unit = {} + def e_34(f: Outer#F with Outer#E): Unit = {} + + val structural1: { type DSub <: D } = new { type DSub <: D } + def d_35(a: A with structural1.DSub): Unit = {} + def d_36(a: structural1.DSub with A): Unit = {} + def d_37(z: Z with structural1.DSub): Unit = {} + def d_38(z: structural1.DSub with Z): Unit = {} + + val structural2: { type SubCB <: C with B } = new { type SubCB <: C with B } + def b_39(c: structural2.SubCB with B): Unit = {} + def b_40(c: B with structural2.SubCB): Unit = {} + + val structural3a: { type SubB <: B; type SubCB <: C with SubB } = new { type SubB <: B; type SubCB <: C with SubB } + val structural3b: { type SubB <: B; type SubCB <: C with SubB } = new { type SubB <: B; type SubCB <: C with SubB } + def b_41(c: structural3a.SubB with structural3a.SubCB): Unit = {} + def b_42(c: structural3a.SubCB with structural3a.SubB): Unit = {} + def b_43(b: structural3a.SubB with structural3b.SubCB): Unit = {} + def b_44(c: structural3b.SubCB with structural3a.SubB): Unit = {} + + type SubStructural <: C with structural3a.SubB + def b_45(x: structural3a.SubB with SubStructural): Unit = {} + def b_46(x: structural3b.SubB with SubStructural): Unit = {} + + type Rec1 <: A with B + type Rec2 <: C with Rec1 + def a_47(a: A with B with Rec2): Unit = {} + def a_48(a: (A with B) @foo with Rec2): Unit = {} + + type F1 = A with B + type F2 = A with B + type Rec3 <: F1 + type Rec4 <: C with Rec3 + def a_49(a: F1 @foo with Rec4): Unit = {} + def a_50(a: F1 with Rec4): Unit = {} + def a_51(a: F2 @foo with Rec4): Unit = {} + def a_52(a: F2 with Rec4): Unit = {} + + type AA = A + type F3 = AA with B + type Rec5 <: F3 + type Rec6 <: C with Rec5 + def a_53(a: F3 @foo with Rec6): Unit = {} + def a_54(a: F3 with Rec6): Unit = {} + + val structural4a: { type M[X] <: A } = new { type M[X] <: A } + val structural4b: { type N <: B with structural4a.M[Int] } = new { type N <: B with structural4a.M[Int] } + def a_55(x: structural4a.M[Any] with structural4b.N): Unit = {} + + type Bla = A { type M[X] <: A } + def a_56(x: Bla#M[Any] with ({ type N <: B with Bla#M[Int] })#N): Unit = {} + type AEq = A + type Bla2 = AEq { type M[X] <: A } + def a_57(x: Bla2#M[Any] with ({ type N <: B with Bla2#M[Int] })#N): Unit = {} + + def int_58(x: Int with Singleton): Unit = {} + def int_59(x: Singleton with Int): Unit = {} + def int_60(x: Int with Any): Unit = {} + def int_61(x: Any with Int): Unit = {} + def int_62(x: Int with AnyVal): Unit = {} + def int_63(x: AnyVal with Int): Unit = {} + + def intARRAY_64(x: Array[Int with Singleton]): Unit = {} + def intARRAY_65(x: Array[_ <: Int]): Unit = {} + def intARRAY_66(x: Array[_ <: Int with Singleton]): Unit = {} + def intARRAY_67(x: Array[_ <: Singleton with Int]): Unit = {} + def intARRAY_68(x: Array[_ <: Int with Any]): Unit = {} + def intARRAY_69(x: Array[_ <: Any with Int]): Unit = {} + def intARRAY_70(x: Array[_ <: Int with AnyVal]): Unit = {} + def intARRAY_71(x: Array[_ <: AnyVal with Int]): Unit = {} + def intARRAY_71a(x: Array[_ <: Int | Int]): Unit = {} + def intARRAY_71b(x: Array[_ <: 1 | 2]): Unit = {} + + def stringARRAY_72(x: Array[String with Singleton]): Unit = {} + def stringARRAY_73(x: Array[_ <: String]): Unit = {} + def stringARRAY_74(x: Array[_ <: String with Singleton]): Unit = {} + def stringARRAY_75(x: Array[_ <: Singleton with String]): Unit = {} + def stringARRAY_76(x: Array[_ <: String with Any]): Unit = {} + def stringARRAY_77(x: Array[_ <: Any with String]): Unit = {} + def stringARRAY_78(x: Array[_ <: String with AnyRef]): Unit = {} + def stringARRAY_79(x: Array[_ <: AnyRef with String]): Unit = {} + def stringARRAY_79a(x: Array[_ <: String | String]): Unit = {} + def stringARRAY_79b(x: Array[_ <: "a" | "b"]): Unit = {} + + def object_80(x: Array[_ <: Singleton]): Unit = {} + def object_81(x: Array[_ <: AnyVal]): Unit = {} + def objectARRAY_82(x: Array[_ <: AnyRef]): Unit = {} + def object_83(x: Array[_ <: Any]): Unit = {} + def object_83a(x: Array[_ <: Matchable]): Unit = {} + def object_83b(x: Array[_ <: Int | Double]): Unit = {} + def object_83c(x: Array[_ <: String | Int]): Unit = {} + def object_83d(x: Array[_ <: Int | Matchable]): Unit = {} + def object_83e(x: Array[_ <: AnyRef | AnyVal]): Unit = {} + + def serializableARRAY_84(x: Array[_ <: Serializable]): Unit = {} + def univARRAY_85(x: Array[_ <: Univ]): Unit = {} + def aARRAY_86(x: Array[_ <: A]): Unit = {} + def aARRAY_87(x: Array[_ <: A with B]): Unit = {} + + def objectARRAY_88(x: Array[Any]): Unit = {} + def objectARRAY_89(x: Array[AnyRef]): Unit = {} + def objectARRAY_90(x: Array[AnyVal]): Unit = {} + + def stringARRAY_91(x: Array[_ <: ({ type Foo <: String with Singleton })#Foo]): Unit = {} + def stringARRAY_92(x: Array[({ type Foo <: String with Singleton })#Foo]): Unit = {} + def stringARRAY_93(x: Array[({ type Id[T] = T })#Id[String with Singleton]]): Unit = {} + + def covARRAY_94(x: Array[Q[String]]): Unit = {} + + def aARRAY_95(x: Array[(A with B { type L <: String }) with C]): Unit = {} + def aARRAY_96(x: Array[A { type L <: String }]): Unit = {} + def zARRAY_97(x: Array[self.type]): Unit = {} + def aARRAY_98(x: Array[(A { type L <: String }) with B]): Unit = {} + def stringARRAY_99[Arg <: String](x: Array[Arg]): Unit = {} + def aARRAY_100(x: Array[Bla2#M[Any] with ({ type N <: B with Bla2#M[Int] })#N]): Unit = {} + def dARRAY_101(x: Array[structural1.DSub with Z]): Unit = {} + def aARRAY_102(x: Array[F3 @foo with Rec6]): Unit = {} + def aARRAY_103(x: Array[A @foo]): Unit = {} + def dARRAY_104(x: Array[singD.type]): Unit = {} + def intARRAY_105(x: Array[3]): Unit = {} + def vcARRAY_106(x: Array[VC]): Unit = {} + def listARRAY_107(x: Array[List[_]]): Unit = {} + def intARRAY_108(x: Array[Int with String]): Unit = {} + def intARRAY_109(x: Array[String with Int]): Unit = {} + + def a_110(x: VC with VC2): Unit = {} + def a_111(x: VC2 with VC): Unit = {} + def vcARRAY_112(x: Array[VC2 with VC]): Unit = {} + def vcARRAY_113(x: Array[VC with VC2]): Unit = {} + def a_114(x: VC with D): Unit = {} + def a_115(x: D with VC): Unit = {} + def a_116(x: D with B with VC): Unit = {} + def a_117(x: B with D with VC): Unit = {} + def a_118(x: VC with B with D): Unit = {} + def a_119(x: VC with Int): Unit = {} + def a_120(x: Int with VC): Unit = {} + + def object_121[T](x: Array[T]): Unit = {} + def object_122(x: Array[_ <: AnyVal with Singleton]): Unit = {} + def objectARRAY_123(x: Array[AnyVal with Singleton]): Unit = {} + def object_124[T, U](x: Array[T with U]): Unit = {} + def objectARRAY_125(x: Array[({ type W <: String }) with ({ type X <: Int })]): Unit = {} + def covARRAY_126(x: Array[Q[B] with Y[SubB]]): Unit = {} + def covARRAY_127(x: Array[Q[B] with Y[SubB] { type X <: Cov[String] }]): Unit = {} + + type SubAny <: Any + type SubAnyVal <: AnyVal + + def object_128(x: Array[SubAny with SubAnyVal]): Unit = {} + def intARRAYARRAY_129(x: Array[Array[Int]]): Unit = {} + def intARRAYARRAY_130(x: Array[_ <: Array[Int]]): Unit = {} + def objectARRAY_130(x: Array[_ <: Array[_ <: AnyVal]]): Unit = {} + def intARRAY_131(x: Array[String] with Array[Int]): Unit = {} + + def enumerated_132(x: Enumerated.C1.type with Enumerated.C2.type): Unit = {} + def enumerated_133(x: Enumerated.C2.type with Enumerated.C1.type): Unit = {} + def enumerated_134(x: Enumerated.C1.type): Unit = {} + def enumeratedARRAY_135(x: Array[Enumerated.C1.type]): Unit = {} + def enumeratedARRAY_136(x: Array[Enumerated.C2.type with Enumerated.C1.type]): Unit = {} + def enumeratedARRAY_137(x: Array[Enumerated.C1.type with Enumerated.C2.type]): Unit = {} + +} diff --git a/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala b/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala index a267db9b6cc7..71b901161da1 100644 --- a/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala +++ b/test/tasty/test/scala/tools/tastytest/TastyTestJUnit.scala @@ -1,6 +1,6 @@ package scala.tools.tastytest -import org.junit.{Test => test} +import org.junit.{Test => test, BeforeClass => setup, AfterClass => teardown} import org.junit.Assert._ import scala.util.{Try, Failure, Properties} @@ -63,17 +63,32 @@ class TastyTestJUnit { additionalDottySettings = Nil ).eval - val propSrc = "tastytest.src" - val propPkgName = "tastytest.packageName" + val propSrc = "tastytest.src" + val propPkgName = "tastytest.packageName" def assertPropIsSet(prop: String): String = { - Properties.propOrNull(prop).ensuring(_ != null, s"-D$prop is not set") + Properties.propOrElse(prop, { + fail(s"-D$prop is not set") + "(unknown)" + }) } } -import scala.reflect.runtime.ReflectionUtils - object TastyTestJUnit { + + private[this] var _dottyClassLoader: Dotc.ClassLoader = _ + implicit def dottyClassLoader: Dotc.ClassLoader = _dottyClassLoader + + @setup + def init(): Unit = { + _dottyClassLoader = Dotc.initClassloader().get + } + + @teardown + def finish(): Unit = { + _dottyClassLoader = null + } + final implicit class TryOps(val op: Try[Unit]) extends AnyVal { def eval: Unit = op match { case Failure(err) => fail(err.toString) diff --git a/versions.properties b/versions.properties index 7ece1c784286..971b4a002731 100644 --- a/versions.properties +++ b/versions.properties @@ -1,5 +1,5 @@ # Scala version used for bootstrapping (see README.md) -starr.version=2.13.4 +starr.version=2.13.6 # These are the versions of the modules that go with this release. # Artifact dependencies: