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..2fc36f1b3cf2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,42 @@ +name: Scala Merge CI + +on: + push: + branches: ['2.*.x'] + +defaults: + run: + shell: bash + +permissions: + contents: read + +jobs: + build_and_test: + name: Test + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + java: [8, 11, 17] + runs-on: ${{matrix.os}} + steps: + - run: git config --global core.autocrlf false + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: ${{matrix.java}} + cache: sbt + + - 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..da448597c4b7 100644 --- a/.gitignore +++ b/.gitignore @@ -57,7 +57,13 @@ local.sbt jitwatch.out +# Used by the restarr/restarrFull commands as target directories +/build-restarr/ +/target-restarr/ + # metals .metals .bloop project/**/metals.sbt + +.bsp diff --git a/.scala-steward.conf b/.scala-steward.conf new file mode 100644 index 000000000000..3b05785b80cb --- /dev/null +++ b/.scala-steward.conf @@ -0,0 +1,18 @@ +# don't rush to take updates, but don't fall indefinitely behind, +# either. hopefully this is a reasonable compromise value? +pullRequests.frequency = "14 days" + +# only used internally, and they aren't ours (we aren't dogfooding +# them), and updates are unlikely to benefit us, so there's really no +# need to keep them current +updates.ignore = [ { groupId = "com.fasterxml.jackson.core"} ] +updates.ignore = [ { groupId = "org.slf4j"} ] +updates.ignore = [ { groupId = "org.eclipse.jgit"} ] +updates.ignore = [ { groupId = "org.openjdk.jol"} ] + +# Ant support is deprecated, so leave the version where it is +updates.ignore = [ { groupId = "org.apache.ant"} ] + +# OSGi stuff is fragile and we suspect it is little-used, +# so let's prefer stability +updates.ignore = [ { groupId = "biz.aQute.bnd"} ] diff --git a/.travis.yml b/.travis.yml index 994d9c446ecc..7da7ef852558 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,130 +8,158 @@ 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-jdk17: &cron-jdk17 + if: type = cron AND repo = scala/scala + env: ADOPTOPENJDK=17 + + 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) - # 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 + 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-jdk17 + + - <<: *test1 + <<: *cron-jdk17 + + - <<: *test2 + <<: *cron-jdk17 + + - 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: - ADOPTOPENJDK=8 - secure: "P8EqpZoin/YTnwel9TTxSSAHtXfZ4M262BKXlYUZmjoQsjyvXDAeZ7yAqgAvX5BeRFrGkBToPiE+V60stdWkPKs3+9COw2BDUB1CULBHhRY9Lxordmz0xVhgEfsoH4f6r6wOlIQ9kuaWhmP+JdB/mzOHZhLch9ziPi8O46Z8t4k=" # PRIV_KEY_SECRET, for scripts/travis-publish-spec.sh - - secure: "T1fxtvLTxioyXJYiC/zVYdNYsBOt+0Piw+xE04rB1pzeKahm9+G2mISdcAyqv6/vze9eIJt6jNHHpKX32/Z3Cs1/Ruha4m3k+jblj3S0SbxV6ht2ieJXLT5WoUPFRrU68KXI8wqUadXpjxeJJV53qF2FC4lhfMUsw1IwwMhdaE8=" # PRIVATE_REPO_PASS - - secure: "feE5A8mYNpkNQKVwCj3aXrwjVrJWh/4ENpRfFlr2HOD9ORk1GORD5Yq907WZd+dTkYK54Lh1gA+qHOCIDgJHbi9ZLU+kjzEjtYKF6lQy6Wb0LI8smTOnAA6IWVVYifiXw8d66MI2MKZb2jjGeIzy8Q00SZjLhEGjLyTeCIB88Ws=" # SONA_USER - - secure: "ek3As5q2tL8UBXcxSBbv4v5YgsoPD41SCzPOSu72kzfbngyxgQxrcziU5pIM+Lib9KaWex7hVVWNL38tMyDbu+0OpDv8bPjMujzlDx5I2pJUfuOJo7QRYsJE1nsXcY4cA72cCLfbRcLEkvtDAhcdLSaUOqlyQe5BY4X4fY5eoPA=" # SONA_PASS - - secure: "dbAvl6KEuLwZ0MVQPZihFsPzCdiLbX0EFk3so+hcfEbksrmLQ1tn4X5ZM7Wy1UDR8uN9lxngEwHch7a7lKqpugzmXMew9Wnikr9WBWbJT77Z+XJ/jHI6YuiCRpRo+nvxXGp9Ry80tSIgx5eju0J83IaJL41BWlBkvyAd7YAHORI=" # GPG_SUBKEY_SECRET - - secure: "RTyzS6nUgthupw5M0fPwTlcOym1sWgBo8eXYepB2xGiQnRu4g583BGuNBW1UZ3vIjRETi/UKQ1HtMR+i7D8ptF1cNpomopncVJA1iy7pU2w0MJ0xgIPMuvtkIa3kxocd/AnxAp+UhUad3nC8lDpkvZsUhhyA0fb4iPKipd2b2xY=" # TRAVIS_TOKEN (login with GitHub as SethTisue) + - secure: "T1fxtvLTxioyXJYiC/zVYdNYsBOt+0Piw+xE04rB1pzeKahm9+G2mISdcAyqv6/vze9eIJt6jNHHpKX32/Z3Cs1/Ruha4m3k+jblj3S0SbxV6ht2ieJXLT5WoUPFRrU68KXI8wqUadXpjxeJJV53qF2FC4lhfMUsw1IwwMhdaE8=" # PRIVATE_REPO_PASS, for publishing to scala-ci Artifactory + - secure: "dbAvl6KEuLwZ0MVQPZihFsPzCdiLbX0EFk3so+hcfEbksrmLQ1tn4X5ZM7Wy1UDR8uN9lxngEwHch7a7lKqpugzmXMew9Wnikr9WBWbJT77Z+XJ/jHI6YuiCRpRo+nvxXGp9Ry80tSIgx5eju0J83IaJL41BWlBkvyAd7YAHORI=" # GPG_SUBKEY_SECRET, so we can sign JARs + - secure: "RTyzS6nUgthupw5M0fPwTlcOym1sWgBo8eXYepB2xGiQnRu4g583BGuNBW1UZ3vIjRETi/UKQ1HtMR+i7D8ptF1cNpomopncVJA1iy7pU2w0MJ0xgIPMuvtkIa3kxocd/AnxAp+UhUad3nC8lDpkvZsUhhyA0fb4iPKipd2b2xY=" # TRAVIS_TOKEN (login with GitHub as SethTisue), for triggering scala-dist job + - secure: "FvhicbSeys7VNTj9ZP/aNT0NhiQP/NNV0KRfK7IHxi3uOeaxFVfaQsln4lzqZn8dkntgzzNrE/VhvMIknfnISAPX7bShy6SRyj3V2BlcUpuem8WtwmkCaZ42xlCJteBL7NW0auG/8rxrNIAJXbRObqF+YdK6XsRMWaBMQHky+ss=" # SONA_USER, token username for publishing to Sonatype + - secure: "Y8CTlEdQbAS+P+LgkY05al/KSbccbX5BATm9N2GI9C6wH7oQuUU/VtU+bwvzeiF9DCsZPjrWXsa0JCuIQE+UzK1NWXxlkhUdGCaCBZ/nUecouBtMk2x/h7uIGpeYInxA041r5SuBecZuZQI79nhl+BwZSLu82Vy1QtP0/Cd8oRM=" # SONA_PASS, token password for publishing to Sonatype # caching for sdkman / sbt / ivy / coursier imported from scala-dev cache: @@ -139,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..197f841d78db 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,9 +10,9 @@ In 2014, you -- the Scala community -- matched the core team at EPFL in number o We are super happy about this, and are eager to make your experience contributing to Scala productive and satisfying, so that we can keep up this growth. We can't do this alone (nor do we want to)! -This is why we're collecting these notes on how to contribute, and we hope you'll share your experience to improve the process for the next contributor! (Feel free to send a PR for this note, send your thoughts to scala/contributors (Gitter) or contributors.scala-lang.org (Discourse).) +This is why we're collecting these notes on how to contribute, and we hope you'll share your experience to improve the process for the next contributor! (Feel free to send a PR for this note, send your thoughts to \#scala-contributors (on [Discord](https://discord.com/invite/scala)) or contributors.scala-lang.org (Discourse).) -By the way, the team at Lightbend is: @lrytz, @retronym, @SethTisue, and @dwijnand. +By the way, the team at Lightbend is: @lrytz, @retronym, @SethTisue, and @dwijnand. ## What kind of PR are you submitting? @@ -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 @@ -268,8 +285,7 @@ See the [scala-jenkins-infra repo](https://github.com/scala/scala-jenkins-infra) ### Pass code review Your PR will need to be assigned to one or more reviewers. You can suggest reviewers -yourself; if you're not sure, see the list in [README.md](README.md) or ask on scala/contributors (Gitter) -or contributors.scala-lang.org (Discourse). +yourself; if you're not sure, see the list in [README.md](README.md) or ask on \#scala-contributors (on [Discord](https://discord.com/invite/scala)) or contributors.scala-lang.org (Discourse). To assign a reviewer, add a "review by @reviewer" to the PR description or in a comment on your PR. @@ -283,8 +299,8 @@ and `push -f` to the branch. This is to keep the git history clean. Additional c are OK if they stand on their own. Once all these conditions are met, we will merge your changes -- if we -agree with it! We are available on scala/contributors (Gitter) or -contributors.scala-lang.org (Discourse) to discuss changes beforehand, +agree with it! We are available on \#scala-contributors (on [Discord](https://discord.com/invite/scala)) +or contributors.scala-lang.org (Discourse) to discuss changes beforehand, before you put in the coding work. diff --git a/NOTICE b/NOTICE index ac3a26b40f48..611c177829c1 100644 --- a/NOTICE +++ b/NOTICE @@ -1,6 +1,6 @@ Scala -Copyright (c) 2002-2020 EPFL -Copyright (c) 2011-2020 Lightbend, Inc. +Copyright (c) 2002-2022 EPFL +Copyright (c) 2011-2022 Lightbend, Inc. Scala includes software developed at LAMP/EPFL (https://lamp.epfl.ch/) and diff --git a/README.md b/README.md index 63f3edafc6bd..e7613b0abed5 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ For more information on building and developing the core of Scala, read the rest # Get in touch! -In order to get in touch with other Scala contributors, join -[scala/contributors](https://gitter.im/scala/contributors) (Gitter) or post on +In order to get in touch with other Scala contributors, join the +\#scala-contributors channel on the [Scala Discord](https://discord.com/invite/scala) chat, or post on [contributors.scala-lang.org](https://contributors.scala-lang.org) (Discourse). If you need some help with your PR at any time, please feel free to @-mention anyone from the list below, and we will do our best to help you out: @@ -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..e956c9dfba7c 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") @@ -36,15 +36,17 @@ 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 jolDep = "org.openjdk.jol" % "jol-core" % "0.13" +val junitInterfaceDep = "com.github.sbt" % "junit-interface" % "0.13.3" % Test +val scalacheckDep = "org.scalacheck" %% "scalacheck" % "1.15.4" % Test +val jolDep = "org.openjdk.jol" % "jol-core" % "0.16" val asmDep = "org.scala-lang.modules" % "scala-asm" % versionProps("scala-asm.version") val jlineDep = "org.jline" % "jline" % versionProps("jline.version") val jnaDep = "net.java.dev.jna" % "jna" % versionProps("jna.version") val jlineDeps = Seq(jlineDep, jnaDep) val testInterfaceDep = "org.scala-sbt" % "test-interface" % "1.0" -val diffUtilsDep = "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0" +val diffUtilsDep = "io.github.java-diff-utils" % "java-diff-utils" % "4.12" + +val projectFolder = settingKey[String]("subfolder in src when using configureAsSubproject, else the project name") // `set Global / fatalWarnings := true` to enable -Werror for the certain modules // currently, many modules cannot support -Werror; ideally this setting will eventually @@ -54,12 +56,13 @@ val fatalWarnings = settingKey[Boolean]("whether or not warnings should be fatal // enable fatal warnings automatically on CI Global / fatalWarnings := insideCI.value +Global / credentials ++= { + val file = Path.userHome / ".credentials" + if (file.exists && !file.isDirectory) List(Credentials(file)) + else Nil +} + lazy val publishSettings : Seq[Setting[_]] = Seq( - credentials ++= { - val file = Path.userHome / ".credentials" - if (file.exists && !file.isDirectory) List(Credentials(file)) - else Nil - }, // Add a "default" Ivy configuration because sbt expects the Scala distribution to have one: ivyConfigurations += Configuration.of("Default", "default", "Default", true, Vector(Configurations.Runtime), true), publishMavenStyle := true @@ -70,7 +73,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.9" Global / baseVersionSuffix := "SNAPSHOT" ThisBuild / organization := "org.scala-lang" ThisBuild / homepage := Some(url("https://www.scala-lang.org")) @@ -112,7 +115,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 } @@ -130,20 +133,21 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories // we always assume that Java classes are standalone and do not have any dependency // on Scala classes compileOrder := CompileOrder.JavaThenScala, + projectFolder := thisProject.value.id, // overridden in configureAsSubproject Compile / javacOptions ++= Seq("-g", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked"), Compile / unmanagedJars := Seq.empty, // no JARs in version control! Compile / sourceDirectory := baseDirectory.value, Compile / unmanagedSourceDirectories := List(baseDirectory.value), - Compile / unmanagedResourceDirectories += (ThisBuild / baseDirectory).value / "src" / thisProject.value.id, + Compile / unmanagedResourceDirectories += (ThisBuild / baseDirectory).value / "src" / projectFolder.value, sourcesInBase := false, Compile / scalaSource := (Compile / sourceDirectory).value, // for some reason sbt 1.4 issues unused-settings warnings for this, it seems to me incorrectly 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, - Compile / classDirectory := buildDirectory.value / "quick/classes" / thisProject.value.id, - Compile / doc / target := buildDirectory.value / "scaladoc" / thisProject.value.id, + target := (ThisBuild / target).value / projectFolder.value, + Compile / classDirectory := buildDirectory.value / "quick/classes" / projectFolder.value, + Compile / doc / target := buildDirectory.value / "scaladoc" / projectFolder.value, // given that classDirectory and doc target are overridden to be _outside_ of target directory, we have // to make sure they are being cleaned properly cleanFiles += (Compile / classDirectory).value, @@ -157,6 +161,9 @@ 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", + // We use @nowarn for some methods that are deprecated in Java > 8 + Compile / scalacOptions += "-Wconf:cat=unused-nowarn:s", + Compile / scalacOptions ++= Seq("-deprecation", "-feature"), Compile / doc / scalacOptions ++= Seq( "-doc-footer", "epfl", "-diagrams", @@ -188,7 +195,7 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories pomExtra := { scm:git:git://github.com/scala/scala.git - https://github.com/scala/scala.git + https://github.com/scala/scala GitHub @@ -225,7 +232,11 @@ lazy val commonSettings = instanceSettings ++ clearSourceAndResourceDirectories lazy val fatalWarningsSettings = Seq( Compile / scalacOptions ++= { - if (fatalWarnings.value) Seq("-Werror", "-Wconf:cat=unused-nowarn:is") + if (fatalWarnings.value) Seq("-Werror") + else Nil + }, + Compile / javacOptions ++= { + if (fatalWarnings.value) Seq("-Werror") else Nil }, Compile / doc / scalacOptions -= "-Werror", // there are too many doc errors to enable this right now @@ -346,7 +357,7 @@ def setForkedWorkingDirectory: Seq[Setting[_]] = { } // This project provides the STARR scalaInstance for bootstrapping -lazy val bootstrap = project in file("target/bootstrap") +lazy val bootstrap = project.in(file("target/bootstrap")).settings(bspEnabled := false) lazy val library = configureAsSubproject(project) .settings(generatePropertiesFileSettings) @@ -413,11 +424,11 @@ lazy val reflect = configureAsSubproject(project) raw"""scala.tools.nsc;resolution:=optional;version="$${range;[==,=+);$${ver}}",""" + "*"), fixPom( - "/project/name" -> Scala Compiler, - "/project/description" -> Compiler for the Scala Programming Language, + "/project/name" -> Scala Reflect, + "/project/description" -> Reflection Library for the Scala Programming Language, "/project/packaging" -> jar ), - apiURL := Some(url(s"https://www.scala-lang.org/api/${versionProperties.value.mavenVersion}/scala-${thisProject.value.id}/")), + apiURL := Some(url(s"https://www.scala-lang.org/api/${versionProperties.value.mavenVersion}/scala-${projectFolder.value}/")), MimaFilters.mimaSettings, ) .dependsOn(library) @@ -432,6 +443,7 @@ lazy val compiler = configureAsSubproject(project) name := "scala-compiler", description := "Scala Compiler", libraryDependencies += asmDep, + libraryDependencies += diffUtilsDep, // These are only needed for the POM: // TODO: jline dependency is only needed for the REPL shell, which should move to its own jar libraryDependencies ++= jlineDeps, @@ -442,7 +454,11 @@ lazy val compiler = configureAsSubproject(project) // (with strings) to deal with mutual recursion Compile / packageBin / products := (Compile / packageBin / products).value ++ - Seq((Compile / dependencyClasspath).value.find(_.get(moduleID.key).map(id => (id.organization, id.name, id.revision)).contains((asmDep.organization, asmDep.name, asmDep.revision))).get.data) ++ + (Compile / dependencyClasspath).value.filter(_.get(moduleID.key).map(id => (id.organization, id.name, id.revision)) match { + case Some((diffUtilsDep.organization, diffUtilsDep.name, diffUtilsDep.revision)) => true + case Some((asmDep.organization, asmDep.name, asmDep.revision)) => true + case _ => false + }).map(_.data) ++ (LocalProject("interactive") / Compile / packageBin / products).value ++ (LocalProject("scaladoc") / Compile / packageBin / products).value ++ (LocalProject("repl") / Compile / packageBin / products).value ++ @@ -487,6 +503,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" @@ -500,7 +517,7 @@ lazy val compiler = configureAsSubproject(project) "/project/description" -> Compiler for the Scala Programming Language, "/project/packaging" -> jar ), - apiURL := Some(url(s"https://www.scala-lang.org/api/${versionProperties.value.mavenVersion}/scala-${thisProject.value.id}/")), + apiURL := Some(url(s"https://www.scala-lang.org/api/${versionProperties.value.mavenVersion}/scala-${projectFolder.value}/")), pomDependencyExclusions += (("org.scala-lang.modules", "scala-asm")) ) .dependsOn(library, reflect) @@ -615,7 +632,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"), ) @@ -625,8 +642,9 @@ lazy val specLib = project.in(file("test") / "instrumented") .settings(commonSettings) .settings(disableDocs) .settings(fatalWarningsSettings) - .settings(publish / skip := true) .settings( + publish / skip := true, + bspEnabled := false, Compile / sourceGenerators += Def.task { import scala.collection.JavaConverters._ val srcBase = (library / Compile / sourceDirectories).value.head / "scala/runtime" @@ -663,12 +681,17 @@ 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:inline:scala/**", "-Wopt"), + scalacOptions ++= Seq("-feature", "-opt:l:inline", "-opt-inline-from:scala/**", "-opt-warnings"), + // Skips JMH source generators during IDE import to avoid needing to compile scala-library during the import + // should not be needed once sbt-jmh 0.4.3 is out (https://github.com/sbt/sbt-jmh/pull/207) + Jmh / bspEnabled := false ).settings(inConfig(JmhPlugin.JmhKeys.Jmh)(scalabuild.JitWatchFilePlugin.jitwatchSettings)) @@ -690,6 +713,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,20 +728,22 @@ 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( "-feature", "-Xlint:-valpattern,_", "-Wconf:msg=match may not be exhaustive:s", // if we missed a case, all that happens is the test fails + "-Wconf:cat=lint-nullary-unit&site=.*Test:s", // normal unit test style "-Ypatmat-exhaust-depth", "40", // despite not caring about patmat exhaustiveness, we still get warnings for this ), 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) + Test / unmanagedSourceDirectories := List(baseDirectory.value), + Test / headerSources := Nil, ) lazy val tasty = project.in(file("test") / "tasty") @@ -722,7 +753,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 +762,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 +795,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) ) @@ -764,8 +817,9 @@ def osgiTestProject(p: Project, framework: ModuleID) = p .dependsOn(library, reflect, compiler) .settings(commonSettings) .settings(disableDocs) - .settings(publish / skip := true) .settings( + publish / skip := true, + bspEnabled := false, Test / fork := true, Test / parallelExecution := false, libraryDependencies ++= { @@ -778,16 +832,16 @@ def osgiTestProject(p: Project, framework: ModuleID) = p "org.ops4j.pax.exam" % "pax-exam-link-assembly" % paxExamVersion, "org.ops4j.pax.url" % "pax-url-aether" % "2.4.1", "org.ops4j.pax.swissbox" % "pax-swissbox-tracker" % "1.8.1", - "ch.qos.logback" % "logback-core" % "1.1.3", - "ch.qos.logback" % "logback-classic" % "1.1.3", - "org.slf4j" % "slf4j-api" % "1.7.12", + "ch.qos.logback" % "logback-core" % "1.2.8", + "ch.qos.logback" % "logback-classic" % "1.2.8", + "org.slf4j" % "slf4j-api" % "1.7.32", framework % Test ) }, 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, @@ -803,9 +857,8 @@ def osgiTestProject(p: Project, framework: ModuleID) = p cleanFiles += (ThisBuild / buildDirectory).value / "osgi" ) -lazy val partestJavaAgent = Project("partestJavaAgent", file(".") / "src" / "partest-javaagent") - .settings(commonSettings) - .settings(generatePropertiesFileSettings) +lazy val partestJavaAgent = configureAsSubproject(project, srcdir = Some("partest-javaagent")) + .settings(fatalWarningsSettings) .settings(disableDocs) .settings( libraryDependencies += asmDep, @@ -838,10 +891,11 @@ 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 / javaOptions ++= { if (scala.util.Properties.isJavaAtLeast("18")) List("-Djava.security.manager=allow") else Nil }, 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 +933,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" @@ -888,6 +943,7 @@ lazy val scalaDist = Project("scalaDist", file(".") / "target" / "scala-dist-dis .settings(commonSettings) .settings(disableDocs) .settings( + bspEnabled := false, name := "scala-dist", Compile / packageBin / mappings ++= { val binBaseDir = buildDirectory.value / "pack" @@ -939,9 +995,9 @@ def partestDesc(in: String): Def.Initialize[Task[(Result[Unit], String)]] = lazy val root: Project = (project in file(".")) .settings(disableDocs) - .settings(publish / skip := true) .settings(generateBuildCharacterFileSettings) .settings( + publish / skip := true, commands ++= ScriptCommands.all, extractBuildCharacterPropertiesFile := { val jar = (bootstrap / scalaInstance).value.allJars.find(_.getName contains "-compiler").get @@ -987,7 +1043,7 @@ lazy val root: Project = (project in file(".")) setIncOptions ) .aggregate(library, reflect, compiler, interactive, repl, replFrontend, - scaladoc, scalap, testkit, partest, junit, scalaDist).settings( + scaladoc, scalap, testkit, partest, junit, scalacheck, tasty, tastytest, scalaDist).settings( Compile / sources := Seq.empty, onLoadMessage := s"""|*** Welcome to the sbt build definition for Scala! *** |version=${(Global / version).value} scalaVersion=${(Global / scalaVersion).value} @@ -1080,6 +1136,7 @@ lazy val distDependencies = Seq(replFrontend, compiler, library, reflect, scalap lazy val dist = (project in file("dist")) .settings(commonSettings) .settings( + bspEnabled := false, libraryDependencies ++= jlineDeps, mkBin := mkBinImpl.value, mkQuick := Def.task { @@ -1091,7 +1148,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 / projectFolder.value, Compile / packageBin := { val targetDir = (ThisBuild / buildDirectory).value / "pack" / "lib" val jlineJAR = findJar((Compile / dependencyClasspath).value, jlineDep).get.data @@ -1128,9 +1185,9 @@ def configureAsSubproject(project: Project, srcdir: Option[String] = None): Proj (project in base) .settings(scalaSubprojectSettings) .settings(generatePropertiesFileSettings) + .settings(projectFolder := srcdir.getOrElse(project.id)) } -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 +1255,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 @@ -1389,13 +1444,6 @@ def findJar(files: Seq[Attributed[File]], dep: ModuleID): Option[Attributed[File files.find(_.get(moduleID.key).map(extract _) == Some(extract(dep))) } -// WhiteSource -whitesourceProduct := "Lightbend Reactive Platform" -whitesourceAggregateProjectName := "scala-2.13-stable" -whitesourceIgnoredScopes := Vector("test", "scala-tool") -// for some reason sbt 1.4 issues an unused-setting warning for this, I don't understand why -Global / excludeLintKeys += whitesourceIgnoredScopes - { scala.build.TravisOutput.installIfOnTravis() Nil diff --git a/doc/LICENSE.md b/doc/LICENSE.md index 83ef781d15f1..1cb1641772c7 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-2022 EPFL -Copyright (c) 2011-2020 Lightbend, Inc. +Copyright (c) 2011-2022 Lightbend, Inc. All rights reserved. diff --git a/doc/License.rtf b/doc/License.rtf index 376ec02cb530..f54f532da402 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-2022 EPFL\ +Copyright (c) 2011-2022 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..5fb86c005d87 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.2.0" // TASTy version 28.2-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.2.0" val compileWithDotty: Boolean = Option(System.getProperty("scala.build.compileWithDotty")).map(_.toBoolean).getOrElse(false) lazy val commonSettings = Seq( @@ -34,11 +38,12 @@ object DottySupport { // Needed to compile scala3-library together with scala-library compileOrder := CompileOrder.Mixed, - // Add the scala3-library sources to the sourcepath + // Add the scala3-library sources to the sourcepath and disable fatal warnings Compile / scalacOptions := { val old = (Compile / scalacOptions).value + val withoutFatalWarnings = old.filter(opt => opt != "-Werror" && !opt.startsWith("-Wconf")) - val (beforeSourcepath, "-sourcepath" :: oldSourcepath :: afterSourcePath) = old.span(_ != "-sourcepath") + val (beforeSourcepath, "-sourcepath" :: oldSourcepath :: afterSourcePath) = withoutFatalWarnings.span(_ != "-sourcepath") val newSourcepath = ((Compile / sourceManaged).value / "scala3-library-src").getAbsolutePath + 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 b6e61976b735..68dd4f35c481 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.8"), ) 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,18 +25,25 @@ 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"), - - // #9487 - ProblemFilters.exclude[MissingClassProblem]("scala.reflect.ClassTag$cache$"), - - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree#Tree.redWithRight"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree#Tree.redWithLeftRight"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree#Tree.blackWithLeftRight"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree#Tree.redWithLeft"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree.partitionKeys"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree.filterKeys"), + // 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"), + + // scala/scala#9819 + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.mutable.PriorityQueue#ResizableArrayAccess.p_ensureAdditionalSize"), // private[PriorityQueue] + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.PStatics"), // private[scala] + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.PStatics$"), // private[scala] + + // internal to wrappers + ProblemFilters.exclude[NewMixinForwarderProblem]("scala.collection.convert.JavaCollectionWrappers#JMapWrapperLike.getOrElseUpdate"), + ProblemFilters.exclude[NewMixinForwarderProblem]("scala.collection.convert.JavaCollectionWrappers#JMapWrapperLike.updateWith"), + + // removing case classing from wrappers means synthetic methods and parent types and companions are missing + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.convert.JavaCollectionWrappers#*"), + ProblemFilters.exclude[MissingTypesProblem]("scala.collection.convert.JavaCollectionWrappers$*"), + ProblemFilters.exclude[MissingClassProblem]("scala.collection.convert.JavaCollectionWrappers$*"), ) override val buildSettings = Seq( diff --git a/project/Osgi.scala b/project/Osgi.scala index 029ecdf82f7c..4dade3aa03a4 100644 --- a/project/Osgi.scala +++ b/project/Osgi.scala @@ -8,11 +8,11 @@ import sbt.Keys._ import collection.JavaConverters._ import VersionUtil.versionProperties -/** OSGi packaging for the Scala build, distilled from sbt-osgi. We do not use sbt-osgi because it - * depends on a newer version of BND which gives slightly different output (probably OK to upgrade - * in the future, now that the Ant build has been removed) and does not allow a crucial bit of +/** OSGi packaging for the Scala build, distilled from sbt-osgi. + * + * We don't use sbt-osgi (yet) because it does not allow a crucial bit of * configuration that we need: Setting the classpath for BND. In sbt-osgi this is always - * `fullClasspath in Compile` whereas we want `products in Compile in packageBin`. */ + * `fullClasspath in Compile` whereas we want `products in Compile in packageBin`. */ object Osgi { val bundle = TaskKey[File]("osgiBundle", "Create an OSGi bundle.") val bundleName = SettingKey[String]("osgiBundleName", "The Bundle-Name for the manifest.") @@ -29,11 +29,30 @@ object Osgi { "Bundle-Name" -> bundleName.value, "Bundle-SymbolicName" -> bundleSymbolicName.value, "ver" -> v, - "Export-Package" -> "*;version=${ver};-split-package:=merge-first", + + // bnd 3.0 fixes for https://github.com/bndtools/bnd/issues/971. This changes our OSGi + // metadata by adding Import-Package automatically for all of our exported packages. + // Supposedly this is the right thing to do: https://blog.osgi.org/2007/04/importance-of-exporting-nd-importing.html + // but I'm disabling the feature (`-noimport:=true`) to avoid changing this detail of + // our little understood OSGi metadata for now. + "Export-Package" -> "*;version=${ver};-noimport:=true;-split-package:=merge-first", + "Import-Package" -> raw"""scala.*;version="$${range;[==,=+);$${ver}}",*""", "Bundle-Version" -> v, "Bundle-RequiredExecutionEnvironment" -> "JavaSE-1.8", - "-eclipse" -> "false" + "-eclipse" -> "false", + + // Great new feature in modern bnd versions: reproducible builds. + // Omits the Bundle-LastModified header and avoids using System.currentTimeMillis + // for ZIP metadata. + "-reproducible" -> "true", + + // https://github.com/bndtools/bnd/commit/2f1d89428559d21857b87b6d5b465a18a300becc (bndlib 4.2.0) + // seems to have fixed a bug in its detection class references in Class.forName("some.Class") + // For our build, this adds an import on the package "com.cloudius.util" (referred to by an optional + // part of JLine. This directive disables the Class.forName scanning. An alternative fix would be + // direct this to be an optional dependency (as we do for jline itself with `"Import-Package" -> ("jline.*;resolution:=optional," + ... )`) + "-noclassforname" -> "true" // ) }, jarlist := false, diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index 7243fa9f631d..89d11246b11f 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -82,22 +82,22 @@ 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", + "-Ybreak-cycles", "-Ydebug", "-Ydebug-type-error", "-Ycompact-trees", "-YdisableFlatCpCaching", "-Ydoc-debug", "-Yide-debug", - "-Yissue-debug", "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand", + "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand", "-Yno-completion", "-Yno-generic-signatures", "-Yno-imports", "-Yno-predef", "-Ymacro-annotations", "-Ypatmat-debug", "-Yno-adapted-args", "-Ypos-debug", "-Ypresentation-debug", "-Ypresentation-strict", "-Ypresentation-verbose", "-Yquasiquote-debug", "-Yrangepos", "-Yreify-copypaste", "-Yreify-debug", "-Yrepl-class-based", "-Yrepl-sync", "-Yshow-member-pos", "-Yshow-symkinds", "-Yshow-symowners", "-Yshow-syms", "-Yshow-trees", "-Yshow-trees-compact", "-Yshow-trees-stringified", "-Ytyper-debug", "-Ywarn-dead-code", "-Ywarn-numeric-widen", "-Ywarn-value-discard", "-Ywarn-extra-implicit", "-Ywarn-self-implicit", "-V", - "-Vclasspath", "-Vdebug", "-Vdebug-tasty", "-Vdoc", "-Vfree-terms", "-Vfree-types", + "-Vclasspath", "-Vdebug", "-Vdebug-tasty", "-Vdebug-type-error", "-Vdoc", "-Vfree-terms", "-Vfree-types", "-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") @@ -140,5 +140,5 @@ object ScalaOptionParser { private def scaladocPathSettingNames = List("-doc-root-content", "-diagrams-dot-path") private def scaladocMultiStringSettingNames = List("-doc-external-doc") - private val targetSettingNames = (8 to 17).map(_.toString).flatMap(v => v :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList + private val targetSettingNames = (8 to 19).map(_.toString).flatMap(v => v :: s"jvm-1.$v" :: s"jvm-$v" :: s"1.$v" :: Nil).toList } diff --git a/project/ScaladocSettings.scala b/project/ScaladocSettings.scala index ed4b8a188f38..125b4836839f 100644 --- a/project/ScaladocSettings.scala +++ b/project/ScaladocSettings.scala @@ -1,13 +1,13 @@ package scala.build import sbt._ -import sbt.Keys.{ artifact, dependencyClasspath, moduleID, resourceManaged } +import sbt.Keys.{ artifact, externalDependencyClasspath, moduleID, resourceManaged } object ScaladocSettings { // when this changes, the integrity check in HtmlFactory.scala also needs updating val webjarResources = Seq( - "org.webjars" % "jquery" % "3.5.1" + "org.webjars" % "jquery" % "3.6.0" ) def extractResourcesFromWebjar = Def.task { @@ -15,8 +15,10 @@ object ScaladocSettings { s.get(artifact.key).isDefined && s.get(moduleID.key).exists(_.organization == "org.webjars") val dest = (resourceManaged.value / "webjars").getAbsoluteFile IO.createDirectory(dest) - val classpathes = (Compile / dependencyClasspath).value - val files: Seq[File] = classpathes.filter(isWebjar).flatMap { classpathEntry => + // externalDependencyClasspath (not dependencyClasspath) to avoid compiling + // upstream projects (library, reflect, compiler) on bsp `buildTarget/resources` + val classpaths = (Compile / externalDependencyClasspath).value + val files: Seq[File] = classpaths.filter(isWebjar).flatMap { classpathEntry => val jarFile = classpathEntry.data IO.unzip(jarFile, dest) } diff --git a/project/ScriptCommands.scala b/project/ScriptCommands.scala index 156a40dbd72a..01d8fd82b3d1 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,17 @@ 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 ++= { + val user = env("SONA_USER") + val pass = env("SONA_PASS") + if (user != "" && pass != "") + List(Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", user, pass)) + else Nil + } // pgpSigningKey and pgpPassphrase are set externally by travis / the bootstrap script, as the sbt-pgp plugin is not enabled by default ) ++ enableOptimizer } @@ -115,12 +122,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 +146,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)") @@ -146,6 +161,7 @@ object ScriptCommands { } private[this] val enableOptimizer = Seq( + //ThisBuild / Compile / scalacOptions ++= Seq("-opt:inline:scala/**") ThisBuild / Compile / scalacOptions ++= Seq("-opt:l:inline", "-opt-inline-from:scala/**") ) @@ -159,7 +175,12 @@ object ScriptCommands { Seq( Global / publishTo := Some("scala-pr-publish" at url2), - Global / credentials += Credentials("Artifactory Realm", "scala-ci.typesafe.com", "scala-ci", env("PRIVATE_REPO_PASS")) + Global / credentials ++= { + val pass = env("PRIVATE_REPO_PASS") + if (pass != "") + List(Credentials("Artifactory Realm", "scala-ci.typesafe.com", "scala-ci", pass)) + else Nil + } ) } diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index 6b4e659cc7a2..b9f21ad180c8 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -2,13 +2,13 @@ package scala.build import sbt._ import Keys._ + import java.util.{Date, Locale, Properties, TimeZone} -import java.io.{File, FileInputStream} +import java.io.{File, FileInputStream, StringWriter} import java.text.SimpleDateFormat import java.time.Instant import java.time.format.DateTimeFormatter import java.time.temporal.{TemporalAccessor, TemporalQueries, TemporalQuery} - import scala.collection.JavaConverters._ import BuildSettings.autoImport._ @@ -30,7 +30,7 @@ object VersionUtil { ) lazy val generatePropertiesFileSettings = Seq[Setting[_]]( - copyrightString := "Copyright 2002-2020, LAMP/EPFL and Lightbend, Inc.", + copyrightString := "Copyright 2002-2022, LAMP/EPFL and Lightbend, Inc.", shellBannerString := """ | ________ ___ / / ___ | / __/ __// _ | / / / _ | @@ -173,13 +173,18 @@ object VersionUtil { } private def writeProps(m: Map[String, String], propFile: File): File = { - val props = new Properties - m.foreach { case (k, v) => props.put(k, v) } - // unfortunately, this will write properties in arbitrary order - // this makes it harder to test for stability of generated artifacts - // consider using https://github.com/etiennestuder/java-ordered-properties - // instead of java.util.Properties - IO.write(props, null, propFile) + // Like: + // IO.write(props, null, propFile) + // But with deterministic key ordering and no timestamp + val fullWriter = new StringWriter() + for (k <- m.keySet.toVector.sorted) { + val writer = new StringWriter() + val props = new Properties() + props.put(k, m(k)) + props.store(writer, null) + writer.toString.linesIterator.drop(1).foreach{line => fullWriter.write(line); fullWriter.write("\n")} + } + IO.write(propFile, fullWriter.toString) propFile } diff --git a/project/build.properties b/project/build.properties index 0b2e09c5ac99..22af2628c413 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.4.7 +sbt.version=1.7.1 diff --git a/project/genprod.scala b/project/genprod.scala index ae5d49652792..1d37ee74cddf 100644 --- a/project/genprod.scala +++ b/project/genprod.scala @@ -100,14 +100,15 @@ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz */ object FunctionZero extends Function(0) { override def genprodString = "\n// genprod generated these sources at: " + java.time.Instant.now() override def covariantSpecs = "@specialized(Specializable.Primitives) " - override def descriptiveComment = " " + functionNTemplate.format("javaVersion", "anonfun0", -""" - * val javaVersion = () => sys.props("java.version") + override def descriptiveComment = " " + functionNTemplate.format("greeting", "anonfun0", +raw""" + * val name = "world" + * val greeting = () => s"hello, $$name" * * val anonfun0 = new Function0[String] { - * def apply(): String = sys.props("java.version") + * def apply(): String = s"hello, $$name" * } - * assert(javaVersion() == anonfun0()) + * assert(greeting() == anonfun0()) * """) override def moreMethods = "" } @@ -207,8 +208,10 @@ class Function(val i: Int) extends Group("Function") with Arity { def descriptiveComment = "" def functionNTemplate = """ - * In the following example, the definition of %s is a - * shorthand for the anonymous class definition %s: + * In the following example, the definition of `%s` is + * shorthand, conceptually, for the anonymous class definition + * `%s`, although the implementation details of how the + * function value is constructed may differ: * * {{{ * object Main extends App {%s} diff --git a/project/plugins.sbt b/project/plugins.sbt index 9294ca79ba70..5499c38f2373 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,9 +1,9 @@ scalacOptions ++= Seq("-unchecked", "-feature", "-deprecation", "-Xlint:-unused,_", "-Xfatal-warnings") -libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.3.2" +libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.12.0" -libraryDependencies += "biz.aQute.bnd" % "biz.aQute.bnd" % "2.4.1" +libraryDependencies += "biz.aQute.bnd" % "biz.aQute.bndlib" % "6.1.0" enablePlugins(BuildInfoPlugin) @@ -17,11 +17,11 @@ buildInfoKeys := Seq[BuildInfoKey](buildClasspath) buildInfoPackage := "scalabuild" -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.0") libraryDependencies ++= Seq( - "org.eclipse.jgit" % "org.eclipse.jgit" % "4.6.0.201612231935-r", - "org.slf4j" % "slf4j-nop" % "1.7.23", + "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.9.201909030838-r", + "org.slf4j" % "slf4j-nop" % "2.0.0", "com.googlecode.java-diff-utils" % "diffutils" % "1.3.0", ) @@ -29,14 +29,6 @@ Global / concurrentRestrictions := Seq( Tags.limitAll(1) // workaround for https://github.com/sbt/sbt/issues/2970 ) -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.4") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.7.0") -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") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") diff --git a/scripts/common b/scripts/common index a0c1e9af137e..e00731fcc01b 100644 --- a/scripts/common +++ b/scripts/common @@ -10,15 +10,16 @@ else IVY2_DIR="$WORKSPACE/.ivy2" fi +SBT_VERSION=`grep sbt.version $WORKSPACE/project/build.properties | sed -n 's/sbt.version=\(.*\)/\1/p'` + SBT_CMD=${SBT_CMD-sbt} -SBT_CMD="$SBT_CMD -sbt-version 1.4.7" +SBT_CMD="$SBT_CMD -sbt-version $SBT_VERSION" # 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 +93,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..921384b40e20 100644 --- a/spec/01-lexical-syntax.md +++ b/spec/01-lexical-syntax.md @@ -6,20 +6,18 @@ chapter: 1 # Lexical Syntax -Scala programs are written using the Unicode Basic Multilingual Plane -(_BMP_) character set; Unicode supplementary characters are not -presently supported. This chapter defines the two modes of Scala's -lexical syntax, the Scala mode, and the _XML mode_. If not -otherwise mentioned, the following descriptions of Scala tokens refer -to _Scala mode_, and literal characters ‘c’ refer to the ASCII fragment -`\u0000` – `\u007F`. +Scala source code consists of Unicode text. + +The program text is tokenized as described in this chapter. +See the last section for special support for XML literals, +which are parsed in _XML mode_. To construct tokens, characters are distinguished according to the following classes (Unicode general category given in parentheses): 1. Whitespace characters. `\u0020 | \u0009 | \u000D | \u000A`. 1. Letters, which include lower case letters (`Ll`), upper case letters (`Lu`), - title case letters (`Lt`), other letters (`Lo`), modifier letters (`Ml`), + title case letters (`Lt`), other letters (`Lo`), modifier letters (`Lm`), letter numerals (`Nl`) and the two characters `\u0024 ‘$’` and `\u005F ‘_’`. 1. Digits `‘0’ | … | ‘9’`. 1. Parentheses `‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ `. @@ -54,7 +52,7 @@ of operator characters. Second, an identifier can start with an operator character followed by an arbitrary sequence of operator characters. The preceding two forms are called _plain_ identifiers. Finally, an identifier may also be formed by an arbitrary string between -back-quotes (host systems may impose some restrictions on which +backquotes (host systems may impose some restrictions on which strings are legal for identifiers). The identifier then is composed of all characters excluding the backquotes themselves. @@ -74,7 +72,7 @@ or `_`, and _constant identifiers_, which do not. For this purpose, lower case letters include not only a-z, but also all characters in Unicode category Ll (lowercase letter), as well as all letters that have contributory property -Other_Lowercase, except characters in category Nl (letter numerals) +Other_Lowercase, except characters in category Nl (letter numerals), which are never taken as lower case. The following are examples of variable identifiers: @@ -463,7 +461,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,37 +501,41 @@ not processed, except for Unicode escapes. #### Interpolated string ```ebnf -interpolatedString ::= alphaid ‘"’ {printableChar \ (‘"’ | ‘$’) | escape} ‘"’ - | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ -escape ::= ‘$$’ - | ‘$’ id +interpolatedString ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ +interpolatedStringPart ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape +escape ::= ‘$$’ + | ‘$"’ + | ‘$’ alphaid | ‘$’ BlockExpr alphaid ::= upper idrest | varid ``` -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, -e.g `$id`. The simpler form is expanded by putting braces around the identifier, -e.g `$id` is equivalent to `${id}`. In the following, unless we explicitly state otherwise, +a letter and followed only by letters, digits, and underscore characters, e.g., `$id`. +The simpler form is expanded by putting braces around the identifier, +e.g., `$id` is equivalent to `${id}`. In the following, unless we explicitly state otherwise, we assume that this expansion has already been performed. The expanded expression is type checked normally. Usually, `StringContext` will resolve to @@ -575,16 +577,7 @@ string literal does not start a valid escape sequence. symbolLiteral ::= ‘'’ plainid ``` -A symbol literal `'x` is a shorthand for the expression `scala.Symbol("x")` and -is of the [literal type](03-types.html#literal-types) `'x`. -`Symbol` is a [case class](05-classes-and-objects.html#case-classes), which is defined as follows. - -```scala -package scala -final case class Symbol private (name: String) { - override def toString: String = "'" + name -} -``` +A symbol literal `'x` is deprecated shorthand for the expression `scala.Symbol("x")`. The `apply` method of `Symbol`'s companion object caches weak references to `Symbol`s, thus ensuring that diff --git a/spec/03-types.md b/spec/03-types.md index 2f898d8acb39..b4bdb7cb2e07 100644 --- a/spec/03-types.md +++ b/spec/03-types.md @@ -131,7 +131,7 @@ determined by evaluating `e == lit`. Literal types are available for all types for which there is dedicated syntax except `Unit`. This includes the numeric types (other than `Byte` and `Short` -which don't currently have syntax), `Boolean`, `Char`, `String` and `Symbol`. +which don't currently have syntax), `Boolean`, `Char` and `String`. ### Stable Types A _stable type_ is a singleton type, a literal type, 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..f316d9a33846 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -240,6 +240,7 @@ depending on whether `B` is mixed in with class `Root` or `A`. ```ebnf SimpleExpr ::= SimpleExpr1 ArgumentExprs ArgumentExprs ::= ‘(’ [Exprs] ‘)’ + | ‘(’ ‘using’ Exprs ‘)’ | ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ | [nl] BlockExpr Exprs ::= Expr {‘,’ Expr} @@ -261,13 +262,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 +296,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 @@ -323,6 +329,9 @@ sum(List(1, 2, 3, 4)) would not typecheck. +An argument list may begin with the soft keyword `using` to facilitate cross-compilation with Scala 3. +The keyword is ignored. + ### Named and Default Arguments If an application is to use named arguments ´p = e´ or default @@ -400,13 +409,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 +599,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 @@ -618,8 +634,9 @@ Expressions can be constructed from operands and operators. ### Prefix Operations A prefix operation ´\mathit{op};e´ consists of a prefix operator ´\mathit{op}´, which -must be one of the identifiers ‘`+`’, ‘`-`’, -‘`!`’ or ‘`~`’. The expression ´\mathit{op};e´ is +must be one of the identifiers ‘`+`’, ‘`-`’, ‘`!`’ or ‘`~`’, +which must not be enclosed in backquotes. +The expression ´\mathit{op};e´ is equivalent to the postfix method application `e.unary_´\mathit{op}´`. @@ -647,7 +664,7 @@ character. Characters are listed below in increasing order of precedence, with characters on the same line having the same precedence. ```scala -(all letters) +(all letters, as defined in [chapter 1](01-lexical-syntax.html), including `_` and `$`) | ^ & @@ -656,7 +673,7 @@ precedence, with characters on the same line having the same precedence. : + - * / % -(all other special characters) +(other operator characters, as defined in [chapter 1](01-lexical-syntax.html), including Unicode categories `Sm` and `So`) ``` That is, operators starting with a letter have lowest precedence, @@ -769,6 +786,7 @@ expression ´e´. ```ebnf Expr1 ::= [SimpleExpr ‘.’] id ‘=’ Expr + | PrefixOperator SimpleExpr ‘=’ Expr | SimpleExpr1 ArgumentExprs ‘=’ Expr ``` @@ -783,6 +801,9 @@ setter method `´x´_=` as member, then the assignment `´x´_=(´e\,´)` of that setter method. Analogously, an assignment `´f.x´ = ´e´` to a parameterless method ´x´ is interpreted as the invocation `´f.x´_=(´e\,´)`. +If ´x´ is an application of a unary operator, then the expression +is interpreted as though it were written as the explicit application +`´x´.unary_´\mathit{op}´`, namely, as `´x´.unary_´\mathit{op}´_=(´e\,´)`. An assignment `´f´(´\mathit{args}\,´) = ´e´` with a method application to the left of the ‘`=`’ operator is interpreted as @@ -907,7 +928,7 @@ A semicolon preceding the `while` symbol of a do loop expression is ignored. Expr1 ::= ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr Enumerators ::= Generator {semi Generator} -Generator ::= Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} +Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} Guard ::= ‘if’ PostfixExpr ``` @@ -917,9 +938,15 @@ A _for comprehension_ `for (´\mathit{enums}\,´) yield ´e´` evaluates expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´ and collects the results. An enumerator sequence always starts with a generator; this can be followed by further generators, value -definitions, or guards. A _generator_ `´p´ <- ´e´` -produces bindings from an expression ´e´ which is matched in some way -against pattern ´p´. A _value definition_ `´p´ = ´e´` +definitions, or guards. + +A _generator_ `´p´ <- ´e´` produces bindings from an expression ´e´ which is +matched in some way against pattern ´p´. Optionally, `case` can appear in front +of a generator pattern, this has no meaning in Scala 2 but will be [required in +Scala 3 if `p` is not +irrefutable](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html). + +A _value definition_ `´p´ = ´e´` binds the value name ´p´ (or several names in a pattern ´p´) to the result of evaluating the expression ´e´. A _guard_ `if ´e´` contains a boolean expression which restricts @@ -1106,7 +1133,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 +1147,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/11-annotations.md b/spec/11-annotations.md index d1f1a6566d4f..388d99b60e56 100644 --- a/spec/11-annotations.md +++ b/spec/11-annotations.md @@ -148,7 +148,7 @@ Java platform, the following annotations have a standard meaning. Whenever the static type of an expression matches a specialized variant of a definition, the compiler will instead use the specialized version. - See the [specialization sid](https://docs.scala-lang.org/sips/completed/scala-specialization.html) for more details of the implementation. + See the [specialization sid](https://docs.scala-lang.org/sips/scala-specialization.html) for more details of the implementation. ## User-defined Annotations diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md index 837054f5a772..6ece538e2ff1 100644 --- a/spec/13-syntax-summary.md +++ b/spec/13-syntax-summary.md @@ -14,15 +14,20 @@ The lexical syntax of Scala is given by the following grammar in EBNF form: ```ebnf whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | … | ‘Z’ | ‘$’ // and any character in Unicode category Lu, Lt or Nl, and any character in Lo and Ml that don't have contributory property Other_Lowercase -lower ::= ‘a’ | … | ‘z’ | ‘_’ // and any character in Unicode category Ll, and and any character in Lo or Ml that has contributory property Other_Lowercase +upper ::= ‘A’ | … | ‘Z’ | ‘$’ and any character in Unicode categories Lu, Lt or Nl, + and any character in Unicode categories Lo and Lm that doesn't have + contributory property Other_Lowercase +lower ::= ‘a’ | … | ‘z’ | ‘_’ and any character in Unicode category Ll, + and any character in Unicode categories Lo or Lm that has contributory + property Other_Lowercase letter ::= upper | lower digit ::= ‘0’ | … | ‘9’ paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ -opchar ::= // printableChar not matched by (whiteSpace | upper | lower | - // letter | digit | paren | delim | Unicode_Sm | Unicode_So) -printableChar ::= // all characters in [\u0020, \u007F] inclusive +opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | + ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ + and any character in Unicode categories Sm or So +printableChar ::= all characters in [\u0020, \u007E] inclusive UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) @@ -60,11 +65,14 @@ stringElement ::= charNoDoubleQuoteOrNewline | escapeSeq multiLineChars ::= {[‘"’] [‘"’] charNoDoubleQuote} {‘"’} -interpolatedString - ::= alphaid ‘"’ {printableChar \ (‘"’ | ‘\$’) | escape} ‘"’ +interpolatedString + ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘\$’) | escape} {‘"’} ‘"""’ -escape ::= ‘\$\$’ - | ‘\$’ id +interpolatedStringPart + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape +escape ::= ‘\$\$’ + | ‘\$"’ + | ‘\$’ alphaid | ‘\$’ BlockExpr alphaid ::= upper idrest | varid @@ -141,6 +149,7 @@ grammar: | ‘throw’ Expr | ‘return’ [Expr] | [SimpleExpr ‘.’] id ‘=’ Expr + | PrefixOperator SimpleExpr ‘=’ Expr | SimpleExpr1 ArgumentExprs ‘=’ Expr | PostfixExpr | PostfixExpr Ascription @@ -148,7 +157,8 @@ grammar: PostfixExpr ::= InfixExpr [id [nl]] InfixExpr ::= PrefixExpr | InfixExpr id [nl] InfixExpr - PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr + PrefixExpr ::= [PrefixOperator] SimpleExpr + PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ SimpleExpr ::= ‘new’ (ClassTemplate | TemplateBody) | BlockExpr | SimpleExpr1 [‘_’] @@ -176,7 +186,7 @@ grammar: | (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block Enumerators ::= Generator {semi Generator} - Generator ::= Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} + Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} CaseClauses ::= CaseClause { CaseClause } CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block diff --git a/spec/_layouts/default.yml b/spec/_layouts/default.yml index 1de9b89d1dba..2589a105dff2 100644 --- a/spec/_layouts/default.yml +++ b/spec/_layouts/default.yml @@ -9,8 +9,8 @@ - - + +